mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-08-24 09:37:01 -05:00
Merge branch 'master' of https://github.com/kwsch/PKHeX
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -14,8 +13,16 @@ public sealed record BattleTemplateLocalization(GameStrings Strings, BattleTempl
|
||||
public const string DefaultLanguage = GameLanguage.DefaultLanguage; // English
|
||||
|
||||
private static readonly Dictionary<string, BattleTemplateLocalization> Cache = new();
|
||||
private static readonly BattleTemplateConfigContext Context = new(LocalizationStorage<BattleTemplateConfig>.Options);
|
||||
public static readonly LocalizationStorage<BattleTemplateConfig> ConfigCache = new("battle", Context.BattleTemplateConfig);
|
||||
public static readonly BattleTemplateLocalization Default = GetLocalization(DefaultLanguage);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the localization for the requested language.
|
||||
/// </summary>
|
||||
/// <param name="language">Language code</param>
|
||||
public static BattleTemplateConfig GetConfig(string language) => ConfigCache.Get(language);
|
||||
|
||||
/// <param name="language"><see cref="LanguageID"/> index</param>
|
||||
/// <inheritdoc cref="GetLocalization(string)"/>
|
||||
public static BattleTemplateLocalization GetLocalization(LanguageID language) =>
|
||||
@@ -37,17 +44,6 @@ public static BattleTemplateLocalization GetLocalization(string language)
|
||||
return result;
|
||||
}
|
||||
|
||||
private static string GetJson(string language) => Util.GetStringResource($"battle_{language}.json");
|
||||
private static BattleTemplateConfigContext GetContext() => new();
|
||||
|
||||
private static BattleTemplateConfig GetConfig(string language)
|
||||
{
|
||||
var text = GetJson(language);
|
||||
var result = JsonSerializer.Deserialize(text, GetContext().BattleTemplateConfig)
|
||||
?? throw new JsonException($"Failed to deserialize {nameof(BattleTemplateConfig)} for {language}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force loads all localizations.
|
||||
/// </summary>
|
||||
|
||||
@@ -994,7 +994,7 @@ private ReadOnlySpan<char> ParseLineMove(ReadOnlySpan<char> line, GameStrings st
|
||||
|
||||
// Defined Hidden Power
|
||||
var type = GetHiddenPowerType(moveString[(hiddenPowerName.Length + 1)..]);
|
||||
var types = strings.types.AsSpan(1, HiddenPower.TypeCount);
|
||||
var types = strings.HiddenPowerTypes;
|
||||
int hpVal = StringUtil.FindIndexIgnoreCase(types, type); // Get HP Type
|
||||
if (hpVal == -1)
|
||||
return hiddenPowerName;
|
||||
|
||||
@@ -76,7 +76,7 @@ public static class BatchMods
|
||||
value => value.StartsWith(CONST_SHINY),
|
||||
(pk, cmd) => CommonEdits.SetShiny(pk, GetRequestedShinyState(cmd.PropertyValue))),
|
||||
|
||||
new ComplexSet(nameof(PKM.Species), value => value is "0", (pk, _) => pk.Data.AsSpan().Clear()),
|
||||
new ComplexSet(nameof(PKM.Species), value => value is "0", (pk, _) => pk.Data.Clear()),
|
||||
new ComplexSet(nameof(PKM.IsNicknamed), value => value.Equals("false", StringComparison.OrdinalIgnoreCase), (pk, _) => pk.SetDefaultNickname()),
|
||||
|
||||
// Complicated
|
||||
|
||||
@@ -22,7 +22,7 @@ public class EntitySummary : IFatefulEncounterReadOnly // do NOT seal, allow inh
|
||||
public string Nature => Get(Strings.natures, (byte)Entity.StatNature);
|
||||
public string Gender => Get(GenderSymbols, Entity.Gender);
|
||||
public string ESV => Entity.PSV.ToString("0000");
|
||||
public string HP_Type => Get(Strings.types, Entity.HPType + 1);
|
||||
public string HP_Type => GetSpan(Strings.HiddenPowerTypes, Entity.HPType);
|
||||
public string Ability => Get(Strings.abilitylist, Entity.Ability);
|
||||
public string Move1 => Get(Strings.movelist, Entity.Move1);
|
||||
public string Move2 => Get(Strings.movelist, Entity.Move2);
|
||||
@@ -100,7 +100,7 @@ public class EntitySummary : IFatefulEncounterReadOnly // do NOT seal, allow inh
|
||||
public string Relearn2 => Get(Strings.movelist, Entity.RelearnMove2);
|
||||
public string Relearn3 => Get(Strings.movelist, Entity.RelearnMove3);
|
||||
public string Relearn4 => Get(Strings.movelist, Entity.RelearnMove4);
|
||||
public ushort Checksum => Entity is ISanityChecksum s ? s.Checksum : Checksums.CRC16_CCITT(Entity.Data.AsSpan(Entity.SIZE_STORED));
|
||||
public ushort Checksum => Entity is ISanityChecksum s ? s.Checksum : Checksums.CRC16_CCITT(Entity.Data[Entity.SIZE_STORED..]);
|
||||
public int Friendship => Entity.OriginalTrainerFriendship;
|
||||
public int EggYear => Entity.EggMetDate.GetValueOrDefault().Year;
|
||||
public int EggMonth => Entity.EggMetDate.GetValueOrDefault().Month;
|
||||
|
||||
@@ -106,7 +106,7 @@ private static SaveFile GetBlankSaveFile(GameVersion version, SaveFile? current)
|
||||
var sav = SaveUtil.GetBlankSAV(version, tr, lang);
|
||||
if (sav.Version == GameVersion.Invalid) // will fail to load
|
||||
{
|
||||
var max = GameInfo.VersionDataSource.MaxBy(z => z.Value)!;
|
||||
var max = GameInfo.Sources.BallDataSource.MaxBy(z => z.Value)!;
|
||||
var maxVer = (GameVersion)max.Value;
|
||||
sav = SaveUtil.GetBlankSAV(maxVer, tr, lang);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -62,7 +61,7 @@ private static List<SlotInfoMisc> GetExtraSlots3(SAV3 sav)
|
||||
return None;
|
||||
return
|
||||
[
|
||||
new(sav.Large.AsMemory(0x3C98), 0) {Type = StorageSlotType.Daycare},
|
||||
new(sav.LargeBuffer[0x3C98..], 0) {Type = StorageSlotType.Daycare},
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,12 @@ public FilteredGameDataSource(SaveFile sav, GameDataSource source, bool HaX = fa
|
||||
var gamelist = GameUtil.GetVersionsWithinRange(sav, sav.Generation).ToList();
|
||||
Games = Source.VersionDataSource.Where(g => gamelist.Contains((GameVersion)g.Value) || g.Value == 0).ToList();
|
||||
|
||||
Languages = GameDataSource.LanguageDataSource(sav.Generation);
|
||||
Languages = Source.LanguageDataSource(sav.Generation);
|
||||
Balls = Source.BallDataSource.Where(b => b.Value <= sav.MaxBallID).ToList();
|
||||
Abilities = Source.AbilityDataSource.Where(a => a.Value <= sav.MaxAbilityID).ToList();
|
||||
|
||||
G4GroundTiles = Source.GroundTileDataSource;
|
||||
ConsoleRegions = Source.Regions;
|
||||
Natures = Source.NatureDataSource;
|
||||
}
|
||||
|
||||
@@ -113,7 +114,7 @@ private static List<ComboItem> GetMovesWithoutDummy(IReadOnlyList<ComboItem> leg
|
||||
public readonly IReadOnlyList<ComboItem> Abilities;
|
||||
public readonly IReadOnlyList<ComboItem> Natures;
|
||||
public readonly IReadOnlyList<ComboItem> G4GroundTiles;
|
||||
public readonly IReadOnlyList<ComboItem> ConsoleRegions = GameDataSource.Regions;
|
||||
public readonly IReadOnlyList<ComboItem> ConsoleRegions;
|
||||
|
||||
private const char HiddenAbilitySuffix = 'H';
|
||||
private const char AbilityIndexSuffix = '1';
|
||||
|
||||
@@ -11,30 +11,24 @@ public sealed class GameDataSource
|
||||
/// <summary>
|
||||
/// List of <see cref="Region3DSIndex"/> values to display.
|
||||
/// </summary>
|
||||
public static readonly IReadOnlyList<ComboItem> Regions =
|
||||
[
|
||||
new ("Japan (日本)", 0),
|
||||
new ("Americas (NA/SA)", 1),
|
||||
new ("Europe (EU/AU)", 2),
|
||||
new ("China (中国大陆)", 4),
|
||||
new ("Korea (한국)", 5),
|
||||
new ("Taiwan (香港/台灣)", 6),
|
||||
];
|
||||
public readonly IReadOnlyList<ComboItem> Regions;
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="LanguageID"/> values to display.
|
||||
/// </summary>
|
||||
private static readonly ComboItem[] LanguageList =
|
||||
private readonly ComboItem[] LanguageList;
|
||||
|
||||
private static ReadOnlySpan<byte> LanguageIDs =>
|
||||
[
|
||||
new ("JPN (日本語)", (int)LanguageID.Japanese),
|
||||
new ("ENG (English)", (int)LanguageID.English),
|
||||
new ("FRE (Français)", (int)LanguageID.French),
|
||||
new ("ITA (Italiano)", (int)LanguageID.Italian),
|
||||
new ("GER (Deutsch)", (int)LanguageID.German),
|
||||
new ("ESP (Español)", (int)LanguageID.Spanish),
|
||||
new ("KOR (한국어)", (int)LanguageID.Korean),
|
||||
new ("CHS (简体中文)", (int)LanguageID.ChineseS),
|
||||
new ("CHT (繁體中文)", (int)LanguageID.ChineseT),
|
||||
(byte)LanguageID.Japanese,
|
||||
(byte)LanguageID.English,
|
||||
(byte)LanguageID.French,
|
||||
(byte)LanguageID.Italian,
|
||||
(byte)LanguageID.German,
|
||||
(byte)LanguageID.Spanish,
|
||||
(byte)LanguageID.Korean,
|
||||
(byte)LanguageID.ChineseS,
|
||||
(byte)LanguageID.ChineseT,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
@@ -42,7 +36,7 @@ public sealed class GameDataSource
|
||||
/// </summary>
|
||||
/// <param name="generation">Generation to get the language list for.</param>
|
||||
/// <returns>List of languages to display.</returns>
|
||||
public static IReadOnlyList<ComboItem> LanguageDataSource(byte generation) => generation switch
|
||||
public IReadOnlyList<ComboItem> LanguageDataSource(byte generation) => generation switch
|
||||
{
|
||||
3 => LanguageList[..6], // No Korean+
|
||||
< 7 => LanguageList[..7], // No Chinese+
|
||||
@@ -57,6 +51,8 @@ public GameDataSource(GameStrings s)
|
||||
NatureDataSource = Util.GetCBList(s.natures);
|
||||
AbilityDataSource = Util.GetCBList(s.abilitylist);
|
||||
GroundTileDataSource = Util.GetUnsortedCBList(s.groundtiletypes, GroundTileTypeExtensions.ValidTileTypes);
|
||||
Regions = Util.GetUnsortedCBList(s.console3ds, Locale3DS.DefinedLocales);
|
||||
LanguageList = Util.GetUnsortedCBList(s.languageNames, LanguageIDs);
|
||||
|
||||
var moves = Util.GetCBList(s.movelist);
|
||||
HaXMoveDataSource = moves;
|
||||
|
||||
@@ -30,7 +30,7 @@ public static GameStrings GetStrings(string lang)
|
||||
|
||||
public static string GetVersionName(GameVersion version)
|
||||
{
|
||||
foreach (var kvp in VersionDataSource)
|
||||
foreach (var kvp in Sources.VersionDataSource)
|
||||
{
|
||||
if (kvp.Value == (int)version)
|
||||
return kvp.Text;
|
||||
@@ -38,19 +38,8 @@ public static string GetVersionName(GameVersion version)
|
||||
return version.ToString();
|
||||
}
|
||||
|
||||
// DataSource providing
|
||||
public static IReadOnlyList<ComboItem> ItemDataSource => FilteredSources.Items;
|
||||
public static IReadOnlyList<ComboItem> SpeciesDataSource => Sources.SpeciesDataSource;
|
||||
public static IReadOnlyList<ComboItem> BallDataSource => Sources.BallDataSource;
|
||||
public static IReadOnlyList<ComboItem> NatureDataSource => Sources.NatureDataSource;
|
||||
public static IReadOnlyList<ComboItem> AbilityDataSource => Sources.AbilityDataSource;
|
||||
public static IReadOnlyList<ComboItem> VersionDataSource => Sources.VersionDataSource;
|
||||
public static IReadOnlyList<ComboItem> MoveDataSource => Sources.HaXMoveDataSource;
|
||||
public static IReadOnlyList<ComboItem> GroundTileDataSource => Sources.GroundTileDataSource;
|
||||
public static IReadOnlyList<ComboItem> Regions => GameDataSource.Regions;
|
||||
|
||||
public static IReadOnlyList<ComboItem> LanguageDataSource(byte generation)
|
||||
=> GameDataSource.LanguageDataSource(generation);
|
||||
=> Sources.LanguageDataSource(generation);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location name for the specified parameters.
|
||||
|
||||
@@ -27,8 +27,12 @@ public sealed class GameStrings : IBasicStrings
|
||||
public readonly string[] wallpapernames, puffs, walkercourses;
|
||||
public readonly string[] uggoods, ugspheres, ugtraps, ugtreasures;
|
||||
public readonly string[] seals, accessories, backdrops, poketchapps;
|
||||
public readonly string[] console3ds, languageNames;
|
||||
private readonly string LanguageFilePrefix;
|
||||
|
||||
public ReadOnlySpan<string> HiddenPowerTypes => types.AsSpan(1, HiddenPower.TypeCount);
|
||||
public readonly RibbonStrings Ribbons;
|
||||
|
||||
public LanguageID Language { get; }
|
||||
public string EggName { get; }
|
||||
public IReadOnlyList<string> Species => specieslist;
|
||||
@@ -59,6 +63,7 @@ internal GameStrings(string langFilePrefix)
|
||||
Language = GameLanguage.GetLanguage(LanguageFilePrefix = langFilePrefix);
|
||||
|
||||
ribbons = Get("ribbons");
|
||||
Ribbons = new(ribbons);
|
||||
|
||||
// Past Generation strings
|
||||
g3items = Get("ItemsG3");
|
||||
@@ -124,6 +129,8 @@ internal GameStrings(string langFilePrefix)
|
||||
accessories = Get("accessories");
|
||||
backdrops = Get("backdrops");
|
||||
poketchapps = Get("poketchapps");
|
||||
console3ds = Get("console3ds");
|
||||
languageNames = Get("language");
|
||||
|
||||
EggName = specieslist[0];
|
||||
Gen4 = Get4("hgss");
|
||||
@@ -784,11 +791,25 @@ private string[] GetItemStrings3(GameVersion game)
|
||||
return g3items;
|
||||
|
||||
var g3ItemsWithEBerry = (string[])g3items.Clone();
|
||||
g3ItemsWithEBerry[175] = EReaderBerrySettings.DisplayName;
|
||||
g3ItemsWithEBerry[175] = GetEnigmaBerryName3(Language, EReaderBerrySettings.Name);
|
||||
return g3ItemsWithEBerry;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetEnigmaBerryName3(LanguageID language, string berryName) => string.Format(language switch
|
||||
{
|
||||
Japanese => "{0}のみ",
|
||||
English => "{0} BERRY",
|
||||
German => "{0}BEERE",
|
||||
French => "BAIE {0}",
|
||||
Italian => "BACCA{0}",
|
||||
Spanish => "BAYA {0}",
|
||||
Korean => "{0}열매",
|
||||
ChineseS => "{0}果",
|
||||
ChineseT => "{0}果",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(language), language, null),
|
||||
}, berryName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the location name for the specified parameters.
|
||||
/// </summary>
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
namespace PKHeX.Core.Bulk;
|
||||
|
||||
public readonly record struct BulkCheckResult(CheckResult Result, string Comment);
|
||||
|
||||
/// <summary>
|
||||
/// Analyzes content within a <see cref="SaveFile"/> for overall <see cref="PKM"/> legality analysis.
|
||||
/// </summary>
|
||||
@@ -12,7 +14,7 @@ public sealed class BulkAnalysis
|
||||
public readonly IReadOnlyList<SlotCache> AllData;
|
||||
public readonly IReadOnlyList<LegalityAnalysis> AllAnalysis;
|
||||
public readonly ITrainerInfo Trainer;
|
||||
public readonly List<CheckResult> Parse = [];
|
||||
public readonly List<BulkCheckResult> Parse = [];
|
||||
public readonly Dictionary<ulong, SlotCache> Trackers = [];
|
||||
public readonly bool Valid;
|
||||
|
||||
@@ -41,7 +43,7 @@ public BulkAnalysis(SaveFile sav, BulkAnalysisSettings settings)
|
||||
CloneFlags = new bool[AllData.Count];
|
||||
|
||||
ScanAll();
|
||||
Valid = Parse.Count == 0 || Parse.TrueForAll(static z => z.Valid);
|
||||
Valid = Parse.Count == 0 || Parse.TrueForAll(static z => z.Result.Valid);
|
||||
}
|
||||
|
||||
// Remove things that aren't actual stored data, or already flagged by legality checks.
|
||||
@@ -79,21 +81,21 @@ private void ScanAll()
|
||||
/// <summary>
|
||||
/// Adds a new entry to the <see cref="Parse"/> list.
|
||||
/// </summary>
|
||||
public void AddLine(SlotCache first, SlotCache second, string msg, CheckIdentifier i, Severity s = Severity.Invalid)
|
||||
public void AddLine(SlotCache first, SlotCache second, LegalityCheckResultCode msg, CheckIdentifier i, Severity s = Severity.Invalid)
|
||||
{
|
||||
var c = $"{msg}{Environment.NewLine}{GetSummary(first)}{Environment.NewLine}{GetSummary(second)}{Environment.NewLine}";
|
||||
var chk = new CheckResult(s, i, c);
|
||||
Parse.Add(chk);
|
||||
var line = $"{msg}{Environment.NewLine}{GetSummary(first)}{Environment.NewLine}{GetSummary(second)}{Environment.NewLine}";
|
||||
var chk = CheckResult.Get(s, i, msg);
|
||||
Parse.Add(new(chk, line));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new entry to the <see cref="Parse"/> list.
|
||||
/// </summary>
|
||||
public void AddLine(SlotCache first, string msg, CheckIdentifier i, Severity s = Severity.Invalid)
|
||||
public void AddLine(SlotCache first, LegalityCheckResultCode msg, CheckIdentifier i, Severity s = Severity.Invalid)
|
||||
{
|
||||
var c = $"{msg}{Environment.NewLine}{GetSummary(first)}{Environment.NewLine}";
|
||||
var chk = new CheckResult(s, i, c);
|
||||
Parse.Add(chk);
|
||||
var line = $"{msg}{Environment.NewLine}{GetSummary(first)}{Environment.NewLine}";
|
||||
var chk = CheckResult.Get(s, i, msg);
|
||||
Parse.Add(new(chk, line));
|
||||
}
|
||||
|
||||
private static LegalityAnalysis[] GetIndividualAnalysis(ReadOnlySpan<SlotCache> list)
|
||||
|
||||
@@ -61,10 +61,10 @@ private static void VerifyECShare(BulkAnalysis input, CombinedReference pr, Comb
|
||||
{
|
||||
if (ca.Info.Generation != gen)
|
||||
{
|
||||
input.AddLine(ps, cs, "EC sharing across generations detected.", ident);
|
||||
input.AddLine(ps, cs, LegalityCheckResultCode.BulkSharingEncryptionConstantGenerationDifferent, ident);
|
||||
return;
|
||||
}
|
||||
input.AddLine(ps, cs, "EC sharing for 3DS-onward origin detected.", ident);
|
||||
input.AddLine(ps, cs, LegalityCheckResultCode.BulkSharingEncryptionConstantGenerationSame, ident);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ private static void VerifyECShare(BulkAnalysis input, CombinedReference pr, Comb
|
||||
|
||||
if (eggMysteryCurrent != eggMysteryPrevious)
|
||||
{
|
||||
input.AddLine(ps, cs, "EC sharing across RNG encounters detected.", ident);
|
||||
input.AddLine(ps, cs, LegalityCheckResultCode.BulkSharingEncryptionConstantEncounterType, ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ private static void CheckDuplicateOwnedGifts(BulkAnalysis input)
|
||||
var grp = tidGroup[0];
|
||||
var first = grp[0].Slot;
|
||||
var second = grp[1].Slot;
|
||||
input.AddLine(first, second, $"Receipt of the same egg mystery gifts detected: {dupe.Key}", Encounter);
|
||||
input.AddLine(first, second, LegalityCheckResultCode.BulkDuplicateMysteryGiftEggReceived, Encounter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,14 +58,14 @@ private static void VerifyPIDShare(BulkAnalysis input, CombinedReference pr, Com
|
||||
|
||||
if (ca.Info.Generation != gen)
|
||||
{
|
||||
input.AddLine(ps, cs, "PID sharing across generations detected.", ident);
|
||||
input.AddLine(ps, cs, LegalityCheckResultCode.BulkSharingPIDGenerationDifferent, ident);
|
||||
return;
|
||||
}
|
||||
|
||||
bool gbaNDS = gen is 3 or 4 or 5;
|
||||
if (!gbaNDS)
|
||||
{
|
||||
input.AddLine(ps, cs, "PID sharing for 3DS-onward origin detected.", ident);
|
||||
input.AddLine(ps, cs, LegalityCheckResultCode.BulkSharingPIDGenerationSame, ident);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ private static void VerifyPIDShare(BulkAnalysis input, CombinedReference pr, Com
|
||||
|
||||
if (eggMysteryCurrent != eggMysteryPrevious)
|
||||
{
|
||||
input.AddLine(ps, cs, "PID sharing across RNG encounters detected.", ident);
|
||||
input.AddLine(ps, cs, LegalityCheckResultCode.BulkSharingPIDEncounterType, ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ private static bool VerifyIDReuse(BulkAnalysis input, SlotCache ps, LegalityAnal
|
||||
// Trainer-ID-SID16 should only occur for one version
|
||||
if (IsSharedVersion(pp, pa, cp, ca))
|
||||
{
|
||||
input.AddLine(ps, cs, "TID sharing across versions detected.", ident);
|
||||
input.AddLine(ps, cs, LegalityCheckResultCode.BulkSharingTrainerVersion, ident);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ private static bool VerifyIDReuse(BulkAnalysis input, SlotCache ps, LegalityAnal
|
||||
if (pp.OriginalTrainerName != cp.OriginalTrainerName)
|
||||
{
|
||||
var severity = ca.Info.Generation == 4 ? Severity.Fishy : Severity.Invalid;
|
||||
input.AddLine(ps, cs, "TID sharing across different trainer names detected.", ident, severity);
|
||||
input.AddLine(ps, cs, LegalityCheckResultCode.BulkSharingTrainerIDs, ident, severity);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using static PKHeX.Core.CheckIdentifier;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core.Bulk;
|
||||
|
||||
@@ -44,7 +44,7 @@ private static void Verify(BulkAnalysis input, SlotCache cs, LegalityAnalysis la
|
||||
var shouldBe0 = tr.IsFromTrainer(pk);
|
||||
byte expect = shouldBe0 ? (byte)0 : (byte)1;
|
||||
if (!HistoryVerifier.IsHandlerStateCorrect(la.EncounterOriginal, pk, current, expect))
|
||||
input.AddLine(cs, LTransferCurrentHandlerInvalid, Trainer);
|
||||
input.AddLine(cs, TransferCurrentHandlerInvalid, Trainer);
|
||||
|
||||
if (current == 1)
|
||||
CheckHandlingTrainerEquals(input, pk, tr, cs);
|
||||
@@ -58,14 +58,14 @@ private static void CheckHandlingTrainerEquals(BulkAnalysis data, PKM pk, SaveFi
|
||||
ht = ht[..len];
|
||||
|
||||
if (!ht.SequenceEqual(tr.OT))
|
||||
data.AddLine(cs, LTransferHTMismatchName, Trainer);
|
||||
data.AddLine(cs, TransferHandlerMismatchName, Trainer);
|
||||
if (pk.HandlingTrainerGender != tr.Gender)
|
||||
data.AddLine(cs, LTransferHTMismatchGender, Trainer);
|
||||
data.AddLine(cs, TransferHandlerMismatchGender, Trainer);
|
||||
|
||||
// If the format exposes a language, check if it matches.
|
||||
// Can be mismatched as the game only checks OT/Gender equivalence -- if it matches, don't update everything else.
|
||||
// Statistically unlikely that players will play in different languages, but it's technically possible.
|
||||
if (pk is IHandlerLanguage h && h.HandlingTrainerLanguage != tr.Language)
|
||||
data.AddLine(cs, LTransferHTMismatchLanguage, Trainer, Severity.Fishy);
|
||||
data.AddLine(cs, TransferHandlerMismatchLanguage, Trainer, Severity.Fishy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ private static void CheckClones(BulkAnalysis input)
|
||||
}
|
||||
|
||||
input.SetIsClone(i, true);
|
||||
input.AddLine(ps, cs, "Clone detected (Details).", Encounter);
|
||||
input.AddLine(ps, cs, LegalityCheckResultCode.BulkCloneDetectedDetails, Encounter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ private static void CheckClonedTrackerHOME(BulkAnalysis input, IHomeTrack home,
|
||||
private static void CheckTrackerPresent(BulkAnalysis input, SlotCache cs, ulong tracker)
|
||||
{
|
||||
if (input.Trackers.TryGetValue(tracker, out var clone))
|
||||
input.AddLine(cs, clone, "Clone detected (Duplicate Tracker).", Encounter);
|
||||
input.AddLine(cs, clone, LegalityCheckResultCode.BulkCloneDetectedTracker, Encounter);
|
||||
else
|
||||
input.Trackers.Add(tracker, cs);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -45,7 +45,7 @@ public static void FindVerifiedEncounter(PKM pk, LegalInfo info)
|
||||
|
||||
// Looks like we might have a good enough match. Check if this is really a good match.
|
||||
info.EncounterMatch = enc;
|
||||
if (e.Comment.Length != 0)
|
||||
if (e.Result != Valid)
|
||||
info.Parse.Add(e);
|
||||
if (!VerifySecondaryChecks(pk, info, encounter))
|
||||
continue;
|
||||
@@ -63,7 +63,7 @@ public static void FindVerifiedEncounter(PKM pk, LegalInfo info)
|
||||
continue;
|
||||
|
||||
// We ran out of possible encounters without finding a suitable match; add a message indicating that the encounter is not a complete match.
|
||||
info.Parse.Add(new CheckResult(Severity.Invalid, CheckIdentifier.Encounter, LEncInvalid));
|
||||
info.Parse.Add(CheckResult.Get(Severity.Invalid, CheckIdentifier.Encounter, EncInvalid));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -71,9 +71,9 @@ public static void FindVerifiedEncounter(PKM pk, LegalInfo info)
|
||||
if (manual != EncounterYieldFlag.None)
|
||||
{
|
||||
if (!info.FrameMatches) // if false, all valid RNG frame matches have already been consumed
|
||||
info.Parse.Add(new CheckResult(ParseSettings.Settings.FramePattern.GetSeverity(info.Generation), CheckIdentifier.PID, LEncConditionBadRNGFrame));
|
||||
info.Parse.Add(CheckResult.Get(ParseSettings.Settings.FramePattern.GetSeverity(info.Generation), CheckIdentifier.PID, EncConditionBadRNGFrame));
|
||||
else if (!info.PIDIVMatches) // if false, all valid PID/IV matches have already been consumed
|
||||
info.Parse.Add(new CheckResult(Severity.Invalid, CheckIdentifier.PID, LPIDTypeMismatch));
|
||||
info.Parse.Add(CheckResult.Get(Severity.Invalid, CheckIdentifier.PID, PIDTypeMismatch));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,22 +164,22 @@ private static bool VerifySecondaryChecks(PKM pk, LegalInfo info, PeekEnumerator
|
||||
private static void VerifyWithoutEncounter(PKM pk, LegalInfo info)
|
||||
{
|
||||
info.EncounterMatch = new EncounterInvalid(pk);
|
||||
string hint = GetHintWhyNotFound(pk, info.EncounterMatch.Generation);
|
||||
var hint = GetHintWhyNotFound(pk, info.EncounterMatch.Generation);
|
||||
|
||||
info.Parse.Add(new CheckResult(Severity.Invalid, CheckIdentifier.Encounter, hint));
|
||||
info.Parse.Add(CheckResult.Get(Severity.Invalid, CheckIdentifier.Encounter, hint));
|
||||
LearnVerifierRelearn.Verify(info.Relearn, info.EncounterOriginal, pk);
|
||||
LearnVerifier.Verify(info.Moves, pk, info.EncounterMatch, info.EvoChainsAllGens);
|
||||
}
|
||||
|
||||
private static string GetHintWhyNotFound(PKM pk, byte generation)
|
||||
private static LegalityCheckResultCode GetHintWhyNotFound(PKM pk, byte generation)
|
||||
{
|
||||
if (WasGiftEgg(pk, generation, pk.EggLocation))
|
||||
return LEncGift;
|
||||
return EncGift;
|
||||
if (WasEventEgg(pk, generation))
|
||||
return LEncGiftEggEvent;
|
||||
return EncGiftEggEvent;
|
||||
if (WasEvent(pk, generation))
|
||||
return LEncGiftNotFound;
|
||||
return LEncInvalid;
|
||||
return EncGiftNotFound;
|
||||
return EncInvalid;
|
||||
}
|
||||
|
||||
private static bool WasGiftEgg(PKM pk, byte generation, ushort eggLocation) => !pk.FatefulEncounter && generation switch
|
||||
|
||||
@@ -118,7 +118,8 @@ private bool SeekForward<TArea>(TArea[] areas)
|
||||
var species = Entity.Species;
|
||||
if (species is (int)Species.Dudunsparce or (int)Species.Maushold)
|
||||
{
|
||||
if (!EvolutionRestrictions.GetIsExpectedEvolveFormEC100(species, Entity.Form, Entity.EncryptionConstant % 100 == 0))
|
||||
var expectRare = EvolutionRestrictions.IsEvolvedSpeciesFormRare(Entity.EncryptionConstant);
|
||||
if (!EvolutionRestrictions.GetIsExpectedEvolveFormEC100(species, Entity.Form, expectRare))
|
||||
return SeekMode.Reverse;
|
||||
}
|
||||
else if (EvolutionRestrictions.IsFormArgEvolution(species))
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -9,15 +8,21 @@ namespace PKHeX.Core;
|
||||
/// </summary>
|
||||
public static class EncounterText
|
||||
{
|
||||
public static IReadOnlyList<string> GetTextLines(this IEncounterInfo enc, bool verbose = false) => GetTextLines(enc, GameInfo.Strings, verbose);
|
||||
private static EncounterDisplayContext GetContext(string language = GameLanguage.DefaultLanguage) => new()
|
||||
{
|
||||
Localization = EncounterDisplayLocalization.Get(language),
|
||||
Strings = GameInfo.GetStrings(language),
|
||||
};
|
||||
|
||||
public static IReadOnlyList<string> GetTextLines(this IEncounterInfo enc, GameStrings strings, bool verbose = false)
|
||||
public static IReadOnlyList<string> GetTextLines(this IEncounterInfo enc, bool verbose = false, string language = GameLanguage.DefaultLanguage) => GetTextLines(enc, GetContext(language), verbose);
|
||||
|
||||
public static IReadOnlyList<string> GetTextLines(this IEncounterInfo enc, EncounterDisplayContext ctx, bool verbose = false)
|
||||
{
|
||||
var lines = new List<string>();
|
||||
var str = strings.Species;
|
||||
var name = (uint)enc.Species < str.Count ? str[enc.Species] : enc.Species.ToString();
|
||||
var EncounterName = $"{(enc is IEncounterable ie ? ie.LongName : "Special")} ({name})";
|
||||
lines.Add(string.Format(L_FEncounterType_0, EncounterName));
|
||||
var loc = ctx.Localization;
|
||||
var name = ctx.GetSpeciesName(enc);
|
||||
lines.Add(string.Format(loc.Format, loc.EncounterType, name));
|
||||
|
||||
if (enc is MysteryGift mg)
|
||||
{
|
||||
lines.AddRange(mg.GetDescription());
|
||||
@@ -26,21 +31,15 @@ public static IReadOnlyList<string> GetTextLines(this IEncounterInfo enc, GameSt
|
||||
{
|
||||
var moves = m.Moves;
|
||||
if (moves.HasMoves)
|
||||
{
|
||||
string result = moves.GetMovesetLine(strings.movelist);
|
||||
lines.Add(result);
|
||||
}
|
||||
lines.Add(ctx.GetMoveset(moves));
|
||||
}
|
||||
|
||||
var loc = enc.GetEncounterLocation(enc.Generation, enc.Version);
|
||||
if (!string.IsNullOrEmpty(loc))
|
||||
lines.Add(string.Format(L_F0_1, "Location", loc));
|
||||
var location = enc.GetEncounterLocation(enc.Generation, enc.Version);
|
||||
if (!string.IsNullOrEmpty(location))
|
||||
lines.Add(string.Format(loc.Format, loc.Location, location));
|
||||
|
||||
var game = enc.Version.IsValidSavedVersion() ? strings.gamelist[(int)enc.Version] : enc.Version.ToString();
|
||||
lines.Add(string.Format(L_F0_1, nameof(GameVersion), game));
|
||||
lines.Add(enc.LevelMin == enc.LevelMax
|
||||
? $"Level: {enc.LevelMin}"
|
||||
: $"Level: {enc.LevelMin}-{enc.LevelMax}");
|
||||
lines.Add(ctx.GetVersionDisplay(enc));
|
||||
lines.Add(ctx.GetLevelDisplay(enc));
|
||||
|
||||
if (!verbose)
|
||||
return lines;
|
||||
@@ -56,3 +55,37 @@ public static IReadOnlyList<string> GetTextLines(this IEncounterInfo enc, GameSt
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
|
||||
public record struct EncounterDisplayContext
|
||||
{
|
||||
public required EncounterDisplayLocalization Localization { get; init; }
|
||||
public required GameStrings Strings { get; init; }
|
||||
|
||||
public string GetSpeciesName(IEncounterTemplate enc)
|
||||
{
|
||||
var encSpecies = enc.Species;
|
||||
var str = Strings.Species;
|
||||
var name = (uint)encSpecies < str.Count ? str[encSpecies] : encSpecies.ToString();
|
||||
var EncounterName = $"{(enc is IEncounterable ie ? ie.LongName : "Special")} ({name})";
|
||||
return EncounterName;
|
||||
}
|
||||
|
||||
public string GetMoveset(Moveset moves) => moves.GetMovesetLine(Strings.movelist);
|
||||
|
||||
public string GetVersionDisplay(IEncounterTemplate enc)
|
||||
{
|
||||
var version = enc.Version;
|
||||
var versionName = enc.Version.IsValidSavedVersion()
|
||||
? Strings.gamelist[(int)enc.Version]
|
||||
: enc.Version.ToString();
|
||||
|
||||
return string.Format(Localization.Format, Localization.Version, versionName);
|
||||
}
|
||||
|
||||
public string GetLevelDisplay(IEncounterTemplate enc)
|
||||
{
|
||||
if (enc.LevelMin == enc.LevelMax)
|
||||
return string.Format(Localization.Format, Localization.Level, enc.LevelMin);
|
||||
return string.Format(Localization.Format, Localization.LevelRange, enc.LevelMin, enc.LevelMax);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -21,15 +21,15 @@ public static class EncounterVerifier
|
||||
|
||||
private static CheckResult VerifyEncounter(PKM pk, IEncounterTemplate enc) => enc switch
|
||||
{
|
||||
EncounterShadow3Colo { IsEReader: true } when pk.Language != (int)LanguageID.Japanese => GetInvalid(LG3EReader),
|
||||
EncounterStatic3 { Species: (int)Species.Mew } when pk.Language != (int)LanguageID.Japanese => GetInvalid(LEncUnreleasedEMewJP),
|
||||
EncounterStatic3 { Species: (int)Species.Deoxys, Location: 200 } when pk.Language == (int)LanguageID.Japanese => GetInvalid(LEncUnreleased),
|
||||
EncounterStatic4 { IsRoaming: true } when pk is G4PKM { MetLocation: 193, GroundTile: GroundTileType.Water } => GetInvalid(LG4InvalidTileR45Surf),
|
||||
EncounterShadow3Colo { IsEReader: true } when pk.Language != (int)LanguageID.Japanese => GetInvalid(G3EReader),
|
||||
EncounterStatic3 { Species: (int)Species.Mew } when pk.Language != (int)LanguageID.Japanese => GetInvalid(EncUnreleasedEMewJP),
|
||||
EncounterStatic3 { Species: (int)Species.Deoxys, Location: 200 } when pk.Language == (int)LanguageID.Japanese => GetInvalid(EncUnreleased),
|
||||
EncounterStatic4 { IsRoaming: true } when pk is G4PKM { MetLocation: 193, GroundTile: GroundTileType.Water } => GetInvalid(G4InvalidTileR45Surf),
|
||||
MysteryGift g => VerifyEncounterEvent(pk, g),
|
||||
IEncounterEgg e when pk.IsEgg => VerifyEncounterEggUnhatched(pk, e),
|
||||
{ IsEgg: true } when !pk.IsEgg => VerifyEncounterEggHatched(pk, enc.Context),
|
||||
EncounterInvalid => GetInvalid(LEncInvalid),
|
||||
_ => GetValid(string.Empty), // todo: refactor
|
||||
EncounterInvalid => GetInvalid(EncInvalid),
|
||||
_ => GetValid(Valid),
|
||||
};
|
||||
|
||||
private static CheckResult VerifyEncounterG12(PKM pk, IEncounterTemplate enc)
|
||||
@@ -39,11 +39,11 @@ private static CheckResult VerifyEncounterG12(PKM pk, IEncounterTemplate enc)
|
||||
|
||||
return enc switch
|
||||
{
|
||||
EncounterSlot1 => GetValid(LEncCondition),
|
||||
EncounterSlot1 => GetValid(EncCondition),
|
||||
EncounterSlot2 s2 => VerifyWildEncounterGen2(pk, s2),
|
||||
EncounterTrade1 t => VerifyEncounterTrade(pk, t),
|
||||
EncounterTrade2 => GetValid(LEncTradeMatch),
|
||||
_ => GetValid(string.Empty), // todo: refactor
|
||||
EncounterTrade2 => GetValid(EncTradeMatch),
|
||||
_ => GetValid(Valid),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -51,9 +51,9 @@ private static CheckResult VerifyEncounterG12(PKM pk, IEncounterTemplate enc)
|
||||
private static CheckResult VerifyWildEncounterGen2(ITrainerID16 pk, EncounterSlot2 enc) => enc.Type switch
|
||||
{
|
||||
SlotType2.Headbutt or SlotType2.HeadbuttSpecial => enc.IsTreeAvailable(pk.TID16)
|
||||
? GetValid(LG2TreeID)
|
||||
: GetInvalid(LG2InvalidTileTreeNotFound),
|
||||
_ => GetValid(LEncCondition),
|
||||
? GetValid(G2TreeID)
|
||||
: GetInvalid(G2InvalidTileTreeNotFound),
|
||||
_ => GetValid(EncCondition),
|
||||
};
|
||||
|
||||
// Eggs
|
||||
@@ -68,7 +68,7 @@ private static CheckResult VerifyEncounterG12(PKM pk, IEncounterTemplate enc)
|
||||
EncounterEgg8b=> VerifyUnhatchedEgg(pk, Locations.LinkTrade6NPC, Locations.Default8bNone),
|
||||
EncounterEgg8 => VerifyUnhatchedEgg(pk, Locations.LinkTrade6),
|
||||
EncounterEgg9 => VerifyUnhatchedEgg(pk, Locations.LinkTrade6),
|
||||
_ => GetInvalid(LEggLocationInvalid),
|
||||
_ => GetInvalid(EggLocationInvalid),
|
||||
};
|
||||
|
||||
private static CheckResult VerifyEncounterEggHatched(PKM pk, EntityContext context) => context switch
|
||||
@@ -82,46 +82,46 @@ private static CheckResult VerifyEncounterG12(PKM pk, IEncounterTemplate enc)
|
||||
EntityContext.Gen8b=> VerifyEncounterEgg8BDSP(pk),
|
||||
EntityContext.Gen8 => VerifyEncounterEgg8(pk),
|
||||
EntityContext.Gen9 => VerifyEncounterEgg9(pk),
|
||||
_ => GetInvalid(LEggLocationInvalid),
|
||||
_ => GetInvalid(EggLocationInvalid),
|
||||
};
|
||||
|
||||
private static CheckResult VerifyEncounterEgg2(PKM pk)
|
||||
{
|
||||
if (pk is not ICaughtData2 { CaughtData: not 0 } c2)
|
||||
return GetValid(LEggLocation);
|
||||
return GetValid(EggLocation);
|
||||
|
||||
if (c2.MetLevel != EggStateLegality.EggMetLevel)
|
||||
return GetInvalid(string.Format(LEggFMetLevel_0, EggStateLegality.EggMetLevel));
|
||||
return GetInvalid(EggFMetLevel_0, EggStateLegality.EggMetLevel);
|
||||
|
||||
if (pk.MetLocation > 95)
|
||||
return GetInvalid(LEggMetLocationFail);
|
||||
return GetInvalid(EggMetLocationFail);
|
||||
// Any met location is fine.
|
||||
return GetValid(LEggLocation);
|
||||
return GetValid(EggLocation);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyUnhatchedEgg2(PKM pk)
|
||||
{
|
||||
if (pk is not ICaughtData2 { CaughtData: not 0 } c2)
|
||||
return new CheckResult(CheckIdentifier.Encounter);
|
||||
return CheckResult.GetValid(CheckIdentifier.Encounter);
|
||||
|
||||
if (c2.MetLevel != EggStateLegality.EggMetLevel)
|
||||
return GetInvalid(string.Format(LEggFMetLevel_0, EggStateLegality.EggMetLevel));
|
||||
return GetInvalid(EggFMetLevel_0, EggStateLegality.EggMetLevel);
|
||||
if (c2.MetLocation != 0)
|
||||
return GetInvalid(LEggLocationInvalid);
|
||||
return GetValid(LEggLocation);
|
||||
return GetInvalid(EggLocationInvalid);
|
||||
return GetValid(EggLocation);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyUnhatchedEgg3(PKM pk)
|
||||
{
|
||||
if (pk.MetLevel != EggStateLegality.EggMetLevel34)
|
||||
return GetInvalid(string.Format(LEggFMetLevel_0, EggStateLegality.EggMetLevel34));
|
||||
return GetInvalid(EggFMetLevel_0, EggStateLegality.EggMetLevel34);
|
||||
|
||||
// Only EncounterEgg should reach here.
|
||||
var loc = pk.FRLG ? Locations.HatchLocationFRLG : Locations.HatchLocationRSE;
|
||||
if (pk.MetLocation != loc)
|
||||
return GetInvalid(LEggMetLocationFail);
|
||||
return GetInvalid(EggMetLocationFail);
|
||||
|
||||
return GetValid(LEggLocation);
|
||||
return GetValid(EggLocation);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyEncounterEgg3(PKM pk)
|
||||
@@ -130,46 +130,47 @@ private static CheckResult VerifyEncounterEgg3(PKM pk)
|
||||
return VerifyEncounterEgg3Transfer(pk);
|
||||
|
||||
if (pk.MetLevel != EggStateLegality.EggMetLevel34)
|
||||
return GetInvalid(string.Format(LEggFMetLevel_0, EggStateLegality.EggMetLevel34));
|
||||
return GetInvalid(EggFMetLevel_0, EggStateLegality.EggMetLevel34);
|
||||
|
||||
// Check the origin game list.
|
||||
var met = (byte)pk.MetLocation;
|
||||
bool valid = EggHatchLocation3.IsValidMet3(met, pk.Version);
|
||||
if (valid)
|
||||
return GetValid(LEggLocation);
|
||||
return GetValid(EggLocation);
|
||||
|
||||
// Version isn't updated when hatching on a different game. Check any game.
|
||||
if (EggHatchLocation3.IsValidMet3Any(met))
|
||||
return GetValid(LEggLocationTrade);
|
||||
return GetInvalid(LEggLocationInvalid);
|
||||
return GetValid(EggLocationTrade);
|
||||
return GetInvalid(EggLocationInvalid);
|
||||
}
|
||||
|
||||
private static CheckResult GetInvalid(string message, CheckIdentifier ident = CheckIdentifier.Encounter) => new(Severity.Invalid, ident, message);
|
||||
private static CheckResult GetValid(string message) => new(Severity.Valid, CheckIdentifier.Encounter, message);
|
||||
private static CheckResult GetInvalid(LegalityCheckResultCode message, CheckIdentifier ident = CheckIdentifier.Encounter) => CheckResult.Get(Severity.Invalid, ident, message);
|
||||
private static CheckResult GetInvalid(LegalityCheckResultCode message, byte value, CheckIdentifier ident = CheckIdentifier.Encounter) => CheckResult.Get(Severity.Invalid, ident, message, value);
|
||||
private static CheckResult GetValid(LegalityCheckResultCode message) => CheckResult.Get(Severity.Valid, CheckIdentifier.Encounter, message);
|
||||
|
||||
private static CheckResult VerifyEncounterEgg3Transfer(PKM pk)
|
||||
{
|
||||
if (pk.IsEgg)
|
||||
return GetInvalid(LTransferEgg);
|
||||
return GetInvalid(TransferEgg);
|
||||
if (pk.MetLevel < EggStateLegality.EggLevel23)
|
||||
return GetInvalid(LTransferEggMetLevel);
|
||||
return GetInvalid(TransferEggMetLevel);
|
||||
|
||||
var expectEgg = pk is PB8 ? Locations.Default8bNone : 0;
|
||||
if (pk.EggLocation != expectEgg)
|
||||
return GetInvalid(LEggLocationNone);
|
||||
return GetInvalid(EggLocationNone);
|
||||
|
||||
if (pk.Format != 4)
|
||||
{
|
||||
if (pk.MetLocation != Locations.Transfer4)
|
||||
return GetInvalid(LTransferEggLocationTransporter);
|
||||
return GetInvalid(TransferEggLocationTransporter);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pk.MetLocation != Locations.Transfer3)
|
||||
return GetInvalid(LEggLocationPalPark);
|
||||
return GetInvalid(EggLocationPalPark);
|
||||
}
|
||||
|
||||
return GetValid(LEggLocation);
|
||||
return GetValid(EggLocation);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyEncounterEgg4(PKM pk)
|
||||
@@ -177,49 +178,49 @@ private static CheckResult VerifyEncounterEgg4(PKM pk)
|
||||
if (pk.Format != 4) // transferred
|
||||
{
|
||||
if (pk.IsEgg)
|
||||
return GetInvalid(LTransferEgg);
|
||||
return GetInvalid(TransferEgg);
|
||||
if (pk.MetLevel < EggStateLegality.EggLevel)
|
||||
return GetInvalid(LTransferEggMetLevel);
|
||||
return GetInvalid(TransferEggMetLevel);
|
||||
if (pk.MetLocation != Locations.Transfer4)
|
||||
return GetInvalid(LTransferEggLocationTransporter);
|
||||
return GetValid(LEggLocation);
|
||||
return GetInvalid(TransferEggLocationTransporter);
|
||||
return GetValid(EggLocation);
|
||||
}
|
||||
|
||||
// Native
|
||||
const byte level = EggStateLegality.EggMetLevel34;
|
||||
if (pk.MetLevel != level)
|
||||
return GetInvalid(string.Format(LEggFMetLevel_0, level));
|
||||
return GetInvalid(EggFMetLevel_0, level);
|
||||
|
||||
var met = pk.MetLocation;
|
||||
bool valid = EggHatchLocation4.IsValidMet4(met, pk.Version);
|
||||
if (valid)
|
||||
return GetValid(LEggLocation);
|
||||
return GetValid(EggLocation);
|
||||
|
||||
// Version isn't updated when hatching on a different game. Check any game.
|
||||
if (pk.EggLocation == Locations.LinkTrade4 && EggHatchLocation4.IsValidMet4Any(met))
|
||||
return GetValid(LEggLocationTrade);
|
||||
return GetInvalid(LEggLocationInvalid);
|
||||
return GetValid(EggLocationTrade);
|
||||
return GetInvalid(EggLocationInvalid);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyEncounterEgg5(PKM pk)
|
||||
{
|
||||
const byte level = EggStateLegality.EggMetLevel;
|
||||
if (pk.MetLevel != level)
|
||||
return GetInvalid(string.Format(LEggFMetLevel_0, level));
|
||||
return GetInvalid(EggFMetLevel_0, level);
|
||||
|
||||
var met = pk.MetLocation;
|
||||
bool valid = EggHatchLocation5.IsValidMet5(met, pk.Version);
|
||||
|
||||
if (valid)
|
||||
return GetValid(LEggLocation);
|
||||
return GetInvalid(LEggLocationInvalid);
|
||||
return GetValid(EggLocation);
|
||||
return GetInvalid(EggLocationInvalid);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyEncounterEgg6(PKM pk)
|
||||
{
|
||||
const byte level = EggStateLegality.EggMetLevel;
|
||||
if (pk.MetLevel != level)
|
||||
return GetInvalid(string.Format(LEggFMetLevel_0, level));
|
||||
return GetInvalid(EggFMetLevel_0, level);
|
||||
|
||||
var met = pk.MetLocation;
|
||||
bool valid = pk.XY
|
||||
@@ -227,15 +228,15 @@ private static CheckResult VerifyEncounterEgg6(PKM pk)
|
||||
: EggHatchLocation6.IsValidMet6AO(met);
|
||||
|
||||
if (valid)
|
||||
return GetValid(LEggLocation);
|
||||
return GetInvalid(LEggLocationInvalid);
|
||||
return GetValid(EggLocation);
|
||||
return GetInvalid(EggLocationInvalid);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyEncounterEgg7(PKM pk)
|
||||
{
|
||||
const byte level = EggStateLegality.EggMetLevel;
|
||||
if (pk.MetLevel != level)
|
||||
return GetInvalid(string.Format(LEggFMetLevel_0, level));
|
||||
return GetInvalid(EggFMetLevel_0, level);
|
||||
|
||||
var met = pk.MetLocation;
|
||||
bool valid = pk.SM
|
||||
@@ -243,20 +244,20 @@ private static CheckResult VerifyEncounterEgg7(PKM pk)
|
||||
: EggHatchLocation7.IsValidMet7USUM(met);
|
||||
|
||||
if (valid)
|
||||
return GetValid(LEggLocation);
|
||||
return GetInvalid(LEggLocationInvalid);
|
||||
return GetValid(EggLocation);
|
||||
return GetInvalid(EggLocationInvalid);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyEncounterEgg8(PKM pk)
|
||||
{
|
||||
const byte level = EggStateLegality.EggMetLevel;
|
||||
if (pk.MetLevel != level)
|
||||
return GetInvalid(string.Format(LEggFMetLevel_0, level));
|
||||
return GetInvalid(EggFMetLevel_0, level);
|
||||
|
||||
var valid = IsValidMetForeignEggSWSH(pk, pk.MetLocation);
|
||||
if (valid)
|
||||
return GetValid(LEggLocation);
|
||||
return GetInvalid(LEggLocationInvalid);
|
||||
return GetValid(EggLocation);
|
||||
return GetInvalid(EggLocationInvalid);
|
||||
}
|
||||
|
||||
private static bool IsValidMetForeignEggSWSH(PKM pk, ushort met)
|
||||
@@ -275,7 +276,7 @@ private static CheckResult VerifyEncounterEgg8BDSP(PKM pk)
|
||||
|
||||
const byte level = EggStateLegality.EggMetLevel;
|
||||
if (pk.MetLevel != level)
|
||||
return GetInvalid(string.Format(LEggFMetLevel_0, level));
|
||||
return GetInvalid(EggFMetLevel_0, level);
|
||||
|
||||
var met = pk.MetLocation;
|
||||
bool valid = pk.Version == GameVersion.BD
|
||||
@@ -283,8 +284,8 @@ private static CheckResult VerifyEncounterEgg8BDSP(PKM pk)
|
||||
: EggHatchLocation8b.IsValidMet8SP(met);
|
||||
|
||||
if (valid)
|
||||
return GetValid(LEggLocation);
|
||||
return GetInvalid(LEggLocationInvalid);
|
||||
return GetValid(EggLocation);
|
||||
return GetInvalid(EggLocationInvalid);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyEncounterEgg9(PKM pk)
|
||||
@@ -294,7 +295,7 @@ private static CheckResult VerifyEncounterEgg9(PKM pk)
|
||||
|
||||
const byte level = EggStateLegality.EggMetLevel;
|
||||
if (pk.MetLevel != level)
|
||||
return GetInvalid(string.Format(LEggFMetLevel_0, level));
|
||||
return GetInvalid(EggFMetLevel_0, level);
|
||||
|
||||
var met = pk.MetLocation;
|
||||
bool valid = pk.Version == GameVersion.SL
|
||||
@@ -302,40 +303,40 @@ private static CheckResult VerifyEncounterEgg9(PKM pk)
|
||||
: EggHatchLocation9.IsValidMet9VL(met);
|
||||
|
||||
if (valid)
|
||||
return GetValid(LEggLocation);
|
||||
return GetInvalid(LEggLocationInvalid);
|
||||
return GetValid(EggLocation);
|
||||
return GetInvalid(EggLocationInvalid);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyUnhatchedEgg(PKM pk, int tradeLoc, ushort noneLoc = 0)
|
||||
{
|
||||
var eggLevel = pk.Format is 3 or 4 ? EggStateLegality.EggMetLevel34 : EggStateLegality.EggMetLevel;
|
||||
if (pk.MetLevel != eggLevel)
|
||||
return GetInvalid(string.Format(LEggFMetLevel_0, eggLevel));
|
||||
return GetInvalid(EggFMetLevel_0, eggLevel);
|
||||
if (pk.EggLocation == tradeLoc)
|
||||
return GetInvalid(LEggLocationTradeFail);
|
||||
return GetInvalid(EggLocationTradeFail);
|
||||
|
||||
var met = pk.MetLocation;
|
||||
if (met == tradeLoc)
|
||||
return GetValid(LEggLocationTrade);
|
||||
return GetValid(EggLocationTrade);
|
||||
return met == noneLoc
|
||||
? GetValid(LEggUnhatched)
|
||||
: GetInvalid(LEggLocationNone);
|
||||
? GetValid(EggUnhatched)
|
||||
: GetInvalid(EggLocationNone);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyUnhatchedEgg5(PKM pk)
|
||||
{
|
||||
const byte eggLevel = EggStateLegality.EggMetLevel;
|
||||
if (pk.MetLevel != eggLevel)
|
||||
return GetInvalid(string.Format(LEggFMetLevel_0, eggLevel));
|
||||
return GetInvalid(EggFMetLevel_0, eggLevel);
|
||||
if (pk.EggLocation is (Locations.LinkTrade5 or Locations.LinkTrade5NPC))
|
||||
return GetInvalid(LEggLocationTradeFail);
|
||||
return GetInvalid(EggLocationTradeFail);
|
||||
|
||||
var met = pk.MetLocation;
|
||||
if (met is (Locations.LinkTrade5 or Locations.LinkTrade5NPC))
|
||||
return GetValid(LEggLocationTrade);
|
||||
return GetValid(EggLocationTrade);
|
||||
return met == 0
|
||||
? GetValid(LEggUnhatched)
|
||||
: GetInvalid(LEggLocationNone);
|
||||
? GetValid(EggUnhatched)
|
||||
: GetInvalid(EggLocationNone);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyEncounterTrade(ISpeciesForm pk, EncounterTrade1 trade)
|
||||
@@ -346,23 +347,13 @@ private static CheckResult VerifyEncounterTrade(ISpeciesForm pk, EncounterTrade1
|
||||
// Pokémon that evolve on trade can not be in the phase evolution after the trade
|
||||
// If the trade holds an Everstone, EvolveOnTrade will be false for the encounter
|
||||
// No need to range check the species, as it matched to a valid encounter species.
|
||||
var names = ParseSettings.SpeciesStrings;
|
||||
var evolved = names[species + 1];
|
||||
var unevolved = names[species];
|
||||
return GetInvalid(string.Format(LEvoTradeReq, unevolved, evolved));
|
||||
return GetInvalid(EncTradeShouldHaveEvolvedToSpecies_0);
|
||||
}
|
||||
return GetValid(LEncTradeMatch);
|
||||
return GetValid(EncTradeMatch);
|
||||
}
|
||||
|
||||
private static CheckResult VerifyEncounterEvent(PKM pk, MysteryGift gift)
|
||||
{
|
||||
switch (gift)
|
||||
{
|
||||
case PCD pcd:
|
||||
if (!pcd.CanBeReceivedByVersion(pk.Version) && pcd.Gift.PK.Version == 0)
|
||||
return GetInvalid(string.Format(L_XMatches0_1, gift.CardHeader, $"-- {LEncGiftVersionNotDistributed}"));
|
||||
break;
|
||||
}
|
||||
if (!pk.IsEgg && gift.IsEgg) // hatched
|
||||
{
|
||||
var hatchCheck = VerifyEncounterEggHatched(pk, gift.Context);
|
||||
@@ -371,6 +362,6 @@ private static CheckResult VerifyEncounterEvent(PKM pk, MysteryGift gift)
|
||||
}
|
||||
|
||||
// Strict matching already performed by EncounterGenerator. May be worth moving some checks here to better flag invalid gifts.
|
||||
return GetValid(string.Format(L_XMatches0_1, gift.CardHeader, string.Empty));
|
||||
return GetValid(Valid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using static PKHeX.Core.EvolutionRestrictions;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace PKHeX.Core;
|
||||
/// </summary>
|
||||
public static class EvolutionVerifier
|
||||
{
|
||||
private static readonly CheckResult VALID = new(CheckIdentifier.Evolution);
|
||||
private static readonly CheckResult VALID = CheckResult.GetValid(CheckIdentifier.Evolution);
|
||||
|
||||
/// <summary>
|
||||
/// Verifies Evolution scenarios of <see cref="IEncounterable"/> templates for an input <see cref="PKM"/> and relevant <see cref="LegalInfo"/>.
|
||||
@@ -19,11 +19,11 @@ public static CheckResult VerifyEvolution(PKM pk, LegalInfo info)
|
||||
{
|
||||
// Check if basic evolution methods are satisfiable with this encounter.
|
||||
if (!IsValidEvolution(pk, info.EvoChainsAllGens, info.EncounterOriginal))
|
||||
return new CheckResult(Severity.Invalid, CheckIdentifier.Evolution, LEvoInvalid);
|
||||
return CheckResult.Get(Severity.Invalid, CheckIdentifier.Evolution, EvoInvalid);
|
||||
|
||||
// Check if complex evolution methods are satisfiable with this encounter.
|
||||
if (!IsValidEvolutionWithMove(pk, info))
|
||||
return new CheckResult(Severity.Invalid, CheckIdentifier.Evolution, string.Format(LMoveEvoFCombination_0, ParseSettings.SpeciesStrings[pk.Species]));
|
||||
return CheckResult.Get(Severity.Invalid, CheckIdentifier.Evolution, MoveEvoFCombination_0, pk.Species);
|
||||
|
||||
return VALID;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -38,24 +38,24 @@ public static CheckResult VerifyGift(PKM pk, MysteryGift g)
|
||||
{
|
||||
bool restricted = TryGetRestriction(g, out var value);
|
||||
if (!restricted)
|
||||
return new CheckResult(CheckIdentifier.GameOrigin);
|
||||
return CheckResult.GetValid(CheckIdentifier.GameOrigin);
|
||||
|
||||
var version = (int)value >> 16;
|
||||
if (version != 0 && !CanVersionReceiveGift(g.Generation, version, pk.Version))
|
||||
return new CheckResult(Severity.Invalid, CheckIdentifier.GameOrigin, LEncGiftVersionNotDistributed);
|
||||
return CheckResult.Get(Severity.Invalid, CheckIdentifier.GameOrigin, EncGiftVersionNotDistributed);
|
||||
|
||||
var lang = value & MysteryGiftRestriction.LangRestrict;
|
||||
if (lang != 0 && !lang.HasFlag((MysteryGiftRestriction) (1 << pk.Language)))
|
||||
return new CheckResult(Severity.Invalid, CheckIdentifier.GameOrigin, string.Format(LOTLanguage, lang.GetSuggestedLanguage(), pk.Language));
|
||||
return CheckResult.Get(Severity.Invalid, CheckIdentifier.GameOrigin, OTLanguageShouldBe_0, (ushort)lang.GetSuggestedLanguage());
|
||||
|
||||
if (pk is IRegionOriginReadOnly tr)
|
||||
{
|
||||
var region = value & MysteryGiftRestriction.RegionRestrict;
|
||||
if (region != 0 && !region.HasFlag((MysteryGiftRestriction)((int)MysteryGiftRestriction.RegionBase << tr.ConsoleRegion)))
|
||||
return new CheckResult(Severity.Invalid, CheckIdentifier.GameOrigin, LGeoHardwareRange);
|
||||
return CheckResult.Get(Severity.Invalid, CheckIdentifier.GameOrigin, EncGiftRegionNotDistributed, (ushort)region.GetSuggestedRegion());
|
||||
}
|
||||
|
||||
return new CheckResult(CheckIdentifier.GameOrigin);
|
||||
return CheckResult.GetValid(CheckIdentifier.GameOrigin);
|
||||
}
|
||||
|
||||
private static bool TryGetRestriction(MysteryGift g, out MysteryGiftRestriction val)
|
||||
|
||||
@@ -82,10 +82,10 @@ public EvolutionCheckResult Check(PKM pk, byte lvl, byte levelMin, bool skipChec
|
||||
// Version checks come in pairs, check for any pair match
|
||||
LevelUpVersion or LevelUpVersionDay or LevelUpVersionNight when (((byte)pk.Version & 1) != (Argument & 1) && pk.IsUntraded) => skipChecks ? Valid : VisitVersion,
|
||||
|
||||
LevelUpKnowMoveEC100 when pk.EncryptionConstant % 100 != 0 => skipChecks ? Valid : WrongEC,
|
||||
LevelUpKnowMoveECElse when pk.EncryptionConstant % 100 == 0 => skipChecks ? Valid : WrongEC,
|
||||
LevelUpInBattleEC100 when pk.EncryptionConstant % 100 != 0 => skipChecks ? Valid : WrongEC,
|
||||
LevelUpInBattleECElse when pk.EncryptionConstant % 100 == 0 => skipChecks ? Valid : WrongEC,
|
||||
LevelUpKnowMoveEC100 when !EvolutionRestrictions.IsEvolvedSpeciesFormRare(pk.EncryptionConstant) => skipChecks ? Valid : WrongEC,
|
||||
LevelUpKnowMoveECElse when EvolutionRestrictions.IsEvolvedSpeciesFormRare(pk.EncryptionConstant) => skipChecks ? Valid : WrongEC,
|
||||
LevelUpInBattleEC100 when !EvolutionRestrictions.IsEvolvedSpeciesFormRare(pk.EncryptionConstant) => skipChecks ? Valid : WrongEC,
|
||||
LevelUpInBattleECElse when EvolutionRestrictions.IsEvolvedSpeciesFormRare(pk.EncryptionConstant) => skipChecks ? Valid : WrongEC,
|
||||
|
||||
_ => Valid,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -12,49 +11,52 @@ public sealed class BaseLegalityFormatter : ILegalityFormatter
|
||||
/// <summary>
|
||||
/// Gets a minimal report string for the analysis.
|
||||
/// </summary>
|
||||
public string GetReport(LegalityAnalysis l)
|
||||
public string GetReport(in LegalityLocalizationContext la)
|
||||
{
|
||||
var l = la.Analysis;
|
||||
if (l.Valid)
|
||||
return L_ALegal;
|
||||
return la.Settings.Lines.Legal;
|
||||
if (!l.Parsed)
|
||||
return L_AnalysisUnavailable;
|
||||
return la.Settings.Lines.AnalysisUnavailable;
|
||||
|
||||
List<string> lines = [];
|
||||
GetLegalityReportLines(l, lines);
|
||||
GetLegalityReportLines(la, lines);
|
||||
return string.Join(Environment.NewLine, lines);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a verbose report string for the analysis.
|
||||
/// </summary>
|
||||
public string GetReportVerbose(LegalityAnalysis l)
|
||||
public string GetReportVerbose(in LegalityLocalizationContext la)
|
||||
{
|
||||
var l = la.Analysis;
|
||||
if (!l.Parsed)
|
||||
return L_AnalysisUnavailable;
|
||||
return la.Settings.Lines.AnalysisUnavailable;
|
||||
|
||||
var lines = GetVerboseLegalityReportLines(l);
|
||||
var lines = GetVerboseLegalityReportLines(la);
|
||||
return string.Join(Environment.NewLine, lines);
|
||||
}
|
||||
|
||||
private static void GetLegalityReportLines(LegalityAnalysis l, List<string> lines)
|
||||
private static void GetLegalityReportLines(in LegalityLocalizationContext la, List<string> lines)
|
||||
{
|
||||
var l = la.Analysis;
|
||||
var info = l.Info;
|
||||
var pk = l.Entity;
|
||||
|
||||
var evos = info.EvoChainsAllGens;
|
||||
LegalityFormatting.AddMoves(info.Moves, lines, pk.Format, false, pk, evos);
|
||||
LegalityFormatting.AddMoves(la, info.Moves, lines, pk.Format, false);
|
||||
if (pk.Format >= 6)
|
||||
LegalityFormatting.AddRelearn(info.Relearn, lines, false, pk, evos);
|
||||
LegalityFormatting.AddSecondaryChecksInvalid(l.Results, lines);
|
||||
LegalityFormatting.AddRelearn(la, info.Relearn, lines, false);
|
||||
LegalityFormatting.AddSecondaryChecksInvalid(la, l.Results, lines);
|
||||
}
|
||||
|
||||
private static List<string> GetVerboseLegalityReportLines(LegalityAnalysis l)
|
||||
private static List<string> GetVerboseLegalityReportLines(in LegalityLocalizationContext la)
|
||||
{
|
||||
var l = la.Analysis;
|
||||
var lines = new List<string>();
|
||||
if (l.Valid)
|
||||
lines.Add(L_ALegal);
|
||||
lines.Add(la.Settings.Lines.Legal);
|
||||
else
|
||||
GetLegalityReportLines(l, lines);
|
||||
GetLegalityReportLines(la, lines);
|
||||
var info = l.Info;
|
||||
var pk = l.Entity;
|
||||
const string separator = "===";
|
||||
@@ -63,20 +65,19 @@ private static List<string> GetVerboseLegalityReportLines(LegalityAnalysis l)
|
||||
int initialCount = lines.Count;
|
||||
|
||||
var format = pk.Format;
|
||||
var evos = info.EvoChainsAllGens;
|
||||
LegalityFormatting.AddMoves(info.Moves, lines, format, true, pk, evos);
|
||||
LegalityFormatting.AddMoves(la, info.Moves, lines, format, true);
|
||||
|
||||
if (format >= 6)
|
||||
LegalityFormatting.AddRelearn(info.Relearn, lines, true, pk, evos);
|
||||
LegalityFormatting.AddRelearn(la, info.Relearn, lines, true);
|
||||
|
||||
if (lines.Count != initialCount) // move info added, break for next section
|
||||
lines.Add(string.Empty);
|
||||
|
||||
LegalityFormatting.AddSecondaryChecksValid(l.Results, lines);
|
||||
LegalityFormatting.AddSecondaryChecksValid(la, l.Results, lines);
|
||||
|
||||
lines.Add(separator);
|
||||
lines.Add(string.Empty);
|
||||
LegalityFormatting.AddEncounterInfo(l, lines);
|
||||
LegalityFormatting.AddEncounterInfo(la, lines);
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace PKHeX.Core;
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Formats legality results into a <see cref="T:System.String"/> for display.
|
||||
@@ -8,10 +8,10 @@ public interface ILegalityFormatter
|
||||
/// <summary>
|
||||
/// Gets a small summary of the legality analysis.
|
||||
/// </summary>
|
||||
string GetReport(LegalityAnalysis l);
|
||||
string GetReport(in LegalityLocalizationContext l);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a verbose summary of the legality analysis.
|
||||
/// </summary>
|
||||
string GetReportVerbose(LegalityAnalysis l);
|
||||
string GetReportVerbose(in LegalityLocalizationContext l);
|
||||
}
|
||||
|
||||
@@ -1,523 +0,0 @@
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Legality Check Message Strings to indicate why certain <see cref="PKM"/> <see cref="LegalInfo"/> values are flagged.
|
||||
/// </summary>
|
||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
|
||||
public static class LegalityCheckStrings
|
||||
{
|
||||
// Message String Name format: L/F[Category][Summary]
|
||||
#region General Strings
|
||||
|
||||
/// <summary>Default text for indicating validity.</summary>
|
||||
public static string L_AValid { get; set; } = "Valid.";
|
||||
|
||||
/// <summary>Default text for indicating legality.</summary>
|
||||
public static string L_ALegal { get; set; } = "Legal!";
|
||||
|
||||
/// <summary>Default text for indicating an error has occurred.</summary>
|
||||
public static string L_AError { get; set; } = "Internal error.";
|
||||
|
||||
/// <summary>Analysis not available for the <see cref="PKM"/></summary>
|
||||
public static string L_AnalysisUnavailable { get; set; } = "Analysis not available for this Pokémon.";
|
||||
|
||||
/// <summary>Format text for exporting a legality check result.</summary>
|
||||
public static string L_F0_1 { get; set; } = "{0}: {1}";
|
||||
|
||||
/// <summary>Format text for exporting a legality check result for a Move.</summary>
|
||||
public static string L_F0_M_1_2 { get; set; } = "{0} Move {1}: {2}";
|
||||
|
||||
/// <summary>Format text for exporting a legality check result for a Relearn Move.</summary>
|
||||
public static string L_F0_RM_1_2 { get; set; } = "{0} Relearn Move {1}: {2}";
|
||||
|
||||
/// <summary>Format text for exporting the type of Encounter that was matched for the <see cref="PKM"/></summary>
|
||||
public static string L_FEncounterType_0 { get; set; } = "Encounter Type: {0}";
|
||||
|
||||
/// <summary>Format text for exporting the <see cref="PIDIV.OriginSeed"/> that was matched for the <see cref="PKM"/></summary>
|
||||
public static string L_FOriginSeed_0 { get; set; } = "Origin Seed: {0}";
|
||||
|
||||
/// <summary>Format text for exporting the <see cref="PIDIV.Type"/> that was matched for the <see cref="PKM"/></summary>
|
||||
public static string L_FPIDType_0 { get; set; } = "PID Type: {0}";
|
||||
|
||||
/// <summary>Severity string for <see cref="Severity.Invalid"/></summary>
|
||||
public static string L_SInvalid { get; set; } = "Invalid";
|
||||
|
||||
/// <summary>Severity string for <see cref="Severity.Fishy"/></summary>
|
||||
public static string L_SFishy { get; set; } = "Fishy";
|
||||
|
||||
/// <summary>Severity string for <see cref="Severity.Valid"/></summary>
|
||||
public static string L_SValid { get; set; } = "Valid";
|
||||
|
||||
/// <summary>Severity string for anything not implemented.</summary>
|
||||
public static string L_SNotImplemented { get; set; } = "Not Implemented";
|
||||
|
||||
public static string L_XOT { get; set; } = "OT";
|
||||
public static string L_XHT { get; set; } = "HT";
|
||||
public static string L_XNickname { get; set; } = "Nickname";
|
||||
public static string L_XKorean { get; set; } = "Korean";
|
||||
public static string L_XKoreanNon { get; set; } = "Non-Korean";
|
||||
public static string L_XLocation { get; set; } = "Location";
|
||||
public static string L_XEnigmaBerry_0 { get; set; } = "{0} Berry";
|
||||
public static string L_XMatches0_1 { get; set; } = "Matches: {0} {1}";
|
||||
public static string L_XWurmpleEvo_0 { get; set; } = "Wurmple Evolution: {0}";
|
||||
public static string L_XRareFormEvo_0_1 { get; set; } = "Evolves into form: {0} (rare: {1})";
|
||||
|
||||
public static string LAbilityCapsuleUsed { get; set; } = "Ability available with Ability Capsule.";
|
||||
public static string LAbilityPatchUsed { get; set; } = "Ability available with Ability Patch.";
|
||||
public static string LAbilityPatchRevertUsed { get; set; } = "Ability available with Ability Patch Revert.";
|
||||
public static string LAbilityFlag { get; set; } = "Ability matches ability number.";
|
||||
public static string LAbilityHiddenFail { get; set; } = "Hidden Ability mismatch for encounter type.";
|
||||
public static string LAbilityHiddenUnavailable { get; set; } = "Hidden Ability not available.";
|
||||
public static string LAbilityMismatch { get; set; } = "Ability mismatch for encounter.";
|
||||
public static string LAbilityMismatch3 { get; set; } = "Ability does not match Generation 3 species ability.";
|
||||
public static string LAbilityMismatchFlag { get; set; } = "Ability does not match ability number.";
|
||||
public static string LAbilityMismatchGift { get; set; } = "Ability does not match Mystery Gift.";
|
||||
public static string LAbilityMismatchGrotto { get; set; } = "Hidden Grotto captures should have Hidden Ability.";
|
||||
public static string LAbilityMismatchHordeSafari { get; set; } = "Hidden Ability on non-horde/friend safari wild encounter.";
|
||||
public static string LAbilityMismatchPID { get; set; } = "Ability does not match PID.";
|
||||
public static string LAbilityMismatchSOS { get; set; } = "Hidden Ability on non-SOS wild encounter.";
|
||||
public static string LAbilityUnexpected { get; set; } = "Ability is not valid for species/form.";
|
||||
|
||||
public static string LAwakenedCap { get; set; } = "Individual AV cannot be greater than {0}.";
|
||||
public static string LAwakenedShouldBeValue { get; set; } = "Individual AV ({1}) should be greater than {0}.";
|
||||
|
||||
public static string LBallAbility { get; set; } = "Can't obtain Hidden Ability with Ball.";
|
||||
public static string LBallEggCherish { get; set; } = "Can't have Cherish Ball for regular Egg.";
|
||||
public static string LBallEggMaster { get; set; } = "Can't have Master Ball for regular Egg.";
|
||||
public static string LBallEnc { get; set; } = "Correct ball for encounter type.";
|
||||
public static string LBallEncMismatch { get; set; } = "Can't have ball for encounter type.";
|
||||
public static string LBallHeavy { get; set; } = "Can't have Heavy Ball for light, low-catch rate species (Gen VII).";
|
||||
public static string LBallNone { get; set; } = "No check satisfied, assuming illegal.";
|
||||
public static string LBallSpecies { get; set; } = "Can't obtain species in Ball.";
|
||||
public static string LBallSpeciesPass { get; set; } = "Ball possible for species.";
|
||||
public static string LBallUnavailable { get; set; } = "Ball unobtainable in origin Generation.";
|
||||
|
||||
public static string LContestZero { get; set; } = "Contest Stats should be 0.";
|
||||
public static string LContestZeroSheen { get; set; } = "Contest Stat Sheen should be 0.";
|
||||
public static string LContestSheenTooLow_0 { get; set; } = "Contest Stat Sheen should be >= {0}.";
|
||||
public static string LContestSheenTooHigh_0 { get; set; } = "Contest Stat Sheen should be <= {0}.";
|
||||
|
||||
public static string LDateOutsideConsoleWindow { get; set; } = "Local Date is outside of console's local time window.";
|
||||
public static string LDateTimeClockInvalid { get; set; } = "Local Time is not a valid timestamp.";
|
||||
public static string LDateOutsideDistributionWindow { get; set; } = "Met Date is outside of distribution window.";
|
||||
|
||||
public static string LEggContest { get; set; } = "Cannot increase Contest Stats of an Egg.";
|
||||
public static string LEggEXP { get; set; } = "Eggs cannot receive experience.";
|
||||
public static string LEggFMetLevel_0 { get; set; } = "Invalid Met Level, expected {0}.";
|
||||
public static string LEggHatchCycles { get; set; } = "Invalid Egg hatch cycles.";
|
||||
public static string LEggLocation { get; set; } = "Able to hatch an Egg at Met Location.";
|
||||
public static string LEggLocationInvalid { get; set; } = "Can't hatch an Egg at Met Location.";
|
||||
public static string LEggLocationNone { get; set; } = "Invalid Egg Location, expected none.";
|
||||
public static string LEggLocationPalPark { get; set; } = "Invalid Met Location, expected Pal Park.";
|
||||
public static string LEggLocationTrade { get; set; } = "Able to hatch a traded Egg at Met Location.";
|
||||
public static string LEggLocationTradeFail { get; set; } = "Invalid Egg Location, shouldn't be 'traded' while an Egg.";
|
||||
public static string LEggMetLocationFail { get; set; } = "Can't obtain Egg from Egg Location.";
|
||||
public static string LEggNature { get; set; } = "Eggs cannot have their Stat Nature changed.";
|
||||
public static string LEggPokeathlon { get; set; } = "Eggs cannot have Pokéathlon stats.";
|
||||
public static string LEggPokerus { get; set; } = "Eggs cannot be infected with Pokérus.";
|
||||
public static string LEggPP { get; set; } = "Eggs cannot have modified move PP counts.";
|
||||
public static string LEggPPUp { get; set; } = "Cannot apply PP Ups to an Egg.";
|
||||
public static string LEggRelearnFlags { get; set; } = "Expected no Relearn Move Flags.";
|
||||
public static string LEggShinyLeaf { get; set; } = "Eggs cannot have Shiny Leaf/Crown.";
|
||||
public static string LEggShinyPokeStar { get; set; } = "Eggs cannot be a Pokéstar Studios star.";
|
||||
public static string LEggSpecies { get; set; } = "Can't obtain Egg for this species.";
|
||||
public static string LEggUnhatched { get; set; } = "Valid un-hatched Egg.";
|
||||
|
||||
public static string LEncCondition { get; set; } = "Valid Wild Encounter at location.";
|
||||
public static string LEncConditionBadRNGFrame { get; set; } = "Unable to match encounter conditions to a possible RNG frame.";
|
||||
public static string LEncConditionBadSpecies { get; set; } = "Species does not exist in origin game.";
|
||||
public static string LEncConditionBlack { get; set; } = "Valid Wild Encounter at location (Black Flute).";
|
||||
public static string LEncConditionBlackLead { get; set; } = "Valid Wild Encounter at location (Black Flute & Pressure/Hustle/Vital Spirit).";
|
||||
public static string LEncConditionDexNav { get; set; } = "Valid Wild Encounter at location (DexNav).";
|
||||
public static string LEncConditionLead { get; set; } = "Valid Wild Encounter at location (Pressure/Hustle/Vital Spirit).";
|
||||
public static string LEncConditionWhite { get; set; } = "Valid Wild Encounter at location (White Flute).";
|
||||
public static string LEncConditionWhiteLead { get; set; } = "Valid Wild Encounter at location (White Flute & Pressure/Hustle/Vital Spirit).";
|
||||
|
||||
public static string LEncGift { get; set; } = "Unable to match a gift Egg encounter from origin game.";
|
||||
public static string LEncGiftEggEvent { get; set; } = "Unable to match an event Egg encounter from origin game.";
|
||||
public static string LEncGiftIVMismatch { get; set; } = "IVs do not match Mystery Gift Data.";
|
||||
public static string LEncGiftNicknamed { get; set; } = "Event gift has been nicknamed.";
|
||||
public static string LEncGiftNotFound { get; set; } = "Unable to match to a Mystery Gift in the database.";
|
||||
public static string LEncGiftPIDMismatch { get; set; } = "Mystery Gift fixed PID mismatch.";
|
||||
public static string LEncGiftShinyMismatch { get; set; } = "Mystery Gift shiny mismatch.";
|
||||
public static string LEncGiftVersionNotDistributed { get; set; } = "Mystery Gift cannot be received by this version.";
|
||||
|
||||
public static string LEncInvalid { get; set; } = "Unable to match an encounter from origin game.";
|
||||
public static string LEncMasteryInitial { get; set; } = "Initial move mastery flags do not match the encounter's expected state.";
|
||||
|
||||
public static string LEncTradeChangedNickname { get; set; } = "In-game Trade Nickname has been altered.";
|
||||
public static string LEncTradeChangedOT { get; set; } = "In-game Trade OT has been altered.";
|
||||
public static string LEncTradeIndexBad { get; set; } = "In-game Trade invalid index?";
|
||||
public static string LEncTradeMatch { get; set; } = "Valid In-game trade.";
|
||||
public static string LEncTradeUnchanged { get; set; } = "In-game Trade OT and Nickname have not been altered.";
|
||||
|
||||
public static string LEncStaticMatch { get; set; } = "Valid gift/static encounter.";
|
||||
public static string LEncStaticPIDShiny { get; set; } = "Static Encounter shiny mismatch.";
|
||||
public static string LEncStaticRelearn { get; set; } = "Static encounter relearn move mismatch.";
|
||||
|
||||
public static string LEncTypeMatch { get; set; } = "Encounter Type matches encounter.";
|
||||
public static string LEncTypeMismatch { get; set; } = "Encounter Type does not match encounter.";
|
||||
public static string LEncUnreleased { get; set; } = "Unreleased event.";
|
||||
public static string LEncUnreleasedEMewJP { get; set; } = "Non japanese Mew from Faraway Island. Unreleased event.";
|
||||
public static string LEncUnreleasedHoOArceus { get; set; } = "Arceus from Hall of Origin. Unreleased event.";
|
||||
public static string LEncUnreleasedPtDarkrai { get; set; } = "Non Platinum Darkrai from Newmoon Island. Unreleased event.";
|
||||
public static string LEncUnreleasedPtShaymin { get; set; } = "Non Platinum Shaymin from Flower Paradise. Unreleased event.";
|
||||
|
||||
public static string LEReaderAmerica { get; set; } = "American E-Reader Berry in Japanese save file.";
|
||||
public static string LEReaderInvalid { get; set; } = "Invalid E-Reader Berry.";
|
||||
public static string LEReaderJapan { get; set; } = "Japanese E-Reader Berry in international save file.";
|
||||
|
||||
public static string LEffort2Remaining { get; set; } = "2 EVs remaining.";
|
||||
public static string LEffortAbove252 { get; set; } = "EVs cannot go above 252.";
|
||||
public static string LEffortAbove510 { get; set; } = "EV total cannot be above 510.";
|
||||
public static string LEffortAllEqual { get; set; } = "EVs are all equal.";
|
||||
public static string LEffortCap100 { get; set; } = "Individual EV for a level 100 encounter in Generation 4 cannot be greater than 100.";
|
||||
public static string LEffortEgg { get; set; } = "Eggs cannot receive EVs.";
|
||||
public static string LEffortShouldBeZero { get; set; } = "Cannot receive EVs.";
|
||||
public static string LEffortEXPIncreased { get; set; } = "All EVs are zero, but leveled above Met Level.";
|
||||
public static string LEffortUntrainedCap { get; set; } = "Individual EV without changing EXP cannot be greater than {0}.";
|
||||
|
||||
public static string LEvoInvalid { get; set; } = "Evolution not valid (or level/trade evolution unsatisfied).";
|
||||
public static string LEvoTradeReq { get; set; } = "In-game trade {0} should have evolved into {1}.";
|
||||
public static string LEvoTradeReqOutsider { get; set; } = "Outsider {0} should have evolved into {1}.";
|
||||
public static string LEvoTradeRequired { get; set; } = "Version Specific evolution requires a trade to opposite version. A Handling Trainer is required.";
|
||||
|
||||
public static string LFateful { get; set; } = "Special In-game Fateful Encounter.";
|
||||
public static string LFatefulGiftMissing { get; set; } = "Fateful Encounter with no matching Encounter. Has the Mystery Gift data been contributed?";
|
||||
public static string LFatefulInvalid { get; set; } = "Fateful Encounter should not be checked.";
|
||||
public static string LFatefulMissing { get; set; } = "Special In-game Fateful Encounter flag missing.";
|
||||
public static string LFatefulMystery { get; set; } = "Mystery Gift Fateful Encounter.";
|
||||
public static string LFatefulMysteryMissing { get; set; } = "Mystery Gift Fateful Encounter flag missing.";
|
||||
|
||||
public static string LFavoriteMarkingUnavailable { get; set; } = "Favorite Marking is not available.";
|
||||
|
||||
public static string LFormArgumentHigh { get; set; } = "Form argument is too high for current form.";
|
||||
public static string LFormArgumentLow { get; set; } = "Form argument is too low for current form.";
|
||||
public static string LFormArgumentNotAllowed { get; set; } = "Form argument is not allowed for this encounter.";
|
||||
public static string LFormArgumentValid { get; set; } = "Form argument is valid.";
|
||||
public static string LFormArgumentInvalid { get; set; } = "Form argument is not valid.";
|
||||
public static string LFormBattle { get; set; } = "Form cannot exist outside of a battle.";
|
||||
public static string LFormEternal { get; set; } = "Valid Eternal Flower encounter.";
|
||||
public static string LFormEternalInvalid { get; set; } = "Invalid Eternal Flower encounter.";
|
||||
public static string LFormInvalidGame { get; set; } = "Form cannot be obtained in origin game.";
|
||||
public static string LFormInvalidNature { get; set; } = "Form cannot have this nature.";
|
||||
public static string LFormInvalidRange { get; set; } = "Form Count is out of range. Expected <= {0}, got {1}.";
|
||||
public static string LFormItem { get; set; } = "Held item matches Form.";
|
||||
public static string LFormItemInvalid { get; set; } = "Held item does not match Form.";
|
||||
public static string LFormParty { get; set; } = "Form cannot exist outside of Party.";
|
||||
public static string LFormPikachuCosplay { get; set; } = "Only Cosplay Pikachu can have this form.";
|
||||
public static string LFormPikachuCosplayInvalid { get; set; } = "Cosplay Pikachu cannot have the default form.";
|
||||
public static string LFormPikachuEventInvalid { get; set; } = "Event Pikachu cannot have the default form.";
|
||||
public static string LFormInvalidExpect_0 { get; set; } = "Form is invalid, expected form index {0}.";
|
||||
public static string LFormValid { get; set; } = "Form is Valid.";
|
||||
public static string LFormVivillon { get; set; } = "Valid Vivillon pattern.";
|
||||
public static string LFormVivillonEventPre { get; set; } = "Event Vivillon pattern on pre-evolution.";
|
||||
public static string LFormVivillonInvalid { get; set; } = "Invalid Vivillon pattern.";
|
||||
public static string LFormVivillonNonNative { get; set; } = "Non-native Vivillon pattern.";
|
||||
|
||||
public static string LG1CatchRateChain { get; set; } = "Catch rate does not match any species from Pokémon evolution chain.";
|
||||
public static string LG1CatchRateEvo { get; set; } = "Catch rate match species without encounters. Expected a preevolution catch rate.";
|
||||
public static string LG1CatchRateItem { get; set; } = "Catch rate does not match a valid held item from Generation 2.";
|
||||
public static string LG1CatchRateMatchPrevious { get; set; } = "Catch Rate matches a species from Pokémon evolution chain.";
|
||||
public static string LG1CatchRateMatchTradeback { get; set; } = "Catch rate matches a valid held item from Generation 2.";
|
||||
public static string LG1CatchRateNone { get; set; } = "Catch rate does not match any species from Pokémon evolution chain or any Generation 2 held items.";
|
||||
public static string LG1CharNick { get; set; } = "Nickname from Generation 1/2 uses unavailable characters.";
|
||||
public static string LG1CharOT { get; set; } = "OT from Generation 1/2 uses unavailable characters.";
|
||||
public static string LG1GBEncounter { get; set; } = "Can't obtain Special encounter in Virtual Console games.";
|
||||
public static string LG1MoveExclusive { get; set; } = "Generation 1 exclusive move. Incompatible with Non-tradeback moves.";
|
||||
public static string LG1MoveLearnSameLevel { get; set; } = "Incompatible moves. Learned at the same level in Red/Blue and Yellow.";
|
||||
public static string LG1MoveTradeback { get; set; } = "Non-tradeback Egg move. Incompatible with Generation 1 exclusive moves.";
|
||||
public static string LG1OTEvent { get; set; } = "Incorrect RBY event OT Name.";
|
||||
public static string LG1OTGender { get; set; } = "Female OT from Generation 1/2 is invalid.";
|
||||
public static string LG1Stadium { get; set; } = "Incorrect Stadium OT.";
|
||||
public static string LG1StadiumInternational { get; set; } = "Valid International Stadium OT.";
|
||||
public static string LG1StadiumJapanese { get; set; } = "Valid Japanese Stadium OT.";
|
||||
public static string LG1TradebackPreEvoMove { get; set; } = "Non-tradeback pre evolution move. Incompatible with Generation 1 exclusive moves.";
|
||||
public static string LG1Type1Fail { get; set; } = "Invalid Type A, does not match species type.";
|
||||
public static string LG1Type2Fail { get; set; } = "Invalid Type B, does not match species type.";
|
||||
public static string LG1TypeMatch1 { get; set; } = "Valid Type A, matches species type.";
|
||||
public static string LG1TypeMatch2 { get; set; } = "Valid Type B, matches species type.";
|
||||
public static string LG1TypeMatchPorygon { get; set; } = "Porygon with valid Type A and B values.";
|
||||
public static string LG1TypePorygonFail { get; set; } = "Porygon with invalid Type A and B values. Does not a match a valid type combination.";
|
||||
public static string LG1TypePorygonFail1 { get; set; } = "Porygon with invalid Type A value.";
|
||||
public static string LG1TypePorygonFail2 { get; set; } = "Porygon with invalid Type B value.";
|
||||
public static string LG2InvalidTilePark { get; set; } = "National Park fishing encounter. Unreachable Water tiles.";
|
||||
public static string LG2InvalidTileR14 { get; set; } = "Kanto Route 14 fishing encounter. Unreachable Water tiles.";
|
||||
public static string LG2InvalidTileSafari { get; set; } = "Generation 2 Safari Zone fishing encounter. Unreachable zone.";
|
||||
public static string LG2InvalidTileTreeID { get; set; } = "Found an unreachable tree for Crystal headbutt encounter that matches OTID.";
|
||||
public static string LG2InvalidTileTreeNotFound { get; set; } = "Could not find a tree for Crystal headbutt encounter that matches OTID.";
|
||||
public static string LG2TreeID { get; set; } = "Found a tree for Crystal headbutt encounter that matches OTID.";
|
||||
public static string LG2OTGender { get; set; } = "OT from Virtual Console games other than Crystal cannot be female.";
|
||||
|
||||
public static string LG3EReader { get; set; } = "Non Japanese Shadow E-reader Pokémon. Unreleased encounter.";
|
||||
public static string LG3OTGender { get; set; } = "OT from Colosseum/XD cannot be female.";
|
||||
public static string LG4InvalidTileR45Surf { get; set; } = "Johto Route 45 surfing encounter. Unreachable Water tiles.";
|
||||
public static string LG5ID_N { get; set; } = "The Name/TID16/SID16 of N is incorrect.";
|
||||
public static string LG5IVAll30 { get; set; } = "All IVs of N's Pokémon should be 30.";
|
||||
public static string LG5OTGenderN { get; set; } = "N's Pokémon must have a male OT gender.";
|
||||
public static string LG5PIDShinyGrotto { get; set; } = "Hidden Grotto captures cannot be shiny.";
|
||||
public static string LG5PIDShinyN { get; set; } = "N's Pokémon cannot be shiny.";
|
||||
public static string LG5SparkleInvalid { get; set; } = "Special In-game N's Sparkle flag should not be checked.";
|
||||
public static string LG5SparkleRequired { get; set; } = "Special In-game N's Sparkle flag missing.";
|
||||
|
||||
public static string LGanbaruStatTooHigh { get; set; } = "One or more Ganbaru Value is above the natural limit of (10 - IV bonus).";
|
||||
|
||||
public static string LGenderInvalidNone { get; set; } = "Genderless Pokémon should not have a gender.";
|
||||
public static string LGeoBadOrder { get; set; } = "GeoLocation Memory: Gap/Blank present.";
|
||||
public static string LGeoHardwareInvalid { get; set; } = "Geolocation: Country is not in 3DS region.";
|
||||
public static string LGeoHardwareRange { get; set; } = "Invalid Console Region.";
|
||||
public static string LGeoHardwareValid { get; set; } = "Geolocation: Country is in 3DS region.";
|
||||
public static string LGeoMemoryMissing { get; set; } = "GeoLocation Memory: Memories should be present.";
|
||||
public static string LGeoNoCountryHT { get; set; } = "GeoLocation Memory: HT Name present but has no previous Country.";
|
||||
public static string LGeoNoRegion { get; set; } = "GeoLocation Memory: Region without Country.";
|
||||
|
||||
public static string LHyperTooLow_0 { get; set; } = "Can't Hyper Train a Pokémon that isn't level {0}.";
|
||||
public static string LHyperPerfectAll { get; set; } = "Can't Hyper Train a Pokémon with perfect IVs.";
|
||||
public static string LHyperPerfectOne { get; set; } = "Can't Hyper Train a perfect IV.";
|
||||
public static string LHyperPerfectUnavailable { get; set; } = "Can't Hyper Train any IV(s).";
|
||||
|
||||
public static string LItemEgg { get; set; } = "Eggs cannot hold items.";
|
||||
public static string LItemUnreleased { get; set; } = "Held item is unreleased.";
|
||||
|
||||
public static string LIVAllEqual_0 { get; set; } = "All IVs are {0}.";
|
||||
public static string LIVNotCorrect { get; set; } = "IVs do not match encounter requirements.";
|
||||
public static string LIVF_COUNT0_31 { get; set; } = "Should have at least {0} IVs = 31.";
|
||||
|
||||
public static string LLevelEXPThreshold { get; set; } = "Current experience matches level threshold.";
|
||||
public static string LLevelEXPTooHigh { get; set; } = "Current experience exceeds maximum amount for level 100.";
|
||||
public static string LLevelMetBelow { get; set; } = "Current level is below met level.";
|
||||
public static string LLevelMetGift { get; set; } = "Met Level does not match Mystery Gift level.";
|
||||
public static string LLevelMetGiftFail { get; set; } = "Current Level below Mystery Gift level.";
|
||||
public static string LLevelMetSane { get; set; } = "Current level is not below met level.";
|
||||
|
||||
public static string LMarkValueOutOfRange_0 { get; set; } = "Individual marking at index {0} is not within the allowed value range.";
|
||||
public static string LMarkValueShouldBeZero { get; set; } = "Marking flags cannot be set.";
|
||||
public static string LMarkValueUnusedBitsPresent { get; set; } = "Marking flags uses bits beyond the accessible range.";
|
||||
|
||||
public static string LMemoryArgBadCatch { get; set; } = "{0} Memory: {0} did not catch this.";
|
||||
public static string LMemoryArgBadHatch { get; set; } = "{0} Memory: {0} did not hatch this.";
|
||||
public static string LMemoryArgBadHT { get; set; } = "Memory: Can't have Handling Trainer Memory as Egg.";
|
||||
public static string LMemoryArgBadID { get; set; } = "{0} Memory: Can't obtain Memory on {0} Version.";
|
||||
public static string LMemoryArgBadItem { get; set; } = "{0} Memory: Species can't hold this item.";
|
||||
public static string LMemoryArgBadLocation { get; set; } = "{0} Memory: Can't obtain Location on {0} Version.";
|
||||
public static string LMemoryArgBadMove { get; set; } = "{0} Memory: Species can't learn this move.";
|
||||
public static string LMemoryArgBadOTEgg { get; set; } = "{0} Memory: Link Trade is not a valid first memory.";
|
||||
public static string LMemoryArgBadSpecies { get; set; } = "{0} Memory: Can't capture species in game.";
|
||||
public static string LMemoryArgSpecies { get; set; } = "{0} Memory: Species can be captured in game.";
|
||||
public static string LMemoryCleared { get; set; } = "Memory: Not cleared properly.";
|
||||
public static string LMemoryF_0_Valid { get; set; } = "{0} Memory is valid.";
|
||||
public static string LMemoryFeelInvalid { get; set; } = "{0} Memory: Invalid Feeling.";
|
||||
public static string LMemoryHTFlagInvalid { get; set; } = "Untraded: Current handler should not be the Handling Trainer.";
|
||||
public static string LMemoryHTGender { get; set; } = "HT Gender invalid: {0}";
|
||||
public static string LMemoryHTLanguage { get; set; } = "HT Language is missing.";
|
||||
|
||||
public static string LMemoryIndexArgHT { get; set; } = "Should have a HT Memory TextVar value (somewhere).";
|
||||
public static string LMemoryIndexFeel { get; set; } = "{0} Memory: Feeling should be index {1}.";
|
||||
public static string LMemoryIndexFeelHT09 { get; set; } = "Should have a HT Memory Feeling value 0-9.";
|
||||
public static string LMemoryIndexID { get; set; } = "{0} Memory: Should be index {1}.";
|
||||
public static string LMemoryIndexIntensity { get; set; } = "{0} Memory: Intensity should be index {1}.";
|
||||
public static string LMemoryIndexIntensityHT1 { get; set; } = "Should have a HT Memory Intensity value (1st).";
|
||||
public static string LMemoryIndexIntensityMin { get; set; } = "{0} Memory: Intensity should be at least {1}.";
|
||||
public static string LMemoryIndexLinkHT { get; set; } = "Should have a Link Trade HT Memory.";
|
||||
public static string LMemoryIndexVar { get; set; } = "{0} Memory: TextVar should be index {1}.";
|
||||
public static string LMemoryMissingHT { get; set; } = "Memory: Handling Trainer Memory missing.";
|
||||
public static string LMemoryMissingOT { get; set; } = "Memory: Original Trainer Memory missing.";
|
||||
|
||||
public static string LMemorySocialZero { get; set; } = "Social Stat should be zero.";
|
||||
public static string LMemorySocialTooHigh_0 { get; set; } = "Social Stat should be <= {0}";
|
||||
|
||||
public static string LMemoryStatAffectionHT0 { get; set; } = "Untraded: Handling Trainer Affection should be 0.";
|
||||
public static string LMemoryStatAffectionOT0 { get; set; } = "OT Affection should be 0.";
|
||||
public static string LMemoryStatFriendshipHT0 { get; set; } = "Untraded: Handling Trainer Friendship should be 0.";
|
||||
public static string LMemoryStatFriendshipOTBaseEvent { get; set; } = "Event OT Friendship does not match base friendship.";
|
||||
|
||||
public static string LMetDetailTimeOfDay { get; set; } = "Met Time of Day value is not within the expected range.";
|
||||
|
||||
public static string LMemoryStatFullness { get; set; } = "Fullness should be {0}.";
|
||||
public static string LMemoryStatEnjoyment { get; set; } = "Enjoyment should be {0}.";
|
||||
|
||||
public static string LMoveEggFIncompatible0_1 { get; set; } = "{0} Inherited Move. Incompatible with {1} inherited moves.";
|
||||
public static string LMoveEggIncompatible { get; set; } = "Egg Move. Incompatible with event Egg moves.";
|
||||
public static string LMoveEggIncompatibleEvent { get; set; } = "Event Egg Move. Incompatible with normal Egg moves.";
|
||||
public static string LMoveEggInherited { get; set; } = "Inherited Egg move.";
|
||||
public static string LMoveEggInheritedTutor { get; set; } = "Inherited tutor move.";
|
||||
public static string LMoveEggInvalid { get; set; } = "Not an expected Egg move.";
|
||||
public static string LMoveEggInvalidEvent { get; set; } = "Egg Move. Not expected in an event Egg.";
|
||||
public static string LMoveEggInvalidEventLevelUp { get; set; } = "Inherited move learned by Level-up. Not expected in an event Egg.";
|
||||
public static string LMoveEggInvalidEventLevelUpGift { get; set; } = "Inherited move learned by Level-up. Not expected in a gift Egg.";
|
||||
public static string LMoveEggInvalidEventTMHM { get; set; } = "Inherited TM/HM move. Not expected in an event Egg.";
|
||||
public static string LMoveEggInvalidEventTutor { get; set; } = "Inherited tutor move. Not expected in an event Egg.";
|
||||
public static string LMoveEggLevelUp { get; set; } = "Inherited move learned by Level-up.";
|
||||
public static string LMoveEggMissing { get; set; } = "Event Egg move missing.";
|
||||
public static string LMoveEggMoveGift { get; set; } = "Egg Move. Not expected in a gift Egg.";
|
||||
public static string LMoveEggTMHM { get; set; } = "Inherited TM/HM move.";
|
||||
|
||||
public static string LMoveEventEggLevelUp { get; set; } = "Inherited move learned by Level-up. Incompatible with event Egg moves.";
|
||||
public static string LMoveEvoFCombination_0 { get; set; } = "Moves combinations is not compatible with {0} evolution.";
|
||||
public static string LMoveEvoFHigher { get; set; } = "Incompatible evolution moves. {1} Move learned at a higher level than other {0} moves.";
|
||||
public static string LMoveEvoFLower { get; set; } = "Incompatible evolution moves. {0} Move learned at a lower level than other {1} moves.";
|
||||
public static string LMoveFDefault_0 { get; set; } = "Default move in Generation {0}.";
|
||||
public static string LMoveFExpect_0 { get; set; } = "Expected the following Moves: {0}";
|
||||
public static string LMoveFExpectSingle_0 { get; set; } = "Expected: {0}";
|
||||
public static string LMoveFLevelUp_0 { get; set; } = "Learned by Level-up in Generation {0}.";
|
||||
public static string LMoveFTMHM_0 { get; set; } = "Learned by TM/HM in Generation {0}.";
|
||||
public static string LMoveFTutor_0 { get; set; } = "Learned by Move Tutor in Generation {0}.";
|
||||
public static string LMoveKeldeoMismatch { get; set; } = "Keldeo Move/Form mismatch.";
|
||||
public static string LMoveNincada { get; set; } = "Only one Ninjask move allowed.";
|
||||
public static string LMoveNincadaEvo { get; set; } = "Learned by evolving Nincada into Ninjask.";
|
||||
public static string LMoveNincadaEvoF_0 { get; set; } = "Learned by evolving Nincada into Ninjask in Generation {0}.";
|
||||
public static string LMovePPExpectHealed_0 { get; set; } = "Move {0} PP is below the amount expected.";
|
||||
public static string LMovePPTooHigh_0 { get; set; } = "Move {0} PP is above the amount allowed.";
|
||||
public static string LMovePPUpsTooHigh_0 { get; set; } = "Move {0} PP Ups is above the amount allowed.";
|
||||
public static string LMoveSourceShared { get; set; } = "Shared Non-Relearn Move.";
|
||||
public static string LMoveSourceSharedF { get; set; } = "Shared Non-Relearn Move in Generation {0}.";
|
||||
|
||||
public static string LMoveRelearnDexNav { get; set; } = "Not an expected DexNav move.";
|
||||
public static string LMoveRelearnUnderground { get; set; } = "Not an expected Underground egg move.";
|
||||
public static string LMoveRelearnEgg { get; set; } = "Base Egg move.";
|
||||
public static string LMoveRelearnEggMissing { get; set; } = "Base Egg move missing.";
|
||||
public static string LMoveRelearnFExpect_0 { get; set; } = "Expected the following Relearn Moves: {0} ({1})";
|
||||
public static string LMoveRelearnFMiss_0 { get; set; } = "Relearn Moves missing: {0}";
|
||||
public static string LMoveRelearnInvalid { get; set; } = "Not an expected Relearnable move.";
|
||||
public static string LMoveRelearnNone { get; set; } = "Expected no Relearn Move in slot.";
|
||||
|
||||
public static string LMoveShopAlphaMoveShouldBeMastered { get; set; } = "Alpha Move should be marked as mastered.";
|
||||
public static string LMoveShopAlphaMoveShouldBeOther { get; set; } = "Alpha encounter cannot be found with this Alpha Move.";
|
||||
public static string LMoveShopAlphaMoveShouldBeZero { get; set; } = "Only Alphas may have an Alpha Move set.";
|
||||
public static string LMoveShopMasterInvalid_0 { get; set; } = "Cannot manually master {0}: not permitted to master.";
|
||||
public static string LMoveShopMasterNotLearned_0 { get; set; } = "Cannot manually master {0}: not in possible learned level up moves.";
|
||||
public static string LMoveShopPurchaseInvalid_0 { get; set; } = "Cannot purchase {0} from the move shop.";
|
||||
|
||||
public static string LMoveSourceDefault { get; set; } = "Default move.";
|
||||
public static string LMoveSourceDuplicate { get; set; } = "Duplicate Move.";
|
||||
public static string LMoveSourceEgg { get; set; } = "Egg Move.";
|
||||
public static string LMoveSourceEggEvent { get; set; } = "Event Egg Move.";
|
||||
public static string LMoveSourceEmpty { get; set; } = "Empty Move.";
|
||||
public static string LMoveSourceInvalid { get; set; } = "Invalid Move.";
|
||||
public static string LMoveSourceInvalidSketch { get; set; } = "Invalid Move (Sketch).";
|
||||
public static string LMoveSourceLevelUp { get; set; } = "Learned by Level-up.";
|
||||
public static string LMoveSourceRelearn { get; set; } = "Relearnable Move.";
|
||||
public static string LMoveSourceSpecial { get; set; } = "Special Non-Relearn Move.";
|
||||
public static string LMoveSourceTMHM { get; set; } = "Learned by TM/HM.";
|
||||
public static string LMoveSourceTutor { get; set; } = "Learned by Move Tutor.";
|
||||
public static string LMoveSourceTR { get; set; } = "Unexpected Technical Record Learned flag: {0}";
|
||||
|
||||
public static string LNickFlagEggNo { get; set; } = "Egg must be not nicknamed.";
|
||||
public static string LNickFlagEggYes { get; set; } = "Egg must be nicknamed.";
|
||||
public static string LNickInvalidChar { get; set; } = "Cannot be given this Nickname.";
|
||||
public static string LNickLengthLong { get; set; } = "Nickname too long.";
|
||||
public static string LNickLengthShort { get; set; } = "Nickname is empty.";
|
||||
public static string LNickMatchLanguage { get; set; } = "Nickname matches species name.";
|
||||
public static string LNickMatchLanguageEgg { get; set; } = "Egg matches language Egg name.";
|
||||
public static string LNickMatchLanguageEggFail { get; set; } = "Egg name does not match language Egg name.";
|
||||
public static string LNickMatchLanguageFail { get; set; } = "Nickname does not match species name.";
|
||||
public static string LNickMatchLanguageFlag { get; set; } = "Nickname flagged, matches species name.";
|
||||
public static string LNickMatchNoOthers { get; set; } = "Nickname does not match another species name.";
|
||||
public static string LNickMatchNoOthersFail { get; set; } = "Nickname matches another species name (+language).";
|
||||
|
||||
public static string LOTLanguage { get; set; } = "Language ID should be {0}, not {1}.";
|
||||
public static string LOTLong { get; set; } = "OT Name too long.";
|
||||
public static string LOTShort { get; set; } = "OT Name too short.";
|
||||
public static string LOTSuspicious { get; set; } = "Suspicious Original Trainer details.";
|
||||
|
||||
public static string LOT_IDEqual { get; set; } = "TID16 and SID16 are equal.";
|
||||
public static string LOT_IDs0 { get; set; } = "TID16 and SID16 are 0.";
|
||||
public static string LOT_SID0 { get; set; } = "SID16 is zero.";
|
||||
public static string LOT_SID0Invalid { get; set; } = "SID16 should be 0.";
|
||||
public static string LOT_TID0 { get; set; } = "TID16 is zero.";
|
||||
public static string LOT_IDInvalid { get; set; } = "TID16 and SID16 combination is not possible.";
|
||||
|
||||
public static string LPIDEncryptWurmple { get; set; } = "Wurmple evolution Encryption Constant mismatch.";
|
||||
public static string LPIDEncryptZero { get; set; } = "Encryption Constant is not set.";
|
||||
public static string LPIDEqualsEC { get; set; } = "Encryption Constant matches PID.";
|
||||
public static string LPIDGenderMatch { get; set; } = "Gender matches PID.";
|
||||
public static string LPIDGenderMismatch { get; set; } = "PID-Gender mismatch.";
|
||||
public static string LPIDNatureMatch { get; set; } = "Nature matches PID.";
|
||||
public static string LPIDNatureMismatch { get; set; } = "PID-Nature mismatch.";
|
||||
public static string LPIDTypeMismatch { get; set; } = "PID+ correlation does not match what was expected for the Encounter's type.";
|
||||
public static string LPIDZero { get; set; } = "PID is not set.";
|
||||
|
||||
public static string LPokerusDaysTooHigh_0 { get; set; } = "Pokérus Days Remaining value is too high; expected <= {0}.";
|
||||
public static string LPokerusStrainUnobtainable_0 { get; set; } = "Pokérus Strain {0} cannot be obtained.";
|
||||
|
||||
public static string LRibbonAllValid { get; set; } = "All ribbons accounted for.";
|
||||
public static string LRibbonEgg { get; set; } = "Can't receive Ribbon(s) as an Egg.";
|
||||
public static string LRibbonFInvalid_0 { get; set; } = "Invalid Ribbons: ";
|
||||
public static string LRibbonFMissing_0 { get; set; } = "Missing Ribbons: ";
|
||||
public static string LRibbonMarkingFInvalid_0 { get; set; } = "Invalid Marking: {0}";
|
||||
public static string LRibbonMarkingAffixedF_0 { get; set; } = "Invalid Affixed Ribbon/Marking: {0}";
|
||||
|
||||
public static string LStatDynamaxInvalid { get; set; } = "Dynamax Level is not within the expected range.";
|
||||
public static string LStatIncorrectHeight { get; set; } = "Calculated Height does not match stored value.";
|
||||
public static string LStatIncorrectHeightCopy { get; set; } = "Copy Height does not match the original value.";
|
||||
public static string LStatIncorrectHeightValue { get; set; } = "Height does not match the expected value.";
|
||||
public static string LStatIncorrectWeight { get; set; } = "Calculated Weight does not match stored value.";
|
||||
public static string LStatIncorrectWeightValue { get; set; } = "Weight does not match the expected value.";
|
||||
public static string LStatInvalidHeightWeight { get; set; } = "Height / Weight values are statistically improbable.";
|
||||
public static string LStatIncorrectCP { get; set; } = "Calculated CP does not match stored value.";
|
||||
public static string LStatGigantamaxInvalid { get; set; } = "Gigantamax Flag mismatch.";
|
||||
public static string LStatGigantamaxValid { get; set; } = "Gigantamax Flag was changed via Max Soup.";
|
||||
public static string LStatNatureInvalid { get; set; } = "Stat Nature is not within the expected range.";
|
||||
public static string LStatBattleVersionInvalid { get; set; } = "Battle Version is not within the expected range.";
|
||||
public static string LStatNobleInvalid { get; set; } = "Noble Flag mismatch.";
|
||||
public static string LStatAlphaInvalid { get; set; } = "Alpha Flag mismatch.";
|
||||
|
||||
public static string LStoredSourceEgg { get; set; } = "Egg must be in Box or Party.";
|
||||
public static string LStoredSourceInvalid_0 { get; set; } = "Invalid Stored Source: {0}";
|
||||
|
||||
public static string LSuperComplete { get; set; } = "Super Training complete flag mismatch.";
|
||||
public static string LSuperDistro { get; set; } = "Distribution Super Training missions are not released.";
|
||||
public static string LSuperEgg { get; set; } = "Can't Super Train an Egg.";
|
||||
public static string LSuperNoComplete { get; set; } = "Can't have active Super Training complete flag for origins.";
|
||||
public static string LSuperNoUnlocked { get; set; } = "Can't have active Super Training unlocked flag for origins.";
|
||||
public static string LSuperUnavailable { get; set; } = "Super Training missions are not available in games visited.";
|
||||
public static string LSuperUnused { get; set; } = "Unused Super Training Flag is flagged.";
|
||||
|
||||
public static string LTeraTypeIncorrect { get; set; } = "Tera Type does not match the expected value.";
|
||||
public static string LTeraTypeMismatch { get; set; } = "Tera Type does not match either of the default types.";
|
||||
|
||||
public static string LTradeNotAvailable { get; set; } = "Encounter cannot be traded to the active trainer.";
|
||||
|
||||
public static string LTrainerIDNoSeed { get; set; } = "Trainer ID is not obtainable from any RNG seed.";
|
||||
|
||||
public static string LTransferBad { get; set; } = "Incorrectly transferred from previous generation.";
|
||||
|
||||
public static string LTransferCurrentHandlerInvalid { get; set; } = "Invalid Current handler value, trainer details for save file expected another value.";
|
||||
public static string LTransferEgg { get; set; } = "Can't transfer Eggs between Generations.";
|
||||
public static string LTransferEggLocationTransporter { get; set; } = "Invalid Met Location, expected Poké Transfer.";
|
||||
public static string LTransferEggMetLevel { get; set; } = "Invalid Met Level for transfer.";
|
||||
public static string LTransferEggVersion { get; set; } = "Can't transfer Eggs to this game.";
|
||||
public static string LTransferFlagIllegal { get; set; } = "Flagged as illegal by the game (glitch abuse).";
|
||||
public static string LTransferHTFlagRequired { get; set; } = "Current handler cannot be the OT.";
|
||||
public static string LTransferHTMismatchName { get; set; } = "Handling trainer does not match the expected trainer name.";
|
||||
public static string LTransferHTMismatchGender { get; set; } = "Handling trainer does not match the expected trainer gender.";
|
||||
public static string LTransferHTMismatchLanguage { get; set; } = "Handling trainer does not match the expected trainer language.";
|
||||
public static string LTransferMet { get; set; } = "Invalid Met Location, expected Poké Transfer or Crown.";
|
||||
public static string LTransferNotPossible { get; set; } = "Unable to transfer into current format from origin format.";
|
||||
public static string LTransferMetLocation { get; set; } = "Invalid Transfer Met Location.";
|
||||
public static string LTransferMove { get; set; } = "Incompatible transfer move.";
|
||||
public static string LTransferMoveG4HM { get; set; } = "Defog and Whirlpool. One of the two moves should have been removed before transferred to Generation 5.";
|
||||
public static string LTransferMoveHM { get; set; } = "Generation {0} HM. Should have been removed before transferred to Generation {1}.";
|
||||
public static string LTransferNature { get; set; } = "Invalid Nature for transfer Experience.";
|
||||
public static string LTransferObedienceLevel { get; set; } = "Invalid Obedience Level.";
|
||||
public static string LTransferOriginFInvalid0_1 { get; set; } = "{0} origin cannot exist in the currently loaded ({1}) save file.";
|
||||
public static string LTransferPIDECBitFlip { get; set; } = "PID should be equal to EC [with top bit flipped]!";
|
||||
public static string LTransferPIDECEquals { get; set; } = "PID should be equal to EC!";
|
||||
public static string LTransferPIDECXor { get; set; } = "Encryption Constant matches shinyxored PID.";
|
||||
public static string LTransferTrackerMissing { get; set; } = "Pokémon HOME Transfer Tracker is missing.";
|
||||
public static string LTransferTrackerShouldBeZero { get; set; } = "Pokémon HOME Transfer Tracker should be 0.";
|
||||
|
||||
public static string LTrashBytesExpected_0 { get; set; } = "Expected Trash Bytes: {0}";
|
||||
public static string LTrashBytesExpected { get; set; } = "Expected Trash Bytes.";
|
||||
public static string LTrashBytesMismatchInitial { get; set; } = "Expected initial trash bytes to match the encounter.";
|
||||
public static string LTrashBytesMissingTerminator { get; set; } = "Final terminator missing.";
|
||||
public static string LTrashBytesShouldBeEmpty { get; set; } = "Trash Bytes should be cleared.";
|
||||
public static string LTrashBytesUnexpected { get; set; } = "Unexpected Trash Bytes.";
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -11,59 +10,70 @@ namespace PKHeX.Core;
|
||||
/// </summary>
|
||||
public static class LegalityFormatting
|
||||
{
|
||||
public static ILegalityFormatter Formatter { private get; set; } = new BaseLegalityFormatter();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a report message with optional verbosity for in-depth analysis.
|
||||
/// </summary>
|
||||
/// <param name="la">Legality result to format</param>
|
||||
/// <param name="verbose">Include all details in the parse, including valid check messages.</param>
|
||||
/// <returns>Single line string</returns>
|
||||
public static string Report(this LegalityAnalysis la, bool verbose = false) => verbose ? GetVerboseLegalityReport(la) : GetLegalityReport(la);
|
||||
|
||||
public static ILegalityFormatter Formatter { private get; set; } = new BaseLegalityFormatter();
|
||||
|
||||
public static string GetLegalityReport(LegalityAnalysis la) => Formatter.GetReport(la);
|
||||
public static string GetVerboseLegalityReport(LegalityAnalysis la) => Formatter.GetReportVerbose(la);
|
||||
|
||||
public static void AddSecondaryChecksValid(IEnumerable<CheckResult> results, List<string> lines)
|
||||
public static string Report(this LegalityAnalysis la, bool verbose = false)
|
||||
{
|
||||
var outputLines = results
|
||||
.Where(chk => chk.Valid && chk.Comment != L_AValid)
|
||||
.OrderBy(chk => chk.Judgement) // Fishy sorted to top
|
||||
.Select(chk => chk.Format(L_F0_1));
|
||||
lines.AddRange(outputLines);
|
||||
var localizer = LegalityLocalizationContext.Create(la);
|
||||
return Report(localizer, verbose);
|
||||
}
|
||||
|
||||
public static void AddSecondaryChecksInvalid(IReadOnlyList<CheckResult> results, List<string> lines)
|
||||
/// <inheritdoc cref="Report(LegalityAnalysis, bool)"/>
|
||||
public static string Report(this LegalityLocalizationContext localizer, bool verbose) => verbose ? GetVerboseLegalityReport(localizer) : GetLegalityReport(localizer);
|
||||
|
||||
/// <inheritdoc cref="Report(LegalityAnalysis, bool)"/>
|
||||
public static string Report(this LegalityAnalysis la, string language, bool verbose = false)
|
||||
{
|
||||
var localizer = LegalityLocalizationContext.Create(la, language);
|
||||
return localizer.Report(verbose);
|
||||
}
|
||||
|
||||
public static string GetLegalityReport(LegalityLocalizationContext la) => Formatter.GetReport(la);
|
||||
public static string GetVerboseLegalityReport(LegalityLocalizationContext la) => Formatter.GetReportVerbose(la);
|
||||
|
||||
public static void AddSecondaryChecksValid(LegalityLocalizationContext la, IEnumerable<CheckResult> results, List<string> lines)
|
||||
{
|
||||
var outputLines = results
|
||||
.Where(chk => chk.Valid && chk.IsNotGeneric())
|
||||
.OrderBy(chk => chk.Judgement); // Fishy sorted to top
|
||||
foreach (var chk in outputLines)
|
||||
lines.Add(la.Humanize(chk));
|
||||
}
|
||||
|
||||
public static void AddSecondaryChecksInvalid(LegalityLocalizationContext la, IReadOnlyList<CheckResult> results, List<string> lines)
|
||||
{
|
||||
foreach (var chk in results)
|
||||
{
|
||||
if (chk.Valid)
|
||||
continue;
|
||||
lines.Add(chk.Format(L_F0_1));
|
||||
lines.Add(la.Humanize(chk));
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddRelearn(ReadOnlySpan<MoveResult> relearn, List<string> lines, bool state, PKM pk, EvolutionHistory history)
|
||||
public static void AddRelearn(LegalityLocalizationContext la, ReadOnlySpan<MoveResult> relearn, List<string> lines, bool state)
|
||||
{
|
||||
for (int i = 0; i < relearn.Length; i++)
|
||||
{
|
||||
var move = relearn[i];
|
||||
if (move.Valid == state)
|
||||
lines.Add(move.Format(L_F0_RM_1_2, i + 1, pk, history));
|
||||
lines.Add(la.FormatRelearn(move, i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddMoves(ReadOnlySpan<MoveResult> moves, List<string> lines, in int currentFormat, bool state, PKM pk, EvolutionHistory history)
|
||||
public static void AddMoves(LegalityLocalizationContext la, ReadOnlySpan<MoveResult> moves, List<string> lines, in byte currentFormat, bool state)
|
||||
{
|
||||
for (int i = 0; i < moves.Length; i++)
|
||||
{
|
||||
var move = moves[i];
|
||||
if (move.Valid != state)
|
||||
continue;
|
||||
var msg = move.Format(L_F0_M_1_2, i + 1, pk, history);
|
||||
var gen = move.Generation;
|
||||
if (currentFormat != gen && gen != 0)
|
||||
msg += $" [Gen{gen}]";
|
||||
var msg = la.FormatMove(move, i + 1, currentFormat);
|
||||
lines.Add(msg);
|
||||
}
|
||||
}
|
||||
@@ -71,41 +81,44 @@ public static void AddMoves(ReadOnlySpan<MoveResult> moves, List<string> lines,
|
||||
/// <summary>
|
||||
/// Adds information about the <see cref="LegalityAnalysis.EncounterMatch"/> to the <see cref="lines"/>.
|
||||
/// </summary>
|
||||
public static void AddEncounterInfo(LegalityAnalysis la, List<string> lines)
|
||||
public static void AddEncounterInfo(LegalityLocalizationContext l, List<string> lines)
|
||||
{
|
||||
var la = l.Analysis;
|
||||
var enc = la.EncounterOriginal;
|
||||
|
||||
var display = l.Settings.Encounter;
|
||||
// Name
|
||||
lines.Add(string.Format(L_FEncounterType_0, enc.GetEncounterName()));
|
||||
lines.Add(string.Format(display.Format, display.EncounterType, enc.GetEncounterName(l.Strings.specieslist)));
|
||||
if (enc is MysteryGift g)
|
||||
lines.Add(g.CardHeader);
|
||||
|
||||
// Location
|
||||
var loc = enc.GetEncounterLocation();
|
||||
if (!string.IsNullOrEmpty(loc))
|
||||
lines.Add(string.Format(L_F0_1, L_XLocation, loc));
|
||||
lines.Add(string.Format(display.Format, display.Location, loc));
|
||||
|
||||
// Version
|
||||
if (enc.Generation <= 2)
|
||||
lines.Add(string.Format(L_F0_1, nameof(GameVersion), enc.Version));
|
||||
lines.Add(string.Format(display.Format, display.Version, enc.Version));
|
||||
|
||||
// PID/IV
|
||||
AddEncounterInfoPIDIV(la, lines);
|
||||
AddEncounterInfoPIDIV(l, lines);
|
||||
}
|
||||
|
||||
public static void AddEncounterInfoPIDIV(LegalityAnalysis la, List<string> lines)
|
||||
public static void AddEncounterInfoPIDIV(LegalityLocalizationContext l, List<string> lines)
|
||||
{
|
||||
var strings = l.Settings;
|
||||
var la = l.Analysis;
|
||||
var info = la.Info;
|
||||
if (!info.PIDParsed)
|
||||
info.PIDIV = MethodFinder.Analyze(la.Entity);
|
||||
AddEncounterInfoPIDIV(lines, info);
|
||||
AddEncounterInfoPIDIV(strings, lines, info);
|
||||
}
|
||||
|
||||
private static void AddEncounterInfoPIDIV(List<string> lines, LegalInfo info)
|
||||
private static void AddEncounterInfoPIDIV(LegalityLocalizationSet strings, List<string> lines, LegalInfo info)
|
||||
{
|
||||
var pidiv = info.PIDIV;
|
||||
var type = pidiv.Type;
|
||||
var msgType = string.Format(L_FPIDType_0, type);
|
||||
var msgType = string.Format(strings.Encounter.Format, strings.Encounter.PIDType, type);
|
||||
var enc = info.EncounterOriginal;
|
||||
if (enc is IRandomCorrelationEvent3 r3)
|
||||
{
|
||||
@@ -128,7 +141,7 @@ private static void AddEncounterInfoPIDIV(List<string> lines, LegalInfo info)
|
||||
{
|
||||
if (type is not PIDType.Pokewalker)
|
||||
return;
|
||||
var line = GetLinePokewalkerSeed(info);
|
||||
var line = GetLinePokewalkerSeed(info, strings);
|
||||
lines.Add(line);
|
||||
}
|
||||
else if (enc is PCD pcd)
|
||||
@@ -137,7 +150,7 @@ private static void AddEncounterInfoPIDIV(List<string> lines, LegalInfo info)
|
||||
if (gift is { HasPID: false }) // tick rand
|
||||
{
|
||||
var ticks = ARNG.Prev(info.Entity.EncryptionConstant);
|
||||
var line = string.Format(L_FOriginSeed_0, ticks.ToString("X8"));
|
||||
var line = string.Format(strings.Encounter.Format, strings.Encounter.OriginSeed, ticks.ToString("X8"));
|
||||
line += $" [{ticks / 524_288f:F2}]"; // seconds?
|
||||
lines.Add(line);
|
||||
}
|
||||
@@ -151,7 +164,7 @@ private static void AddEncounterInfoPIDIV(List<string> lines, LegalInfo info)
|
||||
var initial = ClassicEraRNG.SeekInitialSeedForIVs(ivs, (uint)date.Year, (uint)date.Month, (uint)date.Day, out var origin);
|
||||
var components = ClassicEraRNG.DecomposeSeed(initial, (uint)date.Year, (uint)date.Month, (uint)date.Day);
|
||||
|
||||
AppendInitialDateTime4(lines, initial, origin, components);
|
||||
AppendInitialDateTime4(lines, initial, origin, components, strings.Encounter);
|
||||
if (components.IsInvalid())
|
||||
lines.Add("INVALID");
|
||||
}
|
||||
@@ -160,13 +173,13 @@ private static void AddEncounterInfoPIDIV(List<string> lines, LegalInfo info)
|
||||
{
|
||||
if (Daycare3.TryGetOriginSeed(info.Entity, out var day3))
|
||||
{
|
||||
var line = string.Format(L_FOriginSeed_0, day3.Origin.ToString("X8"));
|
||||
var line = string.Format(strings.Encounter.Format, strings.Encounter.OriginSeed, day3.Origin.ToString("X8"));
|
||||
lines.Add(line);
|
||||
|
||||
lines.Add($"Initial: 0x{day3.Initial:X8}, Frame: {day3.Advances + 1}"); // frames are 1-indexed
|
||||
lines.Add(string.Format(strings.Encounter.FrameInitial, day3.Initial.ToString("X8"), day3.Advances + 1)); // frames are 1-indexed
|
||||
var sb = new StringBuilder();
|
||||
AppendFrameTimeStamp3(day3.Advances, sb);
|
||||
lines.Add($"Time: {sb}");
|
||||
AppendFrameTimeStamp3(day3.Advances, sb, strings.Encounter);
|
||||
lines.Add(string.Format(strings.Encounter.Format, strings.Encounter.Time, sb));
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -174,43 +187,43 @@ private static void AddEncounterInfoPIDIV(List<string> lines, LegalInfo info)
|
||||
|
||||
if (pidiv.IsSeed64())
|
||||
{
|
||||
var line = string.Format(L_FOriginSeed_0, pidiv.Seed64.ToString("X16"));
|
||||
var line = string.Format(strings.Encounter.Format, strings.Encounter.OriginSeed, pidiv.Seed64.ToString("X16"));
|
||||
lines.Add(line);
|
||||
return;
|
||||
}
|
||||
if (enc is IEncounterSlot34 s)
|
||||
{
|
||||
var line = GetLineSlot34(info, pidiv, s);
|
||||
var line = GetLineSlot34(info, strings, pidiv, s);
|
||||
lines.Add(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
var seed = pidiv.OriginSeed;
|
||||
var line = string.Format(L_FOriginSeed_0, seed.ToString("X8"));
|
||||
var line = string.Format(strings.Encounter.Format, strings.Encounter.OriginSeed, seed.ToString("X8"));
|
||||
if (pidiv.Mutated is not 0 && pidiv.OriginSeed != pidiv.EncounterSeed)
|
||||
line += $" [{pidiv.EncounterSeed:X8}]";
|
||||
lines.Add(line);
|
||||
}
|
||||
if (enc is EncounterSlot3 or EncounterStatic3)
|
||||
AppendDetailsFrame3(info, lines);
|
||||
AppendDetailsFrame3(info, lines, strings.Encounter);
|
||||
else if (enc is EncounterSlot4 or EncounterStatic4)
|
||||
AppendDetailsDate4(info, lines);
|
||||
AppendDetailsDate4(info, lines, strings.Encounter);
|
||||
}
|
||||
|
||||
private static string GetLinePokewalkerSeed(LegalInfo info)
|
||||
private static string GetLinePokewalkerSeed(LegalInfo info, LegalityLocalizationSet strings)
|
||||
{
|
||||
var pk = info.Entity;
|
||||
var result = PokewalkerRNG.GetLeastEffortSeed((uint)pk.IV_HP, (uint)pk.IV_ATK, (uint)pk.IV_DEF, (uint)pk.IV_SPA, (uint)pk.IV_SPD, (uint)pk.IV_SPE);
|
||||
var line = string.Format(L_FOriginSeed_0, result.Seed.ToString("X8"));
|
||||
var line = string.Format(strings.Encounter.Format, strings.Encounter.OriginSeed, result.Seed.ToString("X8"));
|
||||
line += $" [{result.Type} @ {result.PriorPoke}]";
|
||||
return line;
|
||||
}
|
||||
|
||||
private static string GetLineSlot34(LegalInfo info, PIDIV pidiv, IEncounterSlot34 s)
|
||||
private static string GetLineSlot34(LegalInfo info, LegalityLocalizationSet strings, PIDIV pidiv, IEncounterSlot34 s)
|
||||
{
|
||||
var lead = pidiv.Lead;
|
||||
var seed = !info.FrameMatches || lead == LeadRequired.Invalid ? pidiv.OriginSeed : pidiv.EncounterSeed;
|
||||
var line = string.Format(L_FOriginSeed_0, seed.ToString("X8"));
|
||||
var line = string.Format(strings.Encounter.Format, strings.Encounter.OriginSeed, seed.ToString("X8"));
|
||||
if (lead != LeadRequired.None)
|
||||
{
|
||||
if (lead is LeadRequired.Static)
|
||||
@@ -220,7 +233,7 @@ private static string GetLineSlot34(LegalInfo info, PIDIV pidiv, IEncounterSlot3
|
||||
else
|
||||
line += $" [{s.SlotNumber}]";
|
||||
|
||||
line += $" ({lead.Localize()})";
|
||||
line += $" ({lead.Localize(strings.Lines)})";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -230,7 +243,7 @@ private static string GetLineSlot34(LegalInfo info, PIDIV pidiv, IEncounterSlot3
|
||||
return line;
|
||||
}
|
||||
|
||||
private static void AppendDetailsDate4(LegalInfo info, List<string> lines)
|
||||
private static void AppendDetailsDate4(LegalInfo info, List<string> lines, EncounterDisplayLocalization loc)
|
||||
{
|
||||
var pidiv = info.PIDIV;
|
||||
if (pidiv.Type is not (PIDType.Method_1 or PIDType.ChainShiny))
|
||||
@@ -244,34 +257,34 @@ private static void AppendDetailsDate4(LegalInfo info, List<string> lines)
|
||||
var entity = info.Entity;
|
||||
var date = entity.MetDate ?? new DateOnly(2000, 1, 1);
|
||||
var initialSeed = ClassicEraRNG.SeekInitialSeed((uint)date.Year, (uint)date.Month, (uint)date.Day, seed);
|
||||
AppendInitialDateTime4(lines, initialSeed, seed, date);
|
||||
AppendInitialDateTime4(lines, initialSeed, seed, date, loc);
|
||||
}
|
||||
|
||||
private static void AppendInitialDateTime4(List<string> lines, uint initialSeed, uint origin, DateOnly date)
|
||||
private static void AppendInitialDateTime4(List<string> lines, uint initialSeed, uint origin, DateOnly date, EncounterDisplayLocalization loc)
|
||||
{
|
||||
var decompose = ClassicEraRNG.DecomposeSeed(initialSeed, (uint)date.Year, (uint)date.Month, (uint)date.Day);
|
||||
AppendInitialDateTime4(lines, initialSeed, origin, decompose);
|
||||
AppendInitialDateTime4(lines, initialSeed, origin, decompose, loc);
|
||||
}
|
||||
|
||||
private static void AppendInitialDateTime4(List<string> lines, uint initialSeed, uint origin, InitialSeedComponents4 decompose)
|
||||
private static void AppendInitialDateTime4(List<string> lines, uint initialSeed, uint origin, InitialSeedComponents4 decompose, EncounterDisplayLocalization loc)
|
||||
{
|
||||
var advances = LCRNG.GetDistance(initialSeed, origin);
|
||||
lines.Add($"{decompose.Year+2000:0000}-{decompose.Month:00}-{decompose.Day:00} @ {decompose.Hour:00}:{decompose.Minute:00}:{decompose.Second:00} - {decompose.Delay}");
|
||||
lines.Add($"Initial: 0x{initialSeed:X8}, Frame: {advances + 1}"); // frames are 1-indexed
|
||||
lines.Add(string.Format(loc.FrameInitial, initialSeed.ToString("X8"), advances + 1)); // frames are 1-indexed
|
||||
}
|
||||
|
||||
private static void AppendDetailsFrame3(LegalInfo info, List<string> lines)
|
||||
private static void AppendDetailsFrame3(LegalInfo info, List<string> lines, EncounterDisplayLocalization loc)
|
||||
{
|
||||
var pidiv = info.PIDIV;
|
||||
var pk = info.Entity;
|
||||
var enc = info.EncounterOriginal;
|
||||
var seed = enc is EncounterSlot3 && info.FrameMatches ? pidiv.EncounterSeed : pidiv.OriginSeed;
|
||||
var (initialSeed, advances) = GetInitialSeed3(seed, pk.Version);
|
||||
lines.Add($"Initial: 0x{initialSeed:X8}, Frame: {advances + 1}"); // frames are 1-indexed
|
||||
lines.Add(string.Format(loc.FrameInitial, initialSeed.ToString("X8"), advances + 1)); // frames are 1-indexed
|
||||
|
||||
var sb = new StringBuilder();
|
||||
AppendFrameTimeStamp3(advances, sb);
|
||||
lines.Add($"Time: {sb}");
|
||||
AppendFrameTimeStamp3(advances, sb, loc);
|
||||
lines.Add(string.Format(loc.Format, loc.Time, sb));
|
||||
|
||||
// Try appending the TID frame if it originates from Emerald.
|
||||
if (pk.Version is not GameVersion.E)
|
||||
@@ -281,10 +294,10 @@ private static void AppendDetailsFrame3(LegalInfo info, List<string> lines)
|
||||
var tidAdvances = LCRNG.GetDistance(tidSeed, seed);
|
||||
if (tidAdvances >= advances)
|
||||
return; // only show if it makes sense to
|
||||
lines.Add($"New Game: 0x{tidSeed:X8}, Frame: {tidAdvances + 1}"); // frames are 1-indexed
|
||||
lines.Add(string.Format(loc.FrameNewGame, tidSeed.ToString("X8"), tidAdvances + 1)); // frames are 1-indexed
|
||||
sb.Clear();
|
||||
AppendFrameTimeStamp3(tidAdvances, sb);
|
||||
lines.Add($"Time: {sb}");
|
||||
AppendFrameTimeStamp3(tidAdvances, sb, loc);
|
||||
lines.Add(string.Format(loc.Format, loc.Time, sb));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -292,7 +305,8 @@ private static void AppendDetailsFrame3(LegalInfo info, List<string> lines)
|
||||
/// </summary>
|
||||
/// <param name="frame">Frames elapsed since the initial seed.</param>
|
||||
/// <param name="sb">StringBuilder to append the timestamp to.</param>
|
||||
private static void AppendFrameTimeStamp3(uint frame, StringBuilder sb)
|
||||
/// <param name="loc">Localization strings for formatting.</param>
|
||||
private static void AppendFrameTimeStamp3(uint frame, StringBuilder sb, EncounterDisplayLocalization loc)
|
||||
{
|
||||
var time = TimeSpan.FromSeconds((double)frame / 60);
|
||||
if (time.TotalHours >= 1)
|
||||
@@ -303,7 +317,7 @@ private static void AppendFrameTimeStamp3(uint frame, StringBuilder sb)
|
||||
sb.Append($"{time.Milliseconds / 10:00}");
|
||||
|
||||
if (time.TotalDays >= 1)
|
||||
sb.Append($" (days: {(int)time.TotalDays})");
|
||||
sb.AppendFormat(loc.SuffixDays, (int)time.TotalDays);
|
||||
}
|
||||
|
||||
private static (uint Seed, uint Advances) GetInitialSeed3(uint seed, GameVersion game)
|
||||
@@ -329,25 +343,24 @@ private static (uint Seed, uint Advances) GetInitialSeed3(uint seed, GameVersion
|
||||
return (nearest16, ctr);
|
||||
}
|
||||
|
||||
private static string Localize(this LeadRequired lead)
|
||||
private static string Localize(this LeadRequired lead, LegalityCheckLocalization localization)
|
||||
{
|
||||
if (lead is LeadRequired.Invalid)
|
||||
return "❌";
|
||||
var (ability, isFail, condition) = lead.GetDisplayAbility();
|
||||
var abilities = GameInfo.Strings.Ability;
|
||||
var name = abilities[(int)ability];
|
||||
var result = isFail ? string.Format(L_F0_1, name, "❌") : name;
|
||||
var result = isFail ? string.Format(localization.F0_1, name, "❌") : name;
|
||||
if (condition != EncounterTriggerCondition.None)
|
||||
result += $"-{condition}";
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string GetEncounterName(this IEncounterable enc)
|
||||
public static string GetEncounterName(this IEncounterable enc, ReadOnlySpan<string> speciesNames)
|
||||
{
|
||||
var str = ParseSettings.SpeciesStrings;
|
||||
// Shouldn't ever be out of range, but just in case.
|
||||
var species = enc.Species;
|
||||
var name = (uint)species < str.Count ? str[species] : species.ToString();
|
||||
var name = (uint)species < speciesNames.Length ? speciesNames[species] : species.ToString();
|
||||
return $"{enc.LongName} ({name})";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using static PKHeX.Core.LearnMethod;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -14,9 +13,9 @@ namespace PKHeX.Core;
|
||||
public readonly record struct MoveLearnInfo(LearnMethod Method, LearnEnvironment Environment, byte Argument = 0)
|
||||
{
|
||||
/// <inheritdoc cref="Summarize(StringBuilder, ReadOnlySpan{char})"/>
|
||||
public void Summarize(StringBuilder sb)
|
||||
public void Summarize(StringBuilder sb, MoveSourceLocalization strings)
|
||||
{
|
||||
var localized = GetLocalizedMethod();
|
||||
var localized = GetLocalizedMethod(strings);
|
||||
Summarize(sb, localized);
|
||||
}
|
||||
|
||||
@@ -34,31 +33,31 @@ private void Summarize(StringBuilder sb, ReadOnlySpan<char> localizedMethod)
|
||||
sb.Append($" @ lv{Argument}");
|
||||
}
|
||||
|
||||
private string GetLocalizedMethod() => Method switch
|
||||
private string GetLocalizedMethod(MoveSourceLocalization strings) => Method switch
|
||||
{
|
||||
Empty => LMoveSourceEmpty,
|
||||
Relearn => LMoveSourceRelearn,
|
||||
Initial => LMoveSourceDefault,
|
||||
LevelUp => LMoveSourceLevelUp,
|
||||
TMHM => LMoveSourceTMHM,
|
||||
Tutor => LMoveSourceTutor,
|
||||
Sketch => LMoveSourceShared,
|
||||
EggMove => LMoveRelearnEgg,
|
||||
InheritLevelUp => LMoveEggInherited,
|
||||
Empty => strings.SourceEmpty,
|
||||
Relearn => strings.SourceRelearn,
|
||||
Initial => strings.SourceDefault,
|
||||
LevelUp => strings.SourceLevelUp,
|
||||
TMHM => strings.SourceTMHM,
|
||||
Tutor => strings.SourceTutor,
|
||||
Sketch => strings.SourceShared,
|
||||
EggMove => strings.RelearnEgg,
|
||||
InheritLevelUp => strings.EggInherited,
|
||||
|
||||
HOME => LMoveSourceSpecial,
|
||||
Evolution => LMoveSourceSpecial,
|
||||
Encounter => LMoveSourceSpecial,
|
||||
SpecialEgg => LMoveSourceSpecial,
|
||||
ShedinjaEvo => LMoveSourceSpecial,
|
||||
HOME => strings.SourceSpecial,
|
||||
Evolution => strings.SourceSpecial,
|
||||
Encounter => strings.SourceSpecial,
|
||||
SpecialEgg => strings.SourceSpecial,
|
||||
ShedinjaEvo => strings.SourceSpecial,
|
||||
|
||||
Shared => LMoveSourceShared,
|
||||
Shared => strings.SourceShared,
|
||||
|
||||
// Invalid
|
||||
None => LMoveSourceInvalid,
|
||||
Unobtainable or UnobtainableExpect => LMoveSourceInvalid,
|
||||
Duplicate => LMoveSourceDuplicate,
|
||||
EmptyInvalid => LMoveSourceEmpty,
|
||||
None => strings.SourceInvalid,
|
||||
Unobtainable or UnobtainableExpect => strings.SourceInvalid,
|
||||
Duplicate => strings.SourceDuplicate,
|
||||
EmptyInvalid => strings.SourceEmpty,
|
||||
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(Method), Method, null),
|
||||
};
|
||||
|
||||
@@ -18,18 +18,22 @@ namespace PKHeX.Core;
|
||||
internal MoveResult(LearnMethod method, LearnEnvironment game) : this(new MoveLearnInfo(method, game), Generation: game.GetGeneration()) { }
|
||||
private MoveResult(LearnMethod method) : this(new MoveLearnInfo(method, LearnEnvironment.None)) { }
|
||||
|
||||
public string Summary(ISpeciesForm current, EvolutionHistory history)
|
||||
public string Summary(in LegalityLocalizationContext ctx)
|
||||
{
|
||||
var sb = new StringBuilder(48);
|
||||
Info.Summarize(sb);
|
||||
Info.Summarize(sb, ctx.Settings.Moves);
|
||||
if (Info.Method.HasExpectedMove())
|
||||
{
|
||||
var name = ParseSettings.MoveStrings[Expect];
|
||||
var str = LegalityCheckStrings.LMoveFExpectSingle_0;
|
||||
var str = ctx.Settings.Lines.MoveFExpectSingle_0;
|
||||
sb.Append(' ').AppendFormat(str, name);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
var la = ctx.Analysis;
|
||||
var history = la.Info.EvoChainsAllGens;
|
||||
var current = la.Entity;
|
||||
|
||||
var detail = GetDetail(history);
|
||||
if (detail.Species == 0)
|
||||
return sb.ToString();
|
||||
@@ -57,9 +61,6 @@ private EvoCriteria GetDetail(EvolutionHistory history)
|
||||
public bool IsRelearn => Info.Method.IsRelearn();
|
||||
|
||||
public Severity Judgement => Valid ? Severity.Valid : Severity.Invalid;
|
||||
public string Rating => Judgement.Description();
|
||||
|
||||
public string Format(string format, int index, PKM pk, EvolutionHistory history) => string.Format(format, Rating, index, Summary(pk, history));
|
||||
|
||||
public static MoveResult Initial(LearnEnvironment game) => new(LearnMethod.Initial, game);
|
||||
public static readonly MoveResult Relearn = new(LearnMethod.Relearn);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static PKHeX.Core.LegalityAnalyzers;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -99,7 +99,7 @@ public LegalityAnalysis(PKM pk, IPersonalInfo pi, StorageSlotType source = Ignor
|
||||
{
|
||||
EncounterFinder.FindVerifiedEncounter(pk, Info);
|
||||
if (!pk.IsOriginValid)
|
||||
AddLine(Severity.Invalid, LEncConditionBadSpecies, CheckIdentifier.GameOrigin);
|
||||
AddLine(Severity.Invalid, EncConditionBadSpecies, CheckIdentifier.GameOrigin);
|
||||
GetParseMethod()();
|
||||
|
||||
Valid = Parse.TrueForAll(chk => chk.Valid)
|
||||
@@ -107,7 +107,7 @@ public LegalityAnalysis(PKM pk, IPersonalInfo pi, StorageSlotType source = Ignor
|
||||
&& MoveResult.AllValid(Info.Relearn);
|
||||
|
||||
if (!Valid && IsPotentiallyMysteryGift(Info, pk))
|
||||
AddLine(Severity.Invalid, LFatefulGiftMissing, CheckIdentifier.Fateful);
|
||||
AddLine(Severity.Invalid, FatefulGiftMissing, CheckIdentifier.Fateful);
|
||||
Parsed = true;
|
||||
}
|
||||
#if SUPPRESS
|
||||
@@ -130,7 +130,7 @@ public LegalityAnalysis(PKM pk, IPersonalInfo pi, StorageSlotType source = Ignor
|
||||
p = MoveResult.Unobtainable();
|
||||
}
|
||||
|
||||
AddLine(Severity.Invalid, L_AError, CheckIdentifier.Misc);
|
||||
AddLine(Severity.Invalid, Error, CheckIdentifier.Misc);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -261,7 +261,7 @@ private void ParsePK9()
|
||||
/// <param name="s">Check severity</param>
|
||||
/// <param name="c">Check comment</param>
|
||||
/// <param name="i">Check type</param>
|
||||
internal void AddLine(Severity s, string c, CheckIdentifier i) => AddLine(new CheckResult(s, i, c));
|
||||
internal void AddLine(Severity s, LegalityCheckResultCode c, CheckIdentifier i) => AddLine(CheckResult.Get(s, i, c));
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new Check parse value.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Localization strings for encounter display information.
|
||||
/// </summary>
|
||||
public sealed record EncounterDisplayLocalization
|
||||
{
|
||||
private static readonly EncounterDisplayLocalizationContext Context = new(LocalizationStorage<EncounterDisplayLocalization>.Options);
|
||||
public static readonly LocalizationStorage<EncounterDisplayLocalization> Cache = new("encounter", Context.EncounterDisplayLocalization);
|
||||
public static EncounterDisplayLocalization Get(string language = GameLanguage.DefaultLanguage) => Cache.Get(language);
|
||||
public static EncounterDisplayLocalization Get(LanguageID language) => Cache.Get(language.GetLanguageCode());
|
||||
|
||||
public required string Format { get; set; } = "{0}: {1}";
|
||||
public required string FormatLevelRange { get; set; } = "{0}: {1}-{2}";
|
||||
public required string EncounterType { get; set; } = "Encounter Type";
|
||||
public required string Version { get; set; } = "Version";
|
||||
public required string Level { get; set; } = "Level";
|
||||
public required string LevelRange { get; set; } = "Level Range";
|
||||
public required string Location { get; set; } = "Location";
|
||||
public required string OriginSeed { get; set; } = "Origin Seed";
|
||||
public required string PIDType { get; set; } = "PID Type";
|
||||
public required string Time { get; set; } = "Time";
|
||||
public required string FrameNewGame { get; set; } = "New Game: 0x{0}, Frame: {1}";
|
||||
public required string FrameInitial { get; set; } = "Initial: 0x{0}, Frame: {1}";
|
||||
public required string SuffixDays { get; set; } = " (days: {0})";
|
||||
}
|
||||
|
||||
[JsonSerializable(typeof(EncounterDisplayLocalization))]
|
||||
public sealed partial class EncounterDisplayLocalizationContext : JsonSerializerContext;
|
||||
27
PKHeX.Core/Legality/Localization/GeneralLocalization.cs
Normal file
27
PKHeX.Core/Legality/Localization/GeneralLocalization.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Localization strings for general display information.
|
||||
/// </summary>
|
||||
public sealed record GeneralLocalization
|
||||
{
|
||||
private static readonly GeneralLocalizationContext Context = new(LocalizationStorage<GeneralLocalization>.Options);
|
||||
public static readonly LocalizationStorage<GeneralLocalization> Cache = new("general", Context.GeneralLocalization);
|
||||
public static GeneralLocalization Get(string language = GameLanguage.DefaultLanguage) => Cache.Get(language);
|
||||
public static GeneralLocalization Get(LanguageID language) => Cache.Get(language.GetLanguageCode());
|
||||
|
||||
public required string[] StatNames { get; init; }
|
||||
public required string OriginalTrainer { get; init; } = "Original Trainer";
|
||||
public required string HandlingTrainer { get; init; } = "Handling Trainer";
|
||||
|
||||
public required string GenderMale { get; init; } = "Male";
|
||||
public required string GenderFemale { get; init; } = "Female";
|
||||
public required string GenderGenderless { get; init; } = "Genderless";
|
||||
}
|
||||
|
||||
[JsonSerializable(typeof(GeneralLocalization))]
|
||||
public sealed partial class GeneralLocalizationContext : JsonSerializerContext;
|
||||
441
PKHeX.Core/Legality/Localization/LegalityCheckLocalization.cs
Normal file
441
PKHeX.Core/Legality/Localization/LegalityCheckLocalization.cs
Normal file
@@ -0,0 +1,441 @@
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Legality Check Message Strings to indicate why certain <see cref="PKM"/> <see cref="LegalInfo"/> values are flagged.
|
||||
/// </summary>
|
||||
public sealed class LegalityCheckLocalization
|
||||
{
|
||||
private static readonly LegalityCheckLocalizationContext Context = new(LocalizationStorage<LegalityCheckLocalization>.Options);
|
||||
public static readonly LocalizationStorage<LegalityCheckLocalization> Cache = new("legality", Context.LegalityCheckLocalization);
|
||||
public static LegalityCheckLocalization Get(string language = GameLanguage.DefaultLanguage) => Cache.Get(language);
|
||||
public static LegalityCheckLocalization Get(LanguageID language) => Cache.Get(language.GetLanguageCode());
|
||||
|
||||
// Message String Name format: L/F[Category][Summary]
|
||||
#region General Strings
|
||||
|
||||
/// <summary>Default text for indicating validity.</summary>
|
||||
public string Valid { get; set; } = "Valid.";
|
||||
|
||||
/// <summary>Default text for indicating legality.</summary>
|
||||
public string Legal { get; set; } = "Legal!";
|
||||
|
||||
/// <summary>Default text for indicating an error has occurred.</summary>
|
||||
public string Error { get; set; } = "Internal error.";
|
||||
|
||||
/// <summary>Analysis not available for the <see cref="PKM"/></summary>
|
||||
public string AnalysisUnavailable { get; set; } = "Analysis not available for this Pokémon.";
|
||||
|
||||
/// <summary>Format text for exporting a legality check result.</summary>
|
||||
public string F0_1 { get; set; } = "{0}: {1}";
|
||||
|
||||
/// <summary>Severity string for <see cref="Severity.Invalid"/></summary>
|
||||
public string SInvalid { get; set; } = "Invalid";
|
||||
|
||||
/// <summary>Severity string for <see cref="Severity.Fishy"/></summary>
|
||||
public string SFishy { get; set; } = "Fishy";
|
||||
|
||||
/// <summary>Severity string for <see cref="Severity.Valid"/></summary>
|
||||
public string SValid { get; set; } = "Valid";
|
||||
|
||||
/// <summary>Severity string for anything not implemented.</summary>
|
||||
public string NotImplemented { get; set; } = "Not Implemented";
|
||||
|
||||
public string AbilityCapsuleUsed { get; set; } = "Ability available with Ability Capsule.";
|
||||
public string AbilityPatchUsed { get; set; } = "Ability available with Ability Patch.";
|
||||
public string AbilityPatchRevertUsed { get; set; } = "Ability available with Ability Patch Revert.";
|
||||
public string AbilityFlag { get; set; } = "Ability matches ability number.";
|
||||
public string AbilityHiddenFail { get; set; } = "Hidden Ability mismatch for encounter type.";
|
||||
public string AbilityHiddenUnavailable { get; set; } = "Hidden Ability not available.";
|
||||
public string AbilityMismatch { get; set; } = "Ability mismatch for encounter.";
|
||||
public string AbilityMismatch3 { get; set; } = "Ability does not match Generation 3 species ability.";
|
||||
public string AbilityMismatchFlag { get; set; } = "Ability does not match ability number.";
|
||||
public string AbilityMismatchGift { get; set; } = "Ability does not match Mystery Gift.";
|
||||
public string AbilityMismatchPID { get; set; } = "Ability does not match PID.";
|
||||
public string AbilityUnexpected { get; set; } = "Ability is not valid for species/form.";
|
||||
|
||||
public string AwakenedCap { get; set; } = "Individual AV cannot be greater than {0}.";
|
||||
public string AwakenedShouldBeValue { get; set; } = "{1} AV should be greater than {0}.";
|
||||
|
||||
public string BallAbility { get; set; } = "Can't obtain Hidden Ability with Ball.";
|
||||
public string BallEggCherish { get; set; } = "Can't have Cherish Ball for regular Egg.";
|
||||
public string BallEggMaster { get; set; } = "Can't have Master Ball for regular Egg.";
|
||||
public string BallEnc { get; set; } = "Correct ball for encounter type.";
|
||||
public string BallEncMismatch { get; set; } = "Can't have ball for encounter type.";
|
||||
public string BallHeavy { get; set; } = "Can't have Heavy Ball for light, low-catch rate species (Gen VII).";
|
||||
public string BallSpecies { get; set; } = "Can't obtain species in Ball.";
|
||||
public string BallSpeciesPass { get; set; } = "Ball possible for species.";
|
||||
public string BallUnavailable { get; set; } = "Ball unobtainable in origin Generation.";
|
||||
|
||||
public string ContestZero { get; set; } = "Contest Stats should be 0.";
|
||||
public string ContestZeroSheen { get; set; } = "Contest Stat Sheen should be 0.";
|
||||
public string ContestSheenGEQ_0 { get; set; } = "Contest Stat Sheen should be >= {0}.";
|
||||
public string ContestSheenLEQ_0 { get; set; } = "Contest Stat Sheen should be <= {0}.";
|
||||
|
||||
public string DateOutsideConsoleWindow { get; set; } = "Local Date is outside of console's local time window.";
|
||||
public string DateTimeClockInvalid { get; set; } = "Local Time is not a valid timestamp.";
|
||||
public string DateOutsideDistributionWindow { get; set; } = "Met Date is outside of distribution window.";
|
||||
|
||||
public string EggContest { get; set; } = "Cannot increase Contest Stats of an Egg.";
|
||||
public string EggEXP { get; set; } = "Eggs cannot receive experience.";
|
||||
public string EggFMetLevel_0 { get; set; } = "Invalid Met Level, expected {0}.";
|
||||
public string EggHatchCycles { get; set; } = "Invalid Egg hatch cycles.";
|
||||
public string EggLocation { get; set; } = "Able to hatch an Egg at Met Location.";
|
||||
public string EggLocationInvalid { get; set; } = "Can't hatch an Egg at Met Location.";
|
||||
public string EggLocationNone { get; set; } = "Invalid Egg Location, expected none.";
|
||||
public string EggLocationPalPark { get; set; } = "Invalid Met Location, expected Pal Park.";
|
||||
public string EggLocationTrade { get; set; } = "Able to hatch a traded Egg at Met Location.";
|
||||
public string EggLocationTradeFail { get; set; } = "Invalid Egg Location, shouldn't be 'traded' while an Egg.";
|
||||
public string EggMetLocationFail { get; set; } = "Can't obtain Egg from Egg Location.";
|
||||
public string EggNature { get; set; } = "Eggs cannot have their Stat Nature changed.";
|
||||
public string EggPokeathlon { get; set; } = "Eggs cannot have Pokéathlon stats.";
|
||||
public string EggPP { get; set; } = "Eggs cannot have modified move PP counts.";
|
||||
public string EggPPUp { get; set; } = "Cannot apply PP Ups to an Egg.";
|
||||
public string EggRelearnFlags { get; set; } = "Expected no Relearn Move Flags.";
|
||||
public string EggShinyLeaf { get; set; } = "Eggs cannot have Shiny Leaf/Crown.";
|
||||
public string EggShinyPokeStar { get; set; } = "Eggs cannot be a Pokéstar Studios star.";
|
||||
public string EggSpecies { get; set; } = "Can't obtain Egg for this species.";
|
||||
public string EggUnhatched { get; set; } = "Valid un-hatched Egg.";
|
||||
|
||||
public string EncCondition { get; set; } = "Valid Wild Encounter at location.";
|
||||
public string EncConditionBadRNGFrame { get; set; } = "Unable to match encounter conditions to a possible RNG frame.";
|
||||
public string EncConditionBadSpecies { get; set; } = "Species does not exist in origin game.";
|
||||
|
||||
public string EncGift { get; set; } = "Unable to match a gift Egg encounter from origin game.";
|
||||
public string EncGiftEggEvent { get; set; } = "Unable to match an event Egg encounter from origin game.";
|
||||
public string EncGiftIVMismatch { get; set; } = "IVs do not match Mystery Gift Data.";
|
||||
public string EncGiftNicknamed { get; set; } = "Event gift has been nicknamed.";
|
||||
public string EncGiftNotFound { get; set; } = "Unable to match to a Mystery Gift in the database.";
|
||||
public string EncGiftPIDMismatch { get; set; } = "Mystery Gift fixed PID mismatch.";
|
||||
public string EncGiftShinyMismatch { get; set; } = "Mystery Gift shiny mismatch.";
|
||||
public string EncGiftVersionNotDistributed { get; set; } = "Mystery Gift cannot be received by this version.";
|
||||
|
||||
public string EncInvalid { get; set; } = "Unable to match an encounter from origin game.";
|
||||
public string EncMasteryInitial { get; set; } = "Initial move mastery flags do not match the encounter's expected state.";
|
||||
|
||||
public string EncTradeChangedNickname { get; set; } = "In-game Trade Nickname has been altered.";
|
||||
public string EncTradeChangedOT { get; set; } = "In-game Trade OT has been altered.";
|
||||
public string EncTradeIndexBad { get; set; } = "In-game Trade invalid index?";
|
||||
public string EncTradeMatch { get; set; } = "Valid In-game trade.";
|
||||
public string EncTradeUnchanged { get; set; } = "In-game Trade OT and Nickname have not been altered.";
|
||||
|
||||
public string EncStaticPIDShiny { get; set; } = "Encounter shiny mismatch.";
|
||||
public string EncTypeMatch { get; set; } = "Encounter Type matches encounter.";
|
||||
public string EncTypeMismatch { get; set; } = "Encounter Type does not match encounter.";
|
||||
public string EncUnreleased { get; set; } = "Unreleased event.";
|
||||
public string EncUnreleasedEMewJP { get; set; } = "Non japanese Mew from Faraway Island. Unreleased event.";
|
||||
|
||||
public string EReaderAmerica { get; set; } = "American E-Reader Berry in Japanese save file.";
|
||||
public string EReaderInvalid { get; set; } = "Invalid E-Reader Berry.";
|
||||
public string EReaderJapan { get; set; } = "Japanese E-Reader Berry in international save file.";
|
||||
|
||||
public string Effort2Remaining { get; set; } = "2 EVs remaining.";
|
||||
public string EffortAbove252 { get; set; } = "EVs cannot go above 252.";
|
||||
public string EffortAbove510 { get; set; } = "EV total cannot be above 510.";
|
||||
public string EffortAllEqual { get; set; } = "EVs are all equal.";
|
||||
public string EffortCap100 { get; set; } = "Individual EV for a level 100 encounter in Generation 4 cannot be greater than 100.";
|
||||
public string EffortEgg { get; set; } = "Eggs cannot receive EVs.";
|
||||
public string EffortShouldBeZero { get; set; } = "Cannot receive EVs.";
|
||||
public string EffortEXPIncreased { get; set; } = "All EVs are zero, but leveled above Met Level.";
|
||||
public string EffortUntrainedCap { get; set; } = "Individual EV without changing EXP cannot be greater than {0}.";
|
||||
|
||||
public string EvoInvalid { get; set; } = "Evolution not valid (or level/trade evolution unsatisfied).";
|
||||
public string EvoTradeReqOutsider { get; set; } = "Outsider {0} should have evolved into {1}.";
|
||||
public string EvoTradeRequired { get; set; } = "Version Specific evolution requires a trade to opposite version. A Handling Trainer is required.";
|
||||
|
||||
public string FatefulGiftMissing { get; set; } = "Fateful Encounter with no matching Encounter. Has the Mystery Gift data been contributed?";
|
||||
public string FatefulInvalid { get; set; } = "Fateful Encounter should not be checked.";
|
||||
public string FatefulMissing { get; set; } = "Special In-game Fateful Encounter flag missing.";
|
||||
public string FatefulMystery { get; set; } = "Mystery Gift Fateful Encounter.";
|
||||
public string FatefulMysteryMissing { get; set; } = "Mystery Gift Fateful Encounter flag missing.";
|
||||
|
||||
public string FavoriteMarkingUnavailable { get; set; } = "Favorite Marking is not available.";
|
||||
|
||||
public string FormArgumentLEQ_0 { get; set; } = "Form argument is too high for current form.";
|
||||
public string FormArgumentGEQ_0 { get; set; } = "Form argument is too low for current form.";
|
||||
public string FormArgumentNotAllowed { get; set; } = "Form argument is not allowed for this encounter.";
|
||||
public string FormArgumentValid { get; set; } = "Form argument is valid.";
|
||||
public string FormArgumentInvalid { get; set; } = "Form argument is not valid.";
|
||||
public string FormBattle { get; set; } = "Form cannot exist outside of a battle.";
|
||||
public string FormEternal { get; set; } = "Valid Eternal Flower encounter.";
|
||||
public string FormEternalInvalid { get; set; } = "Invalid Eternal Flower encounter.";
|
||||
public string FormInvalidGame { get; set; } = "Form cannot be obtained in origin game.";
|
||||
public string FormInvalidNature { get; set; } = "Form cannot have this nature.";
|
||||
public string FormInvalidRange { get; set; } = "Form Count is out of range. Expected <= {0}, got {1}.";
|
||||
public string FormItem { get; set; } = "Held item matches Form.";
|
||||
public string FormItemInvalid { get; set; } = "Held item does not match Form.";
|
||||
public string FormParty { get; set; } = "Form cannot exist outside of Party.";
|
||||
public string FormPikachuCosplay { get; set; } = "Only Cosplay Pikachu can have this form.";
|
||||
public string FormPikachuCosplayInvalid { get; set; } = "Cosplay Pikachu cannot have the default form.";
|
||||
public string FormPikachuEventInvalid { get; set; } = "Event Pikachu cannot have the default form.";
|
||||
public string FormInvalidExpect_0 { get; set; } = "Form is invalid, expected form index {0}.";
|
||||
public string FormValid { get; set; } = "Form is Valid.";
|
||||
public string FormVivillon { get; set; } = "Valid Vivillon pattern.";
|
||||
public string FormVivillonEventPre { get; set; } = "Event Vivillon pattern on pre-evolution.";
|
||||
public string FormVivillonInvalid { get; set; } = "Invalid Vivillon pattern.";
|
||||
public string FormVivillonNonNative { get; set; } = "Non-native Vivillon pattern.";
|
||||
|
||||
public string G1CatchRateChain { get; set; } = "Catch rate does not match any species from Pokémon evolution chain.";
|
||||
public string G1CatchRateEvo { get; set; } = "Catch rate match species without encounters. Expected a preevolution catch rate.";
|
||||
public string G1CatchRateItem { get; set; } = "Catch rate does not match a valid held item from Generation 2.";
|
||||
public string G1CatchRateMatchPrevious { get; set; } = "Catch Rate matches a species from Pokémon evolution chain.";
|
||||
public string G1CatchRateMatchTradeback { get; set; } = "Catch rate matches a valid held item from Generation 2.";
|
||||
public string G1CatchRateNone { get; set; } = "Catch rate does not match any species from Pokémon evolution chain or any Generation 2 held items.";
|
||||
public string G1CharNick { get; set; } = "Nickname from Generation 1/2 uses unavailable characters.";
|
||||
public string G1CharOT { get; set; } = "OT from Generation 1/2 uses unavailable characters.";
|
||||
public string G1OTGender { get; set; } = "Female OT from Generation 1/2 is invalid.";
|
||||
public string G1Stadium { get; set; } = "Incorrect Stadium OT.";
|
||||
public string G1Type1Fail { get; set; } = "Invalid Type A, does not match species type.";
|
||||
public string G1Type2Fail { get; set; } = "Invalid Type B, does not match species type.";
|
||||
public string G1TypeMatch1 { get; set; } = "Valid Type A, matches species type.";
|
||||
public string G1TypeMatch2 { get; set; } = "Valid Type B, matches species type.";
|
||||
public string G1TypeMatchPorygon { get; set; } = "Porygon with valid Type A and B values.";
|
||||
public string G1TypePorygonFail { get; set; } = "Porygon with invalid Type A and B values. Does not a match a valid type combination.";
|
||||
public string G1TypePorygonFail1 { get; set; } = "Porygon with invalid Type A value.";
|
||||
public string G1TypePorygonFail2 { get; set; } = "Porygon with invalid Type B value.";
|
||||
public string G2InvalidTileTreeNotFound { get; set; } = "Could not find a tree for Crystal headbutt encounter that matches OTID.";
|
||||
public string G2TreeID { get; set; } = "Found a tree for Crystal headbutt encounter that matches OTID.";
|
||||
public string G2OTGender { get; set; } = "OT from Virtual Console games other than Crystal cannot be female.";
|
||||
|
||||
public string G3EReader { get; set; } = "Non Japanese Shadow E-reader Pokémon. Unreleased encounter.";
|
||||
public string G3OTGender { get; set; } = "OT from Colosseum/XD cannot be female.";
|
||||
public string G4InvalidTileR45Surf { get; set; } = "Johto Route 45 surfing encounter. Unreachable Water tiles.";
|
||||
public string G5IVAll30 { get; set; } = "All IVs of N's Pokémon should be 30.";
|
||||
public string G5PIDShinyGrotto { get; set; } = "Hidden Grotto captures cannot be shiny.";
|
||||
public string G5SparkleInvalid { get; set; } = "Special In-game N's Sparkle flag should not be checked.";
|
||||
public string G5SparkleRequired { get; set; } = "Special In-game N's Sparkle flag missing.";
|
||||
|
||||
public string GanbaruStatTooHigh { get; set; } = "One or more Ganbaru Value is above the natural limit of (10 - IV bonus).";
|
||||
|
||||
public string GenderInvalidNone { get; set; } = "Genderless Pokémon should not have a gender.";
|
||||
public string GeoBadOrder { get; set; } = "GeoLocation Memory: Gap/Blank present.";
|
||||
public string GeoHardwareInvalid { get; set; } = "Geolocation: Country is not in 3DS region.";
|
||||
public string GeoHardwareRange { get; set; } = "Invalid Console Region.";
|
||||
public string GeoHardwareValid { get; set; } = "Geolocation: Country is in 3DS region.";
|
||||
public string GeoMemoryMissing { get; set; } = "GeoLocation Memory: Memories should be present.";
|
||||
public string GeoNoCountryHT { get; set; } = "GeoLocation Memory: HT Name present but has no previous Country.";
|
||||
public string GeoNoRegion { get; set; } = "GeoLocation Memory: Region without Country.";
|
||||
|
||||
public string HyperTrainLevelGEQ_0 { get; set; } = "Can't Hyper Train a Pokémon that isn't level {0}.";
|
||||
public string HyperPerfectAll { get; set; } = "Can't Hyper Train a Pokémon with perfect IVs.";
|
||||
public string HyperPerfectOne { get; set; } = "Can't Hyper Train a perfect IV.";
|
||||
public string HyperPerfectUnavailable { get; set; } = "Can't Hyper Train any IV(s).";
|
||||
|
||||
public string ItemEgg { get; set; } = "Eggs cannot hold items.";
|
||||
public string ItemUnreleased { get; set; } = "Held item is unreleased.";
|
||||
|
||||
public string IVAllEqual_0 { get; set; } = "All IVs are {0}.";
|
||||
public string IVNotCorrect { get; set; } = "IVs do not match encounter requirements.";
|
||||
public string IVFlawlessCountGEQ_0 { get; set; } = "Should have at least {0} IVs = 31.";
|
||||
|
||||
public string LevelEXPThreshold { get; set; } = "Current experience matches level threshold.";
|
||||
public string LevelEXPTooHigh { get; set; } = "Current experience exceeds maximum amount for level 100.";
|
||||
public string LevelMetBelow { get; set; } = "Current level is below met level.";
|
||||
public string LevelMetGift { get; set; } = "Met Level does not match Mystery Gift level.";
|
||||
public string LevelMetGiftFail { get; set; } = "Current Level below Mystery Gift level.";
|
||||
public string LevelMetSane { get; set; } = "Current level is not below met level.";
|
||||
|
||||
public string MarkValueOutOfRange_0 { get; set; } = "Individual marking at index {0} is not within the allowed value range.";
|
||||
public string MarkValueShouldBeZero { get; set; } = "Marking flags cannot be set.";
|
||||
public string MarkValueUnusedBitsPresent { get; set; } = "Marking flags uses bits beyond the accessible range.";
|
||||
|
||||
public string MemoryArgBadCatch_H { get; set; } = "{0} Memory: {0} did not catch this.";
|
||||
public string MemoryArgBadHatch_H { get; set; } = "{0} Memory: {0} did not hatch this.";
|
||||
public string MemoryArgBadHT { get; set; } = "Memory: Can't have Handling Trainer Memory as Egg.";
|
||||
public string MemoryArgBadID_H { get; set; } = "{0} Memory: Can't obtain Memory on {0} Version.";
|
||||
public string MemoryArgBadItem_H1 { get; set; } = "{0} Memory: Species can't hold this item.";
|
||||
public string MemoryArgBadLocation_H { get; set; } = "{0} Memory: Can't obtain Location on {0} Version.";
|
||||
public string MemoryArgBadMove_H1 { get; set; } = "{0} Memory: Species can't learn {1}.";
|
||||
public string MemoryArgBadOTEgg_H { get; set; } = "{0} Memory: Link Trade is not a valid first memory.";
|
||||
public string MemoryArgBadSpecies_H1 { get; set; } = "{0} Memory: Can't capture species in game.";
|
||||
public string MemoryArgSpecies_H { get; set; } = "{0} Memory: Species can be captured in game.";
|
||||
public string MemoryCleared_H { get; set; } = "Memory: Not cleared properly.";
|
||||
public string MemoryValid_H { get; set; } = "{0} Memory is valid.";
|
||||
public string MemoryFeelInvalid_H { get; set; } = "{0} Memory: Invalid Feeling.";
|
||||
public string MemoryHTFlagInvalid { get; set; } = "Untraded: Current handler should not be the Handling Trainer.";
|
||||
public string MemoryHTGender_0 { get; set; } = "HT Gender invalid: {0}";
|
||||
public string MemoryHTLanguage { get; set; } = "HT Language is missing.";
|
||||
|
||||
public string MemoryIndexArgHT { get; set; } = "Should have a HT Memory TextVar value (somewhere).";
|
||||
public string MemoryIndexFeel_H1 { get; set; } = "{0} Memory: Feeling should be index {1}.";
|
||||
public string MemoryIndexFeelHTLEQ9 { get; set; } = "Should have a HT Memory Feeling value 0-9.";
|
||||
public string MemoryIndexID_H1 { get; set; } = "{0} Memory: Should be index {1}.";
|
||||
public string MemoryIndexIntensity_H1 { get; set; } = "{0} Memory: Intensity should be index {1}.";
|
||||
public string MemoryIndexIntensityHT1 { get; set; } = "Should have a HT Memory Intensity value (1st).";
|
||||
public string MemoryIndexIntensityMin_H1 { get; set; } = "{0} Memory: Intensity should be at least {1}.";
|
||||
public string MemoryIndexLinkHT { get; set; } = "Should have a Link Trade HT Memory.";
|
||||
public string MemoryIndexVar { get; set; } = "{0} Memory: TextVar should be index {1}.";
|
||||
public string MemoryMissingHT { get; set; } = "Memory: Handling Trainer Memory missing.";
|
||||
public string MemoryMissingOT { get; set; } = "Memory: Original Trainer Memory missing.";
|
||||
|
||||
public string MemorySocialZero { get; set; } = "Social Stat should be zero.";
|
||||
public string MemoryStatSocialLEQ_0 { get; set; } = "Social Stat should be <= {0}";
|
||||
|
||||
public string MemoryStatAffectionHT0 { get; set; } = "Untraded: Handling Trainer Affection should be 0.";
|
||||
public string MemoryStatAffectionOT0 { get; set; } = "OT Affection should be 0.";
|
||||
public string MemoryStatFriendshipHT0 { get; set; } = "Untraded: Handling Trainer Friendship should be 0.";
|
||||
public string MemoryStatFriendshipOTBaseEvent_0 { get; set; } = "Event OT Friendship does not match base friendship ({0}).";
|
||||
|
||||
public string MetDetailTimeOfDay { get; set; } = "Met Time of Day value is not within the expected range.";
|
||||
public string MoveEvoFCombination_0 { get; set; } = "Moves combinations is not compatible with {0} evolution.";
|
||||
public string MoveFExpectSingle_0 { get; set; } = "Expected: {0}";
|
||||
public string MoveKeldeoMismatch { get; set; } = "Keldeo Move/Form mismatch.";
|
||||
public string MovePPExpectHealed_0 { get; set; } = "Move {0} PP is below the amount expected.";
|
||||
public string MovePPTooHigh_0 { get; set; } = "Move {0} PP is above the amount allowed.";
|
||||
public string MovePPUpsTooHigh_0 { get; set; } = "Move {0} PP Ups is above the amount allowed.";
|
||||
|
||||
public string MoveShopAlphaMoveShouldBeMastered_0 { get; set; } = "Alpha Move should be marked as mastered.";
|
||||
public string MoveShopAlphaMoveShouldBeOther { get; set; } = "Alpha encounter cannot be found with this Alpha Move.";
|
||||
public string MoveShopAlphaMoveShouldBeZero { get; set; } = "Only Alphas may have an Alpha Move set.";
|
||||
public string MoveShopMasterInvalid_0 { get; set; } = "Cannot manually master {0}: not permitted to master.";
|
||||
public string MoveShopMasterNotLearned_0 { get; set; } = "Cannot manually master {0}: not in possible learned level up moves.";
|
||||
public string MoveShopPurchaseInvalid_0 { get; set; } = "Cannot purchase {0} from the move shop.";
|
||||
|
||||
public string MoveTechRecordFlagMissing_0 { get; set; } = "Unexpected Technical Record Learned flag: {0}";
|
||||
|
||||
public string NickFlagEggNo { get; set; } = "Egg must be not nicknamed.";
|
||||
public string NickFlagEggYes { get; set; } = "Egg must be nicknamed.";
|
||||
public string NickInvalidChar { get; set; } = "Cannot be given this Nickname.";
|
||||
public string NickLengthLong { get; set; } = "Nickname too long.";
|
||||
public string NickLengthShort { get; set; } = "Nickname is empty.";
|
||||
public string NickMatchLanguage { get; set; } = "Nickname matches species name.";
|
||||
public string NickMatchLanguageEgg { get; set; } = "Egg matches language Egg name.";
|
||||
public string NickMatchLanguageEggFail { get; set; } = "Egg name does not match language Egg name.";
|
||||
public string NickMatchLanguageFail { get; set; } = "Nickname does not match species name.";
|
||||
public string NickMatchLanguageFlag { get; set; } = "Nickname flagged, matches species name.";
|
||||
public string NickMatchNoOthers { get; set; } = "Nickname does not match another species name.";
|
||||
public string NickMatchNoOthersFail { get; set; } = "Nickname matches another species name (+language).";
|
||||
|
||||
public string OTLanguage { get; set; } = "Language ID should be {0}, not {1}.";
|
||||
public string OTLong { get; set; } = "OT Name too long.";
|
||||
public string OTShort { get; set; } = "OT Name too short.";
|
||||
public string OTSuspicious { get; set; } = "Suspicious Original Trainer details.";
|
||||
|
||||
public string OT_IDEqual { get; set; } = "TID16 and SID16 are equal.";
|
||||
public string OT_IDs0 { get; set; } = "TID16 and SID16 are 0.";
|
||||
public string OT_SID0 { get; set; } = "SID16 is zero.";
|
||||
public string OT_SID0Invalid { get; set; } = "SID16 should be 0.";
|
||||
public string OT_TID0 { get; set; } = "TID16 is zero.";
|
||||
public string OT_IDInvalid { get; set; } = "TID16 and SID16 combination is not possible.";
|
||||
|
||||
public string PIDEncryptWurmple { get; set; } = "Wurmple evolution Encryption Constant mismatch.";
|
||||
public string PIDEncryptZero { get; set; } = "Encryption Constant is not set.";
|
||||
public string PIDEqualsEC { get; set; } = "Encryption Constant matches PID.";
|
||||
public string PIDGenderMatch { get; set; } = "Gender matches PID.";
|
||||
public string PIDGenderMismatch { get; set; } = "PID-Gender mismatch.";
|
||||
public string PIDNatureMatch { get; set; } = "Nature matches PID.";
|
||||
public string PIDNatureMismatch { get; set; } = "PID-Nature mismatch.";
|
||||
public string PIDTypeMismatch { get; set; } = "PID+ correlation does not match what was expected for the Encounter's type.";
|
||||
public string PIDZero { get; set; } = "PID is not set.";
|
||||
|
||||
public string PokerusDaysTooHigh_0 { get; set; } = "Pokérus Days Remaining value is too high; expected <= {0}.";
|
||||
public string PokerusStrainUnobtainable_0 { get; set; } = "Pokérus Strain {0} cannot be obtained.";
|
||||
|
||||
public string RibbonAllValid { get; set; } = "All ribbons accounted for.";
|
||||
public string RibbonEgg { get; set; } = "Can't receive Ribbon(s) as an Egg.";
|
||||
public string RibbonFInvalid_0 { get; set; } = "Invalid Ribbons: {0}";
|
||||
public string RibbonMissing_0 { get; set; } = "Missing Ribbons: {0}";
|
||||
public string RibbonMarkingInvalid_0 { get; set; } = "Invalid Marking: {0}";
|
||||
public string RibbonMarkingAffixed_0 { get; set; } = "Invalid Affixed Ribbon/Marking: {0}";
|
||||
|
||||
public string StatDynamaxInvalid { get; set; } = "Dynamax Level is not within the expected range.";
|
||||
public string StatIncorrectHeight { get; set; } = "Calculated Height does not match stored value.";
|
||||
public string StatIncorrectHeightCopy { get; set; } = "Copy Height does not match the original value.";
|
||||
public string StatIncorrectHeightValue { get; set; } = "Height does not match the expected value.";
|
||||
public string StatIncorrectWeight { get; set; } = "Calculated Weight does not match stored value.";
|
||||
public string StatIncorrectWeightValue { get; set; } = "Weight does not match the expected value.";
|
||||
public string StatInvalidHeightWeight { get; set; } = "Height / Weight values are statistically improbable.";
|
||||
public string StatIncorrectCP { get; set; } = "Calculated CP does not match stored value.";
|
||||
public string StatGigantamaxInvalid { get; set; } = "Gigantamax Flag mismatch.";
|
||||
public string StatGigantamaxValid { get; set; } = "Gigantamax Flag was changed via Max Soup.";
|
||||
public string StatNatureInvalid { get; set; } = "Stat Nature is not within the expected range.";
|
||||
public string StatBattleVersionInvalid { get; set; } = "Battle Version is not within the expected range.";
|
||||
public string StatNobleInvalid { get; set; } = "Noble Flag mismatch.";
|
||||
public string StatAlphaInvalid { get; set; } = "Alpha Flag mismatch.";
|
||||
|
||||
public string StoredSourceEgg { get; set; } = "Egg must be in Box or Party.";
|
||||
public string StoredSlotSourceInvalid_0 { get; set; } = "Invalid Stored Source: {0}";
|
||||
|
||||
public string SuperComplete { get; set; } = "Super Training complete flag mismatch.";
|
||||
public string SuperDistro { get; set; } = "Distribution Super Training missions are not released.";
|
||||
public string SuperEgg { get; set; } = "Can't Super Train an Egg.";
|
||||
public string SuperNoComplete { get; set; } = "Can't have active Super Training complete flag for origins.";
|
||||
public string SuperNoUnlocked { get; set; } = "Can't have active Super Training unlocked flag for origins.";
|
||||
public string SuperUnavailable { get; set; } = "Super Training missions are not available in games visited.";
|
||||
public string SuperUnused { get; set; } = "Unused Super Training Flag is flagged.";
|
||||
|
||||
public string TeraTypeIncorrect { get; set; } = "Tera Type does not match the expected value.";
|
||||
public string TeraTypeMismatch { get; set; } = "Tera Type does not match either of the default types.";
|
||||
|
||||
public string TradeNotAvailable { get; set; } = "Encounter cannot be traded to the active trainer.";
|
||||
|
||||
public string TrainerIDNoSeed { get; set; } = "Trainer ID is not obtainable from any RNG seed.";
|
||||
|
||||
public string TransferBad { get; set; } = "Incorrectly transferred from previous generation.";
|
||||
|
||||
public string TransferCurrentHandlerInvalid { get; set; } = "Invalid Current handler value, trainer details for save file expected another value.";
|
||||
public string TransferEgg { get; set; } = "Can't transfer Eggs between Generations.";
|
||||
public string TransferEggLocationTransporter { get; set; } = "Invalid Met Location, expected Poké Transfer.";
|
||||
public string TransferEggMetLevel { get; set; } = "Invalid Met Level for transfer.";
|
||||
public string TransferEggVersion { get; set; } = "Can't transfer Eggs to this game.";
|
||||
public string TransferFlagIllegal { get; set; } = "Flagged as illegal by the game (glitch abuse).";
|
||||
public string TransferHTFlagRequired { get; set; } = "Current handler cannot be the OT.";
|
||||
public string TransferHTMismatchName { get; set; } = "Handling trainer does not match the expected trainer name.";
|
||||
public string TransferHTMismatchGender { get; set; } = "Handling trainer does not match the expected trainer gender.";
|
||||
public string TransferHTMismatchLanguage { get; set; } = "Handling trainer does not match the expected trainer language.";
|
||||
public string TransferKoreanGen4 { get; set; } = "Korean Generation 4 games cannot interact with International Generation 4 games.";
|
||||
public string TransferMet { get; set; } = "Invalid Met Location, expected Poké Transfer or Crown.";
|
||||
public string TransferNotPossible { get; set; } = "Unable to transfer into current format from origin format.";
|
||||
public string TransferMetLocation { get; set; } = "Invalid Transfer Met Location.";
|
||||
public string TransferNature { get; set; } = "Invalid Nature for transfer Experience.";
|
||||
public string TransferObedienceLevel { get; set; } = "Invalid Obedience Level.";
|
||||
public string TransferPIDECBitFlip { get; set; } = "PID should be equal to EC [with top bit flipped]!";
|
||||
public string TransferPIDECEquals { get; set; } = "PID should be equal to EC!";
|
||||
public string TransferPIDECXor { get; set; } = "Encryption Constant matches shinyxored PID.";
|
||||
public string TransferTrackerMissing { get; set; } = "Pokémon HOME Transfer Tracker is missing.";
|
||||
public string TransferTrackerShouldBeZero { get; set; } = "Pokémon HOME Transfer Tracker should be 0.";
|
||||
|
||||
public string TrashBytesExpected { get; set; } = "Expected Trash Bytes.";
|
||||
public string TrashBytesMismatchInitial { get; set; } = "Expected initial trash bytes to match the encounter.";
|
||||
public string TrashBytesMissingTerminator { get; set; } = "Final terminator missing.";
|
||||
public string TrashBytesShouldBeEmpty { get; set; } = "Trash Bytes should be cleared.";
|
||||
|
||||
#endregion
|
||||
|
||||
public string EncTradeShouldHaveEvolvedToSpecies_0 { get; set; } = "Trade Encounter should have evolved to species: {0}.";
|
||||
public string EncGiftLanguageNotDistributed { get; set; } = "Gift Encounter was never distributed with this language.";
|
||||
public string EncGiftRegionNotDistributed { get; set; } = "Gift Encounter was never distributed to this Console Region.";
|
||||
public string FormInvalidRangeLEQ_0 { get; set; } = "Form Count is out of range. Expected <= {0}, got {1}.";
|
||||
public string MovesShouldMatchRelearnMoves { get; set; } = "Moves should exactly match Relearn Moves.";
|
||||
public string MemoryStatEnjoyment_0 { get; set; } = "Enjoyment should be {0}.";
|
||||
public string MemoryStatFullness_0 { get; set; } = "Fullness should be {0}.";
|
||||
public string MemoryStatFullnessLEQ_0 { get; set; } = "Fullness should be <= {0}.";
|
||||
public string OTLanguageShouldBe_0 { get; set; } = "Language ID should be {0}, not {1}.";
|
||||
public string OTLanguageShouldBe_0or1 { get; set; } = "Language ID should be {0} or {1}, not {2}.";
|
||||
public string OTLanguageShouldBeLeq_0 { get; set; } = "Language ID should be <= {0}, not {1}.";
|
||||
public string OTLanguageCannotPlayOnVersion_0 { get; set; } = "Language ID {0} cannot be played on this version.";
|
||||
public string OTLanguageCannotTransferToConsoleRegion_0 { get; set; } = "Language ID {0} cannot be transferred to this Console Region.";
|
||||
|
||||
public string WordFilterInvalidCharacter_0 { get; set; } = "Word Filter: Invalid character '{0}' (0x{1}).";
|
||||
public string WordFilterFlaggedPattern_01 { get; set; } = "Word Filter ({1}): Flagged pattern '{0}'.";
|
||||
public string WordFilterTooManyNumbers_0 { get; set; } = "Word Filter: Too many numbers (>{0}).";
|
||||
public string BulkCloneDetectedDetails { get; set; } = "Clone detected (Details).";
|
||||
public string BulkCloneDetectedTracker { get; set; } = "Clone detected (Duplicate Tracker).";
|
||||
public string HintEvolvesToSpecies_0 { get; set; } = "Evolves to species: {0}.";
|
||||
public string HintEvolvesToRareForm_0 { get; set; } = "Evolves to rare form: {0}.";
|
||||
public string BulkSharingEncryptionConstantGenerationDifferent { get; set; } = "Detected sharing of Encryption Constant across generations.";
|
||||
public string BulkSharingEncryptionConstantGenerationSame { get; set; } = "Detected sharing of Encryption Constant.";
|
||||
public string BulkSharingEncryptionConstantRNGType { get; set; } = "Detected sharing of Encryption Constant sharing for different RNG encounters.";
|
||||
public string BulkSharingPIDGenerationDifferent { get; set; } = "Detected sharing of PID across generations.";
|
||||
public string BulkSharingPIDGenerationSame { get; set; } = "Detected sharing of PID.";
|
||||
public string BulkSharingPIDRNGType { get; set; } = "Detected sharing of PID for different RNG encounters.";
|
||||
public string BulkDuplicateMysteryGiftEggReceived { get; set; } = "Detected multiple redemptions of the same non-repeatable Mystery Gift Egg.";
|
||||
public string BulkSharingTrainerID { get; set; } = "Detected sharing of Trainer ID across multiple trainer names.";
|
||||
public string BulkSharingTrainerVersion { get; set; } = "Detected sharing of Trainer ID across multiple versions.";
|
||||
}
|
||||
|
||||
[JsonSerializable(typeof(LegalityCheckLocalization))]
|
||||
internal sealed partial class LegalityCheckLocalizationContext : JsonSerializerContext;
|
||||
@@ -0,0 +1,407 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="LegalityCheckResultCode"/> to convert to human-readable strings.
|
||||
/// </summary>
|
||||
public static class LegalityCheckResultCodeExtensions
|
||||
{
|
||||
public static bool IsArgument(this LegalityCheckResultCode code) => code is < FirstWithMove and >= FirstWithArgument;
|
||||
public static bool IsMove(this LegalityCheckResultCode code) => code is < FirstWithLanguage and >= FirstWithMove;
|
||||
public static bool IsLanguage(this LegalityCheckResultCode code) => code is < FirstWithMemory and >= FirstWithLanguage;
|
||||
public static bool IsMemory(this LegalityCheckResultCode code) => code is < FirstComplex and >= FirstWithMemory;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the template string for the given result code.
|
||||
/// </summary>
|
||||
public static string GetTemplate(this LegalityCheckResultCode code, LegalityCheckLocalization localization) => code switch
|
||||
{
|
||||
// General Strings
|
||||
Valid => localization.Valid,
|
||||
Error => localization.Error,
|
||||
|
||||
// Ability
|
||||
AbilityCapsuleUsed => localization.AbilityCapsuleUsed,
|
||||
AbilityPatchUsed => localization.AbilityPatchUsed,
|
||||
AbilityPatchRevertUsed => localization.AbilityPatchRevertUsed,
|
||||
AbilityFlag => localization.AbilityFlag,
|
||||
AbilityHiddenFail => localization.AbilityHiddenFail,
|
||||
AbilityHiddenUnavailable => localization.AbilityHiddenUnavailable,
|
||||
AbilityMismatch => localization.AbilityMismatch,
|
||||
AbilityMismatch3 => localization.AbilityMismatch3,
|
||||
AbilityMismatchFlag => localization.AbilityMismatchFlag,
|
||||
AbilityMismatchGift => localization.AbilityMismatchGift,
|
||||
AbilityMismatchPID => localization.AbilityMismatchPID,
|
||||
AbilityUnexpected => localization.AbilityUnexpected,
|
||||
|
||||
// Awakened Values
|
||||
AwakenedCap => localization.AwakenedCap,
|
||||
AwakenedStatGEQ_01 => localization.AwakenedShouldBeValue,
|
||||
|
||||
// Ball
|
||||
BallAbility => localization.BallAbility,
|
||||
BallEggCherish => localization.BallEggCherish,
|
||||
BallEggMaster => localization.BallEggMaster,
|
||||
BallEnc => localization.BallEnc,
|
||||
BallEncMismatch => localization.BallEncMismatch,
|
||||
BallHeavy => localization.BallHeavy,
|
||||
BallSpecies => localization.BallSpecies,
|
||||
BallSpeciesPass => localization.BallSpeciesPass,
|
||||
BallUnavailable => localization.BallUnavailable,
|
||||
|
||||
// Contest
|
||||
ContestZero => localization.ContestZero,
|
||||
ContestZeroSheen => localization.ContestZeroSheen,
|
||||
ContestSheenGEQ_0 => localization.ContestSheenGEQ_0,
|
||||
ContestSheenLEQ_0 => localization.ContestSheenLEQ_0,
|
||||
|
||||
// Date & Timestamps
|
||||
DateOutsideConsoleWindow => localization.DateOutsideConsoleWindow,
|
||||
DateTimeClockInvalid => localization.DateTimeClockInvalid,
|
||||
DateOutsideDistributionWindow => localization.DateOutsideDistributionWindow,
|
||||
|
||||
// Egg
|
||||
EggContest => localization.EggContest,
|
||||
EggEXP => localization.EggEXP,
|
||||
EggFMetLevel_0 => localization.EggFMetLevel_0,
|
||||
EggHatchCycles => localization.EggHatchCycles,
|
||||
EggLocation => localization.EggLocation,
|
||||
EggLocationInvalid => localization.EggLocationInvalid,
|
||||
EggLocationNone => localization.EggLocationNone,
|
||||
EggLocationPalPark => localization.EggLocationPalPark,
|
||||
EggLocationTrade => localization.EggLocationTrade,
|
||||
EggLocationTradeFail => localization.EggLocationTradeFail,
|
||||
EggMetLocationFail => localization.EggMetLocationFail,
|
||||
EggNature => localization.EggNature,
|
||||
EggPokeathlon => localization.EggPokeathlon,
|
||||
EggPP => localization.EggPP,
|
||||
EggPPUp => localization.EggPPUp,
|
||||
EggRelearnFlags => localization.EggRelearnFlags,
|
||||
EggShinyLeaf => localization.EggShinyLeaf,
|
||||
EggShinyPokeStar => localization.EggShinyPokeStar,
|
||||
EggSpecies => localization.EggSpecies,
|
||||
EggUnhatched => localization.EggUnhatched,
|
||||
|
||||
// Encounter
|
||||
EncCondition => localization.EncCondition,
|
||||
EncConditionBadRNGFrame => localization.EncConditionBadRNGFrame,
|
||||
EncConditionBadSpecies => localization.EncConditionBadSpecies,
|
||||
EncGift => localization.EncGift,
|
||||
EncGiftEggEvent => localization.EncGiftEggEvent,
|
||||
EncGiftIVMismatch => localization.EncGiftIVMismatch,
|
||||
EncGiftNicknamed => localization.EncGiftNicknamed,
|
||||
EncGiftNotFound => localization.EncGiftNotFound,
|
||||
EncGiftPIDMismatch => localization.EncGiftPIDMismatch,
|
||||
EncGiftShinyMismatch => localization.EncGiftShinyMismatch,
|
||||
EncGiftVersionNotDistributed => localization.EncGiftVersionNotDistributed,
|
||||
EncInvalid => localization.EncInvalid,
|
||||
EncMasteryInitial => localization.EncMasteryInitial,
|
||||
EncTradeChangedNickname => localization.EncTradeChangedNickname,
|
||||
EncTradeChangedOT => localization.EncTradeChangedOT,
|
||||
EncTradeIndexBad => localization.EncTradeIndexBad,
|
||||
EncTradeMatch => localization.EncTradeMatch,
|
||||
EncTradeUnchanged => localization.EncTradeUnchanged,
|
||||
EncStaticPIDShiny => localization.EncStaticPIDShiny,
|
||||
EncTypeMatch => localization.EncTypeMatch,
|
||||
EncTypeMismatch => localization.EncTypeMismatch,
|
||||
EncUnreleased => localization.EncUnreleased,
|
||||
EncUnreleasedEMewJP => localization.EncUnreleasedEMewJP,
|
||||
|
||||
// E-Reader
|
||||
EReaderAmerica => localization.EReaderAmerica,
|
||||
EReaderInvalid => localization.EReaderInvalid,
|
||||
EReaderJapan => localization.EReaderJapan,
|
||||
|
||||
// Effort Values
|
||||
Effort2Remaining => localization.Effort2Remaining,
|
||||
EffortAbove252 => localization.EffortAbove252,
|
||||
EffortAbove510 => localization.EffortAbove510,
|
||||
EffortAllEqual => localization.EffortAllEqual,
|
||||
EffortCap100 => localization.EffortCap100,
|
||||
EffortEgg => localization.EffortEgg,
|
||||
EffortShouldBeZero => localization.EffortShouldBeZero,
|
||||
EffortEXPIncreased => localization.EffortEXPIncreased,
|
||||
EffortUntrainedCap_0 => localization.EffortUntrainedCap,
|
||||
|
||||
// Evolution
|
||||
EvoInvalid => localization.EvoInvalid,
|
||||
EvoTradeReqOutsider_0 => localization.EvoTradeReqOutsider,
|
||||
EvoTradeRequired => localization.EvoTradeRequired,
|
||||
|
||||
// Form
|
||||
FormArgumentLEQ_0 => localization.FormArgumentLEQ_0,
|
||||
FormArgumentGEQ_0 => localization.FormArgumentGEQ_0,
|
||||
FormArgumentNotAllowed => localization.FormArgumentNotAllowed,
|
||||
FormArgumentValid => localization.FormArgumentValid,
|
||||
FormArgumentInvalid => localization.FormArgumentInvalid,
|
||||
FormBattle => localization.FormBattle,
|
||||
FormEternal => localization.FormEternal,
|
||||
FormEternalInvalid => localization.FormEternalInvalid,
|
||||
FormInvalidGame => localization.FormInvalidGame,
|
||||
FormInvalidNature => localization.FormInvalidNature,
|
||||
FormInvalidRange_0 => localization.FormInvalidRange,
|
||||
FormItemMatches => localization.FormItem,
|
||||
FormItemInvalid => localization.FormItemInvalid,
|
||||
FormParty => localization.FormParty,
|
||||
FormPikachuCosplay => localization.FormPikachuCosplay,
|
||||
FormPikachuCosplayInvalid => localization.FormPikachuCosplayInvalid,
|
||||
FormPikachuEventInvalid => localization.FormPikachuEventInvalid,
|
||||
FormInvalidExpect_0 => localization.FormInvalidExpect_0,
|
||||
FormValid => localization.FormValid,
|
||||
FormVivillon => localization.FormVivillon,
|
||||
FormVivillonEventPre => localization.FormVivillonEventPre,
|
||||
FormVivillonInvalid => localization.FormVivillonInvalid,
|
||||
FormVivillonNonNative => localization.FormVivillonNonNative,
|
||||
|
||||
// Hyper Training
|
||||
HyperTrainLevelGEQ_0 => localization.HyperTrainLevelGEQ_0,
|
||||
HyperPerfectAll => localization.HyperPerfectAll,
|
||||
HyperPerfectOne => localization.HyperPerfectOne,
|
||||
HyperPerfectUnavailable => localization.HyperPerfectUnavailable,
|
||||
|
||||
// IVs
|
||||
IVAllEqual_0 => localization.IVAllEqual_0,
|
||||
IVNotCorrect => localization.IVNotCorrect,
|
||||
IVFlawlessCountGEQ_0 => localization.IVFlawlessCountGEQ_0,
|
||||
|
||||
// Markings
|
||||
MarkValueOutOfRange_0 => localization.MarkValueOutOfRange_0,
|
||||
MarkValueShouldBeZero => localization.MarkValueShouldBeZero,
|
||||
MarkValueUnusedBitsPresent => localization.MarkValueUnusedBitsPresent,
|
||||
|
||||
// Moves
|
||||
MoveEvoFCombination_0 => localization.MoveEvoFCombination_0,
|
||||
MovePPExpectHealed_0 => localization.MovePPExpectHealed_0,
|
||||
MovePPTooHigh_0 => localization.MovePPTooHigh_0,
|
||||
MovePPUpsTooHigh_0 => localization.MovePPUpsTooHigh_0,
|
||||
MoveShopMasterInvalid_0 => localization.MoveShopMasterInvalid_0,
|
||||
MoveShopMasterNotLearned_0 => localization.MoveShopMasterNotLearned_0,
|
||||
MoveShopPurchaseInvalid_0 => localization.MoveShopPurchaseInvalid_0,
|
||||
MoveTechRecordFlagMissing_0 => localization.MoveTechRecordFlagMissing_0,
|
||||
|
||||
// Memory
|
||||
MemoryStatSocialLEQ_0 => localization.MemoryStatSocialLEQ_0,
|
||||
|
||||
// Pokerus
|
||||
PokerusDaysLEQ_0 => localization.PokerusDaysTooHigh_0,
|
||||
PokerusStrainUnobtainable_0 => localization.PokerusStrainUnobtainable_0,
|
||||
|
||||
// Ribbons
|
||||
RibbonFInvalid_0 => localization.RibbonFInvalid_0,
|
||||
RibbonMissing_0 => localization.RibbonMissing_0,
|
||||
RibbonMarkingInvalid_0 => localization.RibbonMarkingInvalid_0,
|
||||
RibbonMarkingAffixed_0 => localization.RibbonMarkingAffixed_0,
|
||||
|
||||
// Storage
|
||||
StoredSlotSourceInvalid_0 => localization.StoredSlotSourceInvalid_0,
|
||||
|
||||
EncGiftLanguageNotDistributed_0 => localization.EncGiftLanguageNotDistributed,
|
||||
EncGiftRegionNotDistributed => localization.EncGiftRegionNotDistributed,
|
||||
EncTradeShouldHaveEvolvedToSpecies_0 => localization.EncTradeShouldHaveEvolvedToSpecies_0,
|
||||
FatefulGiftMissing => localization.FatefulGiftMissing,
|
||||
FatefulInvalid => localization.FatefulInvalid,
|
||||
FatefulMissing => localization.FatefulMissing,
|
||||
FatefulMystery => localization.FatefulMystery,
|
||||
FatefulMysteryMissing => localization.FatefulMysteryMissing,
|
||||
FavoriteMarkingUnavailable => localization.FavoriteMarkingUnavailable,
|
||||
FormInvalidRangeLEQ_0 => localization.FormInvalidRangeLEQ_0,
|
||||
G1CatchRateChain => localization.G1CatchRateChain,
|
||||
G1CatchRateEvo => localization.G1CatchRateEvo,
|
||||
G1CatchRateItem => localization.G1CatchRateItem,
|
||||
G1CatchRateMatchPrevious => localization.G1CatchRateMatchPrevious,
|
||||
G1CatchRateMatchTradeback => localization.G1CatchRateMatchTradeback,
|
||||
G1CatchRateNone => localization.G1CatchRateNone,
|
||||
G1CharNick => localization.G1CharNick,
|
||||
G1CharOT => localization.G1CharOT,
|
||||
G1OTGender => localization.G1OTGender,
|
||||
G1Stadium => localization.G1Stadium,
|
||||
G1Type1Fail => localization.G1Type1Fail,
|
||||
G1Type2Fail => localization.G1Type2Fail,
|
||||
G1TypeMatch1 => localization.G1TypeMatch1,
|
||||
G1TypeMatch2 => localization.G1TypeMatch2,
|
||||
G1TypeMatchPorygon => localization.G1TypeMatchPorygon,
|
||||
G1TypePorygonFail => localization.G1TypePorygonFail,
|
||||
G1TypePorygonFail1 => localization.G1TypePorygonFail1,
|
||||
G1TypePorygonFail2 => localization.G1TypePorygonFail2,
|
||||
G2InvalidTileTreeNotFound => localization.G2InvalidTileTreeNotFound,
|
||||
G2TreeID => localization.G2TreeID,
|
||||
G2OTGender => localization.G2OTGender,
|
||||
G3EReader => localization.G3EReader,
|
||||
G3OTGender => localization.G3OTGender,
|
||||
G4InvalidTileR45Surf => localization.G4InvalidTileR45Surf,
|
||||
G5IVAll30 => localization.G5IVAll30,
|
||||
G5PIDShinyGrotto => localization.G5PIDShinyGrotto,
|
||||
G5SparkleInvalid => localization.G5SparkleInvalid,
|
||||
G5SparkleRequired => localization.G5SparkleRequired,
|
||||
GanbaruStatLEQ_01 => localization.GanbaruStatTooHigh,
|
||||
GenderInvalidNone => localization.GenderInvalidNone,
|
||||
GeoBadOrder => localization.GeoBadOrder,
|
||||
GeoHardwareInvalid => localization.GeoHardwareInvalid,
|
||||
GeoHardwareRange => localization.GeoHardwareRange,
|
||||
GeoHardwareValid => localization.GeoHardwareValid,
|
||||
GeoMemoryMissing => localization.GeoMemoryMissing,
|
||||
GeoNoCountryHT => localization.GeoNoCountryHT,
|
||||
GeoNoRegion => localization.GeoNoRegion,
|
||||
HintEvolvesToSpecies_0 => localization.HintEvolvesToSpecies_0,
|
||||
HintEvolvesToRareForm_0 => localization.HintEvolvesToRareForm_0,
|
||||
ItemEgg => localization.ItemEgg,
|
||||
ItemUnreleased => localization.ItemUnreleased,
|
||||
LevelEXPThreshold => localization.LevelEXPThreshold,
|
||||
LevelEXPTooHigh => localization.LevelEXPTooHigh,
|
||||
LevelMetBelow => localization.LevelMetBelow,
|
||||
LevelMetGift => localization.LevelMetGift,
|
||||
LevelMetGiftFail => localization.LevelMetGiftFail,
|
||||
LevelMetSane => localization.LevelMetSane,
|
||||
|
||||
MemoryArgBadCatch_H => localization.MemoryArgBadCatch_H,
|
||||
MemoryArgBadHatch_H => localization.MemoryArgBadHatch_H,
|
||||
MemoryArgBadHT => localization.MemoryArgBadHT,
|
||||
MemoryArgBadID_H => localization.MemoryArgBadID_H,
|
||||
MemoryArgBadItem_H1 => localization.MemoryArgBadItem_H1,
|
||||
MemoryArgBadLocation_H => localization.MemoryArgBadLocation_H,
|
||||
MemoryArgBadMove_H1 => localization.MemoryArgBadMove_H1,
|
||||
MemoryArgBadOTEgg_H => localization.MemoryArgBadOTEgg_H,
|
||||
MemoryArgBadSpecies_H1 => localization.MemoryArgBadSpecies_H1,
|
||||
MemoryArgSpecies_H => localization.MemoryArgSpecies_H,
|
||||
MemoryCleared_H => localization.MemoryCleared_H,
|
||||
MemoryValid_H => localization.MemoryValid_H,
|
||||
MemoryFeelInvalid_H => localization.MemoryFeelInvalid_H,
|
||||
MemoryHTFlagInvalid => localization.MemoryHTFlagInvalid,
|
||||
MemoryHTGender_0 => localization.MemoryHTGender_0,
|
||||
MemoryHTLanguage => localization.MemoryHTLanguage,
|
||||
MemoryIndexArgHT => localization.MemoryIndexArgHT,
|
||||
MemoryIndexFeel_H1 => localization.MemoryIndexFeel_H1,
|
||||
MemoryIndexFeelHTLEQ9 => localization.MemoryIndexFeelHTLEQ9,
|
||||
MemoryIndexID_H1 => localization.MemoryIndexID_H1,
|
||||
MemoryIndexIntensity_H1 => localization.MemoryIndexIntensity_H1,
|
||||
MemoryIndexIntensityHT1 => localization.MemoryIndexIntensityHT1,
|
||||
MemoryIndexIntensityMin_H1 => localization.MemoryIndexIntensityMin_H1,
|
||||
MemoryIndexLinkHT => localization.MemoryIndexLinkHT,
|
||||
MemoryIndexVar_H1 => localization.MemoryIndexVar,
|
||||
MemoryMissingHT => localization.MemoryMissingHT,
|
||||
MemoryMissingOT => localization.MemoryMissingOT,
|
||||
MemorySocialZero => localization.MemorySocialZero,
|
||||
MemoryStatAffectionHT0 => localization.MemoryStatAffectionHT0,
|
||||
MemoryStatAffectionOT0 => localization.MemoryStatAffectionOT0,
|
||||
MemoryStatFriendshipHT0 => localization.MemoryStatFriendshipHT0,
|
||||
MemoryStatFriendshipOTBaseEvent_0 => localization.MemoryStatFriendshipOTBaseEvent_0,
|
||||
MemoryStatFullness_0 => localization.MemoryStatFullness_0,
|
||||
MemoryStatFullnessLEQ_0 => localization.MemoryStatFullnessLEQ_0,
|
||||
MemoryStatEnjoyment_0 => localization.MemoryStatEnjoyment_0,
|
||||
|
||||
MetDetailTimeOfDay => localization.MetDetailTimeOfDay,
|
||||
MoveKeldeoMismatch => localization.MoveKeldeoMismatch,
|
||||
MovesShouldMatchRelearnMoves => localization.MovesShouldMatchRelearnMoves,
|
||||
MoveShopAlphaMoveShouldBeMastered_0 => localization.MoveShopAlphaMoveShouldBeMastered_0,
|
||||
MoveShopAlphaMoveShouldBeOther => localization.MoveShopAlphaMoveShouldBeOther,
|
||||
MoveShopAlphaMoveShouldBeZero => localization.MoveShopAlphaMoveShouldBeZero,
|
||||
NickFlagEggNo => localization.NickFlagEggNo,
|
||||
NickFlagEggYes => localization.NickFlagEggYes,
|
||||
NickInvalidChar => localization.NickInvalidChar,
|
||||
NickLengthLong => localization.NickLengthLong,
|
||||
NickLengthShort => localization.NickLengthShort,
|
||||
NickMatchLanguage => localization.NickMatchLanguage,
|
||||
NickMatchLanguageEgg => localization.NickMatchLanguageEgg,
|
||||
NickMatchLanguageEggFail => localization.NickMatchLanguageEggFail,
|
||||
NickMatchLanguageFail => localization.NickMatchLanguageFail,
|
||||
NickMatchLanguageFlag => localization.NickMatchLanguageFlag,
|
||||
NickMatchNoOthers => localization.NickMatchNoOthers,
|
||||
NickMatchNoOthersFail => localization.NickMatchNoOthersFail,
|
||||
OTLanguage => localization.OTLanguage,
|
||||
OTLanguageShouldBe_0 => localization.OTLanguageShouldBe_0,
|
||||
OTLanguageShouldBe_0or1 => localization.OTLanguageShouldBe_0or1,
|
||||
OTLanguageShouldBeLeq_0 => localization.OTLanguageShouldBeLeq_0,
|
||||
OTLanguageCannotPlayOnVersion_0 => localization.OTLanguageCannotPlayOnVersion_0,
|
||||
OTLanguageCannotTransferToConsoleRegion_0 => localization.OTLanguageCannotTransferToConsoleRegion_0,
|
||||
OTLong => localization.OTLong,
|
||||
OTShort => localization.OTShort,
|
||||
OTSuspicious => localization.OTSuspicious,
|
||||
OT_IDEqual => localization.OT_IDEqual,
|
||||
OT_IDs0 => localization.OT_IDs0,
|
||||
OT_SID0 => localization.OT_SID0,
|
||||
OT_SID0Invalid => localization.OT_SID0Invalid,
|
||||
OT_TID0 => localization.OT_TID0,
|
||||
OT_IDInvalid => localization.OT_IDInvalid,
|
||||
PIDEncryptWurmple => localization.PIDEncryptWurmple,
|
||||
PIDEncryptZero => localization.PIDEncryptZero,
|
||||
PIDEqualsEC => localization.PIDEqualsEC,
|
||||
PIDGenderMatch => localization.PIDGenderMatch,
|
||||
PIDGenderMismatch => localization.PIDGenderMismatch,
|
||||
PIDNatureMatch => localization.PIDNatureMatch,
|
||||
PIDNatureMismatch => localization.PIDNatureMismatch,
|
||||
PIDTypeMismatch => localization.PIDTypeMismatch,
|
||||
PIDZero => localization.PIDZero,
|
||||
RibbonAllValid => localization.RibbonAllValid,
|
||||
RibbonEgg => localization.RibbonEgg,
|
||||
StatDynamaxInvalid => localization.StatDynamaxInvalid,
|
||||
StatIncorrectHeight => localization.StatIncorrectHeight,
|
||||
StatIncorrectHeightCopy => localization.StatIncorrectHeightCopy,
|
||||
StatIncorrectHeightValue => localization.StatIncorrectHeightValue,
|
||||
StatIncorrectWeight => localization.StatIncorrectWeight,
|
||||
StatIncorrectWeightValue => localization.StatIncorrectWeightValue,
|
||||
StatInvalidHeightWeight => localization.StatInvalidHeightWeight,
|
||||
StatIncorrectCP_0 => localization.StatIncorrectCP,
|
||||
StatGigantamaxInvalid => localization.StatGigantamaxInvalid,
|
||||
StatGigantamaxValid => localization.StatGigantamaxValid,
|
||||
StatNatureInvalid => localization.StatNatureInvalid,
|
||||
StatBattleVersionInvalid => localization.StatBattleVersionInvalid,
|
||||
StatNobleInvalid => localization.StatNobleInvalid,
|
||||
StatAlphaInvalid => localization.StatAlphaInvalid,
|
||||
StoredSourceEgg => localization.StoredSourceEgg,
|
||||
SuperComplete => localization.SuperComplete,
|
||||
SuperDistro => localization.SuperDistro,
|
||||
SuperEgg => localization.SuperEgg,
|
||||
SuperNoComplete => localization.SuperNoComplete,
|
||||
SuperNoUnlocked => localization.SuperNoUnlocked,
|
||||
SuperUnavailable => localization.SuperUnavailable,
|
||||
SuperUnused => localization.SuperUnused,
|
||||
TeraTypeIncorrect => localization.TeraTypeIncorrect,
|
||||
TeraTypeMismatch => localization.TeraTypeMismatch,
|
||||
TradeNotAvailable => localization.TradeNotAvailable,
|
||||
TrainerIDNoSeed => localization.TrainerIDNoSeed,
|
||||
TransferBad => localization.TransferBad,
|
||||
TransferCurrentHandlerInvalid => localization.TransferCurrentHandlerInvalid,
|
||||
TransferEgg => localization.TransferEgg,
|
||||
TransferEggLocationTransporter => localization.TransferEggLocationTransporter,
|
||||
TransferEggMetLevel => localization.TransferEggMetLevel,
|
||||
TransferEggVersion => localization.TransferEggVersion,
|
||||
TransferFlagIllegal => localization.TransferFlagIllegal,
|
||||
TransferHandlerFlagRequired => localization.TransferHTFlagRequired,
|
||||
TransferHandlerMismatchName => localization.TransferHTMismatchName,
|
||||
TransferHandlerMismatchGender => localization.TransferHTMismatchGender,
|
||||
TransferHandlerMismatchLanguage => localization.TransferHTMismatchLanguage,
|
||||
TransferMet => localization.TransferMet,
|
||||
TransferNotPossible => localization.TransferNotPossible,
|
||||
TransferMetLocation => localization.TransferMetLocation,
|
||||
TransferNature => localization.TransferNature,
|
||||
TransferObedienceLevel => localization.TransferObedienceLevel,
|
||||
TransferKoreanGen4 => localization.TransferKoreanGen4,
|
||||
TransferEncryptGen6BitFlip => localization.TransferPIDECBitFlip,
|
||||
TransferEncryptGen6Equals => localization.TransferPIDECEquals,
|
||||
TransferEncryptGen6Xor => localization.TransferPIDECXor,
|
||||
TransferTrackerMissing => localization.TransferTrackerMissing,
|
||||
TransferTrackerShouldBeZero => localization.TransferTrackerShouldBeZero,
|
||||
TrashBytesExpected => localization.TrashBytesExpected,
|
||||
TrashBytesMismatchInitial => localization.TrashBytesMismatchInitial,
|
||||
TrashBytesMissingTerminator => localization.TrashBytesMissingTerminator,
|
||||
TrashBytesShouldBeEmpty => localization.TrashBytesShouldBeEmpty,
|
||||
WordFilterInvalidCharacter_0 => localization.WordFilterInvalidCharacter_0,
|
||||
WordFilterFlaggedPattern_01 => localization.WordFilterFlaggedPattern_01,
|
||||
WordFilterTooManyNumbers_0 => localization.WordFilterTooManyNumbers_0,
|
||||
BulkCloneDetectedDetails => localization.BulkCloneDetectedDetails,
|
||||
BulkCloneDetectedTracker => localization.BulkCloneDetectedTracker,
|
||||
BulkSharingEncryptionConstantGenerationSame => localization.BulkSharingEncryptionConstantGenerationSame,
|
||||
BulkSharingEncryptionConstantGenerationDifferent => localization.BulkSharingEncryptionConstantGenerationDifferent,
|
||||
BulkSharingEncryptionConstantEncounterType => localization.BulkSharingEncryptionConstantRNGType,
|
||||
BulkSharingPIDGenerationDifferent => localization.BulkSharingPIDGenerationDifferent,
|
||||
BulkSharingPIDGenerationSame => localization.BulkSharingPIDGenerationSame,
|
||||
BulkSharingPIDEncounterType => localization.BulkSharingPIDRNGType,
|
||||
BulkDuplicateMysteryGiftEggReceived => localization.BulkDuplicateMysteryGiftEggReceived,
|
||||
BulkSharingTrainerIDs => localization.BulkSharingTrainerID,
|
||||
BulkSharingTrainerVersion => localization.BulkSharingTrainerVersion,
|
||||
|
||||
>= MAX => throw new ArgumentOutOfRangeException(nameof(code), code, null),
|
||||
};
|
||||
}
|
||||
211
PKHeX.Core/Legality/Localization/LegalityLocalizationContext.cs
Normal file
211
PKHeX.Core/Legality/Localization/LegalityLocalizationContext.cs
Normal file
@@ -0,0 +1,211 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public sealed class LegalityLocalizationSet
|
||||
{
|
||||
private static readonly Dictionary<string, LegalityLocalizationSet> Cache = new();
|
||||
|
||||
public required LegalityCheckLocalization Lines { get; init; }
|
||||
public required GameStrings Strings { get; init; }
|
||||
public required EncounterDisplayLocalization Encounter { get; init; }
|
||||
public required MoveSourceLocalization Moves { get; init; }
|
||||
public required GeneralLocalization General { get; init; }
|
||||
|
||||
public static LegalityLocalizationSet GetLocalization(LanguageID language) => GetLocalization(language.GetLanguageCode());
|
||||
|
||||
/// <summary>
|
||||
/// Gets the localization for the requested language.
|
||||
/// </summary>
|
||||
/// <param name="language">Language code</param>
|
||||
public static LegalityLocalizationSet GetLocalization(string language)
|
||||
{
|
||||
if (Cache.TryGetValue(language, out var result))
|
||||
return result;
|
||||
|
||||
result = new LegalityLocalizationSet
|
||||
{
|
||||
Strings = GameInfo.GetStrings(language),
|
||||
Lines = LegalityCheckLocalization.Get(language),
|
||||
Encounter = EncounterDisplayLocalization.Get(language),
|
||||
Moves = MoveSourceLocalization.Get(language),
|
||||
General = GeneralLocalization.Get(language),
|
||||
};
|
||||
Cache[language] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force loads all localizations.
|
||||
/// </summary>
|
||||
public static bool ForceLoadAll()
|
||||
{
|
||||
bool anyLoaded = false;
|
||||
foreach (var lang in GameLanguage.AllSupportedLanguages)
|
||||
{
|
||||
if (Cache.ContainsKey(lang))
|
||||
continue;
|
||||
_ = GetLocalization(lang);
|
||||
anyLoaded = true;
|
||||
}
|
||||
return anyLoaded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all localizations.
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<string, LegalityLocalizationSet> GetAll()
|
||||
{
|
||||
_ = ForceLoadAll();
|
||||
return Cache;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly ref struct LegalityLocalizationContext
|
||||
{
|
||||
public required LegalityAnalysis Analysis { get; init; }
|
||||
public required LegalityLocalizationSet Settings { get; init; }
|
||||
|
||||
public GameStrings Strings => Settings.Strings;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a complete <see cref="LegalityLocalizationContext"/> with proper localization initialization.
|
||||
/// </summary>
|
||||
/// <param name="la">Legality analysis</param>
|
||||
/// <param name="settings">Export settings</param>
|
||||
/// <returns>Fully initialized localization context</returns>
|
||||
public static LegalityLocalizationContext Create(LegalityAnalysis la, LegalityLocalizationSet settings) => new()
|
||||
{
|
||||
Analysis = la,
|
||||
Settings = settings,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Creates a complete <see cref="LegalityLocalizationContext"/> using the specified language.
|
||||
/// </summary>
|
||||
/// <param name="la">Legality analysis</param>
|
||||
/// <param name="language">Language code</param>
|
||||
/// <returns>Fully initialized localization context</returns>
|
||||
public static LegalityLocalizationContext Create(LegalityAnalysis la, string language = GameLanguage.DefaultLanguage)
|
||||
=> Create(la, LegalityLocalizationSet.GetLocalization(language));
|
||||
|
||||
public string GetRibbonMessage() => RibbonVerifier.GetMessage(Analysis, Strings.Ribbons, Settings.Lines);
|
||||
public string GetStatName(int displayIndex) => GetSafe(Settings.General.StatNames, displayIndex);
|
||||
public string GetMoveName(ushort move) => GetSafe(Strings.movelist, move);
|
||||
public string GetSpeciesName(ushort species) => GetSafe(Strings.specieslist, species);
|
||||
public string GetConsoleRegion3DS(int index) => GetSafe(Strings.console3ds, index);
|
||||
public string GetRibbonName(int index) => GetSafe(Strings.ribbons, index);
|
||||
public string GetLanguageName(int index) => GetSafe(Strings.languageNames, index);
|
||||
|
||||
private static string GetSafe(ReadOnlySpan<string> arr, int index)
|
||||
{
|
||||
if ((uint)index >= arr.Length)
|
||||
return string.Empty;
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
public string GetTrainer(int index) => index switch
|
||||
{
|
||||
0 => Settings.General.OriginalTrainer,
|
||||
1 => Settings.General.HandlingTrainer,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(index), $"Invalid Trainer argument: {index}"),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="LegalityCheckResultCode"/> to its corresponding localized string,
|
||||
/// applying formatting with the provided argument if needed.
|
||||
/// </summary>
|
||||
/// <param name="chk">Raw check value for formatting the string.</param>
|
||||
/// <param name="verbose">Include Identifier</param>
|
||||
/// <returns>The localized string from <see cref="LegalityCheckLocalization"/>, with formatting applied if needed</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the enum value doesn't have a corresponding string</exception>
|
||||
public string Humanize(in CheckResult chk, bool verbose = false)
|
||||
{
|
||||
var str = GetInternalString(chk);
|
||||
var format = Settings.Lines.F0_1;
|
||||
if (verbose)
|
||||
str = string.Format(format, chk.Identifier.ToString(), str);
|
||||
return string.Format(format, Description(chk.Judgement), str);
|
||||
}
|
||||
|
||||
public string Description(Severity s) => s switch
|
||||
{
|
||||
Severity.Invalid => Settings.Lines.SInvalid,
|
||||
Severity.Fishy => Settings.Lines.SFishy,
|
||||
Severity.Valid => Settings.Lines.SValid,
|
||||
_ => Settings.Lines.NotImplemented,
|
||||
};
|
||||
|
||||
private string GetInternalString(CheckResult chk)
|
||||
{
|
||||
var code = chk.Result;
|
||||
var template = code.GetTemplate(Settings.Lines);
|
||||
if (code < FirstWithArgument)
|
||||
return template;
|
||||
if (code.IsArgument())
|
||||
return string.Format(template, chk.Argument);
|
||||
if (code.IsMove())
|
||||
return string.Format(template, GetMoveName(chk.Argument));
|
||||
if (code.IsLanguage())
|
||||
return string.Format(template, GetLanguageName(chk.Argument), GetLanguageName(Analysis.Entity.Language));
|
||||
if (code.IsMemory())
|
||||
return GetMemory(chk, template, code);
|
||||
|
||||
// Complex codes may require additional context or arguments.
|
||||
return GetComplex(chk, template, code);
|
||||
}
|
||||
|
||||
private string GetMemory(CheckResult chk, string template, LegalityCheckResultCode code)
|
||||
{
|
||||
if (code is < FirstMemoryWithValue)
|
||||
return string.Format(template, chk.Value);
|
||||
if (code is MemoryArgBadItem_H1)
|
||||
return string.Format(template, GetTrainer(chk.Argument), GetSpeciesName(chk.Argument2));
|
||||
if (code is MemoryArgBadMove_H1)
|
||||
return string.Format(template, GetTrainer(chk.Argument), GetMoveName(chk.Argument2));
|
||||
if (code is MemoryArgBadSpecies_H1)
|
||||
return string.Format(template, GetTrainer(chk.Argument), GetSpeciesName(chk.Argument2));
|
||||
return string.Format(template, GetTrainer(chk.Argument), chk.Argument2);
|
||||
}
|
||||
|
||||
private string GetComplex(CheckResult chk, string format, LegalityCheckResultCode code) => code switch
|
||||
{
|
||||
< FirstComplex => format, // why are you even here?
|
||||
RibbonFInvalid_0 => string.Format(format, GetRibbonMessage()),
|
||||
WordFilterFlaggedPattern_01 => string.Format(format, (WordFilterType)chk.Argument, WordFilter.GetPattern((WordFilterType)chk.Argument, chk.Argument2)),
|
||||
WordFilterInvalidCharacter_0 => string.Format(format, chk.Argument.ToString("X4")),
|
||||
|
||||
AwakenedStatGEQ_01 => string.Format(format, chk.Argument, GetStatName(chk.Argument2)),
|
||||
GanbaruStatLEQ_01 => string.Format(format, chk.Argument, GetStatName(chk.Argument2)),
|
||||
|
||||
OTLanguageCannotTransferToConsoleRegion_0 => string.Format(format, GetConsoleRegion3DS(chk.Argument)),
|
||||
EncTradeShouldHaveEvolvedToSpecies_0 => string.Format(format, GetSpeciesName(chk.Argument)),
|
||||
MoveEvoFCombination_0 => string.Format(format, GetSpeciesName(chk.Argument)),
|
||||
HintEvolvesToSpecies_0 => string.Format(format, GetSpeciesName(chk.Argument)),
|
||||
|
||||
RibbonMarkingInvalid_0 => string.Format(format, GetRibbonName(chk.Argument)),
|
||||
RibbonMarkingAffixed_0 => string.Format(format, GetRibbonName(chk.Argument)),
|
||||
RibbonMissing_0 => string.Format(format, GetRibbonName(chk.Argument)),
|
||||
|
||||
StoredSlotSourceInvalid_0 => string.Format(format, (StorageSlotSource)chk.Argument),
|
||||
HintEvolvesToRareForm_0 => string.Format(format, chk.Argument == 1),
|
||||
|
||||
OTLanguageShouldBe_0or1 => string.Format(format, GetLanguageName(chk.Argument), GetLanguageName(chk.Argument2), GetLanguageName(Analysis.Entity.Language)),
|
||||
|
||||
>= MAX => throw new ArgumentOutOfRangeException(nameof(code), code, null),
|
||||
};
|
||||
|
||||
public string FormatMove(in MoveResult move, int index, byte currentFormat)
|
||||
{
|
||||
var result = Format(move, index, Settings.Moves.FormatMove);
|
||||
var gen = move.Generation;
|
||||
if (currentFormat != gen && gen != 0)
|
||||
result += $" [Gen{gen}]";
|
||||
return result;
|
||||
}
|
||||
|
||||
public string FormatRelearn(in MoveResult move, int index) => Format(move, index, Settings.Moves.FormatRelearn);
|
||||
private string Format(in MoveResult move, int index, string format) => string.Format(format, Description(move.Judgement), index, move.Summary(this));
|
||||
}
|
||||
45
PKHeX.Core/Legality/Localization/MoveSourceLocalization.cs
Normal file
45
PKHeX.Core/Legality/Localization/MoveSourceLocalization.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Localization strings for move learning source information.
|
||||
/// </summary>
|
||||
public sealed class MoveSourceLocalization
|
||||
{
|
||||
private static readonly MoveSourceLocalizationContext Context = new(LocalizationStorage<MoveSourceLocalization>.Options);
|
||||
public static readonly LocalizationStorage<MoveSourceLocalization> Cache = new("movesource", Context.MoveSourceLocalization);
|
||||
public static MoveSourceLocalization Get(string language = GameLanguage.DefaultLanguage) => Cache.Get(language);
|
||||
public static MoveSourceLocalization Get(LanguageID language) => Cache.Get(language.GetLanguageCode());
|
||||
|
||||
/// <summary>Format text for exporting a legality check result for a Move.</summary>
|
||||
public required string FormatMove { get; init; } = "{0} Move {1}: {2}";
|
||||
|
||||
/// <summary>Format text for exporting a legality check result for a Relearn Move.</summary>
|
||||
public required string FormatRelearn { get; init; } = "{0} Relearn Move {1}: {2}";
|
||||
|
||||
// Basic source types
|
||||
public required string SourceDefault { get; init; } = "Default move.";
|
||||
public required string SourceDuplicate { get; init; } = "Duplicate Move.";
|
||||
public required string SourceEmpty { get; init; } = "Empty Move.";
|
||||
public required string SourceInvalid { get; init; } = "Invalid Move.";
|
||||
public required string SourceLevelUp { get; init; } = "Learned by Level-up.";
|
||||
public required string SourceRelearn { get; init; } = "Relearnable Move.";
|
||||
public required string SourceSpecial { get; init; } = "Special Non-Relearn Move.";
|
||||
public required string SourceTMHM { get; init; } = "Learned by TM/HM.";
|
||||
public required string SourceTutor { get; init; } = "Learned by Move Tutor.";
|
||||
public required string SourceShared { get; init; } = "Shared Non-Relearn Move.";
|
||||
|
||||
// Egg-related sources
|
||||
public required string RelearnEgg { get; init; } = "Base Egg move.";
|
||||
public required string EggInherited { get; init; } = "Inherited Egg move.";
|
||||
public required string EggTMHM { get; init; } = "Inherited TM/HM move.";
|
||||
public required string EggInheritedTutor { get; init; } = "Inherited tutor move.";
|
||||
public required string EggInvalid { get; init; } = "Not an expected Egg move.";
|
||||
public required string EggLevelUp { get; init; } = "Inherited move learned by Level-up.";
|
||||
}
|
||||
|
||||
[JsonSerializable(typeof(MoveSourceLocalization))]
|
||||
public sealed partial class MoveSourceLocalizationContext : JsonSerializerContext;
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LearnMethod;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckLocalization;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -94,62 +94,62 @@ public static class EggSourceUtil
|
||||
/// <summary>
|
||||
/// Unboxes the parse result and returns a user-friendly string for the move result.
|
||||
/// </summary>
|
||||
public static string GetSourceString(Array parse, byte generation, int index)
|
||||
public static string GetSourceString(Array parse, byte generation, int index, MoveSourceLocalization loc)
|
||||
{
|
||||
if (index >= parse.Length)
|
||||
return LMoveSourceEmpty;
|
||||
return loc.SourceEmpty;
|
||||
|
||||
return generation switch
|
||||
{
|
||||
2 => ((EggSource2[])parse)[index].GetSourceString(),
|
||||
3 or 4 => ((EggSource34[])parse)[index].GetSourceString(),
|
||||
5 => ((EggSource5[])parse)[index].GetSourceString(),
|
||||
>= 6 => ((EggSource6[])parse)[index].GetSourceString(),
|
||||
_ => LMoveSourceEmpty,
|
||||
2 => ((EggSource2[])parse)[index].GetSourceString(loc),
|
||||
3 or 4 => ((EggSource34[])parse)[index].GetSourceString(loc),
|
||||
5 => ((EggSource5[])parse)[index].GetSourceString(loc),
|
||||
>= 6 => ((EggSource6[])parse)[index].GetSourceString(loc),
|
||||
_ => loc.SourceEmpty,
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetSourceString(this EggSource2 source) => source switch
|
||||
private static string GetSourceString(this EggSource2 source, MoveSourceLocalization loc) => source switch
|
||||
{
|
||||
EggSource2.Base => LMoveRelearnEgg,
|
||||
EggSource2.FatherEgg => LMoveEggInherited,
|
||||
EggSource2.FatherTM => LMoveEggTMHM,
|
||||
EggSource2.ParentLevelUp => LMoveEggLevelUp,
|
||||
EggSource2.Tutor => LMoveEggInheritedTutor,
|
||||
EggSource2.Base => loc.RelearnEgg,
|
||||
EggSource2.FatherEgg => loc.EggInherited,
|
||||
EggSource2.FatherTM => loc.EggTMHM,
|
||||
EggSource2.ParentLevelUp => loc.EggLevelUp,
|
||||
EggSource2.Tutor => loc.EggInheritedTutor,
|
||||
EggSource2.Max => "Any",
|
||||
_ => LMoveEggInvalid,
|
||||
_ => loc.EggInvalid,
|
||||
};
|
||||
|
||||
private static string GetSourceString(this EggSource34 source) => source switch
|
||||
private static string GetSourceString(this EggSource34 source, MoveSourceLocalization loc) => source switch
|
||||
{
|
||||
EggSource34.Base => LMoveRelearnEgg,
|
||||
EggSource34.FatherEgg => LMoveEggInherited,
|
||||
EggSource34.FatherTM => LMoveEggTMHM,
|
||||
EggSource34.ParentLevelUp => LMoveEggLevelUp,
|
||||
EggSource34.Base => loc.RelearnEgg,
|
||||
EggSource34.FatherEgg => loc.EggInherited,
|
||||
EggSource34.FatherTM => loc.EggTMHM,
|
||||
EggSource34.ParentLevelUp => loc.EggLevelUp,
|
||||
EggSource34.Max => "Any",
|
||||
EggSource34.VoltTackle => LMoveSourceSpecial,
|
||||
_ => LMoveEggInvalid,
|
||||
EggSource34.VoltTackle => loc.SourceSpecial,
|
||||
_ => loc.EggInvalid,
|
||||
};
|
||||
|
||||
private static string GetSourceString(this EggSource5 source) => source switch
|
||||
private static string GetSourceString(this EggSource5 source, MoveSourceLocalization loc) => source switch
|
||||
{
|
||||
EggSource5.Base => LMoveRelearnEgg,
|
||||
EggSource5.FatherEgg => LMoveEggInherited,
|
||||
EggSource5.ParentLevelUp => LMoveEggLevelUp,
|
||||
EggSource5.FatherTM => LMoveEggTMHM,
|
||||
EggSource5.Base => loc.RelearnEgg,
|
||||
EggSource5.FatherEgg => loc.EggInherited,
|
||||
EggSource5.ParentLevelUp => loc.EggLevelUp,
|
||||
EggSource5.FatherTM => loc.EggTMHM,
|
||||
EggSource5.Max => "Any",
|
||||
EggSource5.VoltTackle => LMoveSourceSpecial,
|
||||
_ => LMoveEggInvalid,
|
||||
EggSource5.VoltTackle => loc.SourceSpecial,
|
||||
_ => loc.EggInvalid,
|
||||
};
|
||||
|
||||
private static string GetSourceString(this EggSource6 source) => source switch
|
||||
private static string GetSourceString(this EggSource6 source, MoveSourceLocalization loc) => source switch
|
||||
{
|
||||
EggSource6.Base => LMoveRelearnEgg,
|
||||
EggSource6.ParentLevelUp => LMoveEggLevelUp,
|
||||
EggSource6.ParentEgg => LMoveEggInherited,
|
||||
EggSource6.Base => loc.RelearnEgg,
|
||||
EggSource6.ParentLevelUp => loc.EggLevelUp,
|
||||
EggSource6.ParentEgg => loc.EggInherited,
|
||||
EggSource6.Max => "Any",
|
||||
EggSource6.VoltTackle => LMoveSourceSpecial,
|
||||
_ => LMoveEggInvalid,
|
||||
EggSource6.VoltTackle => loc.SourceSpecial,
|
||||
_ => loc.EggInvalid,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -36,6 +36,8 @@ internal static class EvolutionRestrictions
|
||||
_ => NONE,
|
||||
};
|
||||
|
||||
public static bool IsEvolvedSpeciesFormRare(uint encryptionConstant) => encryptionConstant % 100 is 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the species-form that it will evolve into.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LanguageID;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -8,6 +9,14 @@ namespace PKHeX.Core;
|
||||
/// <remarks>These values were specific to the 3DS games (Generations 6 and 7, excluding LGP/E)</remarks>
|
||||
public static class Locale3DS
|
||||
{
|
||||
/// <summary>
|
||||
/// List of defined Console Regions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 3 was reserved for AUS, but was later merged into Europe, so it is not a valid locale code.
|
||||
/// </remarks>
|
||||
public static ReadOnlySpan<byte> DefinedLocales => [0, 1, 2, /*3,*/ 4, 5, 6];
|
||||
|
||||
/// <summary>
|
||||
/// Compares the <see cref="IRegionOrigin.ConsoleRegion"/> and <see cref="IRegionOrigin.Country"/> to determine if the country is available within that region.
|
||||
/// </summary>
|
||||
|
||||
@@ -25,5 +25,5 @@ public abstract class MemoryContext
|
||||
|
||||
public abstract bool CanHaveIntensity(byte memory, byte intensity);
|
||||
public abstract bool CanHaveFeeling(byte memory, byte feeling, ushort argument);
|
||||
public abstract int GetMinimumIntensity(byte memory);
|
||||
public abstract byte GetMinimumIntensity(byte memory);
|
||||
}
|
||||
|
||||
@@ -154,5 +154,5 @@ public static byte GetMinimumIntensity6(int memory)
|
||||
|
||||
public override bool CanHaveIntensity(byte memory, byte intensity) => CanHaveIntensity6(memory, intensity);
|
||||
public override bool CanHaveFeeling(byte memory, byte feeling, ushort argument) => CanHaveFeeling6(memory, feeling, argument);
|
||||
public override int GetMinimumIntensity(byte memory) => GetMinimumIntensity6(memory);
|
||||
public override byte GetMinimumIntensity(byte memory) => GetMinimumIntensity6(memory);
|
||||
}
|
||||
|
||||
@@ -238,5 +238,5 @@ public static byte GetMinimumIntensity8(int memory)
|
||||
|
||||
public override bool CanHaveIntensity(byte memory, byte intensity) => CanHaveIntensity8(memory, intensity);
|
||||
public override bool CanHaveFeeling(byte memory, byte feeling, ushort argument) => CanHaveFeeling8(memory, feeling, argument);
|
||||
public override int GetMinimumIntensity(byte memory) => GetMinimumIntensity8(memory);
|
||||
public override byte GetMinimumIntensity(byte memory) => GetMinimumIntensity8(memory);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
@@ -10,12 +8,12 @@ namespace PKHeX.Core;
|
||||
/// <param name="Variable">Argument for the memory</param>
|
||||
/// <param name="Intensity">How strongly they remember the memory</param>
|
||||
/// <param name="Feeling">How they feel about the memory</param>
|
||||
public readonly record struct MemoryVariableSet(string Handler, byte MemoryID, ushort Variable, byte Intensity, byte Feeling)
|
||||
public readonly record struct MemoryVariableSet(byte Handler, byte MemoryID, ushort Variable, byte Intensity, byte Feeling)
|
||||
{
|
||||
public static MemoryVariableSet Read(ITrainerMemories pk, int handler) => handler switch
|
||||
{
|
||||
0 => new(L_XOT, pk.OriginalTrainerMemory, pk.OriginalTrainerMemoryVariable, pk.OriginalTrainerMemoryIntensity, pk.OriginalTrainerMemoryFeeling), // OT
|
||||
1 => new(L_XHT, pk.HandlingTrainerMemory, pk.HandlingTrainerMemoryVariable, pk.HandlingTrainerMemoryIntensity, pk.HandlingTrainerMemoryFeeling), // HT
|
||||
_ => new(L_XOT, 0, 0, 0, 0),
|
||||
0 => new(0, pk.OriginalTrainerMemory, pk.OriginalTrainerMemoryVariable, pk.OriginalTrainerMemoryIntensity, pk.OriginalTrainerMemoryFeeling), // OT
|
||||
1 => new(1, pk.HandlingTrainerMemory, pk.HandlingTrainerMemoryVariable, pk.HandlingTrainerMemoryIntensity, pk.HandlingTrainerMemoryFeeling), // HT
|
||||
_ => new(0, 0, 0, 0, 0),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -33,7 +32,7 @@ internal static Regex[] LoadPatterns(ReadOnlySpan<char> patterns)
|
||||
/// <param name="regexes">Console regex set to check against.</param>
|
||||
/// <param name="regMatch">Matching regex that filters the phrase.</param>
|
||||
/// <returns>Boolean result if the message is filtered or not.</returns>
|
||||
internal static bool TryMatch(ReadOnlySpan<char> message, ReadOnlySpan<Regex> regexes, [NotNullWhen(true)] out string? regMatch)
|
||||
internal static bool TryMatch(ReadOnlySpan<char> message, ReadOnlySpan<Regex> regexes, out int regMatch)
|
||||
{
|
||||
// Clean the string
|
||||
Span<char> clean = stackalloc char[message.Length];
|
||||
@@ -41,76 +40,114 @@ internal static bool TryMatch(ReadOnlySpan<char> message, ReadOnlySpan<Regex> re
|
||||
if (ctr != clean.Length)
|
||||
clean = clean[..ctr];
|
||||
|
||||
foreach (var regex in regexes)
|
||||
for (var i = 0; i < regexes.Length; i++)
|
||||
{
|
||||
var regex = regexes[i];
|
||||
foreach (var _ in regex.EnumerateMatches(clean))
|
||||
{
|
||||
regMatch = regex.ToString();
|
||||
regMatch = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
regMatch = null;
|
||||
|
||||
regMatch = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IsFiltered(ReadOnlySpan{char}, out string?, EntityContext, EntityContext)"/>
|
||||
public static bool IsFiltered(ReadOnlySpan<char> message, [NotNullWhen(true)] out string? regMatch,
|
||||
EntityContext current)
|
||||
=> IsFiltered(message, out regMatch, current, current);
|
||||
/// <inheritdoc cref="IsFiltered(ReadOnlySpan{char}, EntityContext, EntityContext, out WordFilterType, out int)"/>
|
||||
public static bool IsFiltered(ReadOnlySpan<char> message, EntityContext current, out WordFilterType type, out int regMatch)
|
||||
=> IsFiltered(message, current, current, out type, out regMatch);
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a phrase contains filtered content.
|
||||
/// </summary>
|
||||
/// <param name="message">Phrase to check for</param>
|
||||
/// <param name="regMatch">Matching regex that filters the phrase.</param>
|
||||
/// <param name="current">Current context to check.</param>
|
||||
/// <param name="original">Earliest context to check.</param>
|
||||
/// <param name="type">Word filter set that matched the phrase.</param>
|
||||
/// <param name="regMatch">Matching regex that filters the phrase.</param>
|
||||
/// <returns>Boolean result if the message is filtered or not.</returns>
|
||||
public static bool IsFiltered(ReadOnlySpan<char> message, [NotNullWhen(true)] out string? regMatch,
|
||||
EntityContext current, EntityContext original)
|
||||
public static bool IsFiltered(ReadOnlySpan<char> message, EntityContext current, EntityContext original, out WordFilterType type, out int regMatch)
|
||||
{
|
||||
regMatch = null;
|
||||
regMatch = -1;
|
||||
if (message.IsWhiteSpace() || message.Length <= 1)
|
||||
{
|
||||
type = WordFilterType.None;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only check against the single filter if requested
|
||||
if (ParseSettings.Settings.WordFilter.DisableWordFilterPastGen)
|
||||
return IsFilteredCurrentOnly(message, ref regMatch, current, original);
|
||||
{
|
||||
if (IsFilteredCurrentOnly(message, current, original, out regMatch))
|
||||
{
|
||||
type = WordFilterTypeExtensions.GetName(current);
|
||||
return true;
|
||||
}
|
||||
type = WordFilterType.None;
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsFilteredLookBack(message, out regMatch, current, original);
|
||||
return IsFilteredLookBack(message, current, original, out type, out regMatch);
|
||||
}
|
||||
|
||||
private static bool IsFilteredCurrentOnly(ReadOnlySpan<char> message, [NotNullWhen(true)] ref string? regMatch,
|
||||
EntityContext current, EntityContext original) => current switch
|
||||
private static bool IsFilteredCurrentOnly(ReadOnlySpan<char> message, EntityContext current, EntityContext original, out int regMatch)
|
||||
{
|
||||
EntityContext.Gen5 => WordFilter5.IsFiltered(message, out regMatch),
|
||||
|
||||
EntityContext.Gen6 => WordFilter3DS.IsFilteredGen6(message, out regMatch),
|
||||
EntityContext.Gen7 when original is EntityContext.Gen6
|
||||
=> WordFilter3DS.IsFilteredGen6(message, out regMatch),
|
||||
|
||||
EntityContext.Gen7 => WordFilter3DS.IsFilteredGen7(message, out regMatch),
|
||||
_ => current.GetConsole() switch
|
||||
regMatch = 0;
|
||||
return current switch
|
||||
{
|
||||
GameConsole.NX => WordFilterNX.IsFiltered(message, out regMatch, original),
|
||||
_ => false,
|
||||
},
|
||||
};
|
||||
EntityContext.Gen5 => WordFilter5.IsFiltered(message, out regMatch),
|
||||
|
||||
private static bool IsFilteredLookBack(ReadOnlySpan<char> message, [NotNullWhen(true)] out string? regMatch,
|
||||
EntityContext current, EntityContext original)
|
||||
EntityContext.Gen6 => WordFilter3DS.IsFilteredGen6(message, out regMatch),
|
||||
EntityContext.Gen7 when original is EntityContext.Gen6
|
||||
=> WordFilter3DS.IsFilteredGen6(message, out regMatch),
|
||||
|
||||
EntityContext.Gen7 => WordFilter3DS.IsFilteredGen7(message, out regMatch),
|
||||
_ => current.GetConsole() switch
|
||||
{
|
||||
GameConsole.NX => WordFilterNX.IsFiltered(message, out regMatch, original),
|
||||
_ => false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsFilteredLookBack(ReadOnlySpan<char> message, EntityContext current, EntityContext original, out WordFilterType type, out int regMatch)
|
||||
{
|
||||
// Switch 2 backwards transfer? Won't know for another couple years.
|
||||
if (WordFilterNX.IsFiltered(message, out regMatch, original))
|
||||
{
|
||||
type = WordFilterType.NintendoSwitch;
|
||||
return true;
|
||||
}
|
||||
|
||||
var generation = original.Generation();
|
||||
if (generation > 7 || original is EntityContext.Gen7b)
|
||||
{
|
||||
type = WordFilterType.None;
|
||||
return false;
|
||||
if (WordFilter3DS.IsFiltered(message, out regMatch, original))
|
||||
return true;
|
||||
}
|
||||
|
||||
return generation == 5 && WordFilter5.IsFiltered(message, out regMatch);
|
||||
if (WordFilter3DS.IsFiltered(message, out regMatch, original))
|
||||
{
|
||||
type = WordFilterType.Nintendo3DS;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (generation == 5 && WordFilter5.IsFiltered(message, out regMatch))
|
||||
{
|
||||
type = WordFilterType.Gen5;
|
||||
return true;
|
||||
}
|
||||
// no other word filters (none in Gen3 or Gen4)
|
||||
type = WordFilterType.None;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string GetPattern(WordFilterType chkArgument, int index) => chkArgument switch
|
||||
{
|
||||
WordFilterType.Gen5 => WordFilter5.GetPattern(index),
|
||||
WordFilterType.Nintendo3DS => WordFilter3DS.GetPattern(index),
|
||||
WordFilterType.NintendoSwitch => WordFilterNX.GetPattern(index),
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -12,24 +11,25 @@ public static class WordFilter3DS
|
||||
{
|
||||
private static readonly Regex[] Regexes = WordFilter.LoadPatterns(Util.GetStringResource("badwords_3ds"));
|
||||
|
||||
public static string GetPattern(int index) => Regexes[index].ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Regex patterns to check against
|
||||
/// </summary>
|
||||
/// <remarks>No need to keep the original pattern strings around; the <see cref="Regex"/> object retrieves this via <see cref="Regex.ToString()"/></remarks>
|
||||
private static readonly ConcurrentDictionary<string, string?>.AlternateLookup<ReadOnlySpan<char>> Lookup =
|
||||
new ConcurrentDictionary<string, string?>().GetAlternateLookup<ReadOnlySpan<char>>();
|
||||
private static readonly ConcurrentDictionary<string, int>.AlternateLookup<ReadOnlySpan<char>> PreviouslyChecked =
|
||||
new ConcurrentDictionary<string, int>().GetAlternateLookup<ReadOnlySpan<char>>();
|
||||
|
||||
private const int MAX_COUNT = (1 << 17) - 1; // arbitrary cap for max dictionary size
|
||||
private const int NoMatch = -1;
|
||||
|
||||
/// <inheritdoc cref="IsFiltered"/>
|
||||
/// <remarks>Generation 6 is case-sensitive.</remarks>
|
||||
public static bool IsFilteredGen6(ReadOnlySpan<char> message, [NotNullWhen(true)] out string? regMatch)
|
||||
=> IsFiltered(message, out regMatch, EntityContext.Gen6);
|
||||
public static bool IsFilteredGen6(ReadOnlySpan<char> message, out int regMatch) => IsFiltered(message, out regMatch, EntityContext.Gen6);
|
||||
|
||||
/// <inheritdoc cref="IsFiltered"/>
|
||||
/// <remarks>Generation 7 is case-insensitive.</remarks>
|
||||
public static bool IsFilteredGen7(ReadOnlySpan<char> message, [NotNullWhen(true)] out string? regMatch)
|
||||
=> IsFiltered(message, out regMatch, EntityContext.Gen7);
|
||||
public static bool IsFilteredGen7(ReadOnlySpan<char> message, out int regMatch) => IsFiltered(message, out regMatch, EntityContext.Gen7);
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a phrase contains filtered content.
|
||||
@@ -38,27 +38,27 @@ public static bool IsFilteredGen7(ReadOnlySpan<char> message, [NotNullWhen(true)
|
||||
/// <param name="regMatch">Matching regex that filters the phrase.</param>
|
||||
/// <param name="original">Earliest context to check.</param>
|
||||
/// <returns>Boolean result if the message is filtered or not.</returns>
|
||||
public static bool IsFiltered(ReadOnlySpan<char> message, [NotNullWhen(true)] out string? regMatch, EntityContext original)
|
||||
public static bool IsFiltered(ReadOnlySpan<char> message, out int regMatch, EntityContext original)
|
||||
{
|
||||
regMatch = null;
|
||||
regMatch = NoMatch;
|
||||
if (IsSpeciesName(message, original))
|
||||
return false;
|
||||
|
||||
// Check dictionary
|
||||
if (Lookup.TryGetValue(message, out regMatch))
|
||||
return regMatch is not null;
|
||||
if (PreviouslyChecked.TryGetValue(message, out regMatch))
|
||||
return regMatch is not NoMatch;
|
||||
|
||||
// not in dictionary, check patterns
|
||||
if (WordFilter.TryMatch(message, Regexes, out regMatch))
|
||||
{
|
||||
Lookup.TryAdd(message, regMatch);
|
||||
PreviouslyChecked.TryAdd(message, regMatch);
|
||||
return true;
|
||||
}
|
||||
|
||||
// didn't match any pattern, cache result
|
||||
if ((Lookup.Dictionary.Count & ~MAX_COUNT) != 0)
|
||||
Lookup.Dictionary.Clear(); // reset
|
||||
Lookup.TryAdd(message, regMatch = null);
|
||||
if ((PreviouslyChecked.Dictionary.Count & ~MAX_COUNT) != 0)
|
||||
PreviouslyChecked.Dictionary.Clear(); // reset
|
||||
PreviouslyChecked.TryAdd(message, regMatch = NoMatch);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public static class WordFilter5
|
||||
{
|
||||
private static readonly HashSet<string>.AlternateLookup<ReadOnlySpan<char>> Words =
|
||||
new HashSet<string>(Util.GetStringList("badwords_gen5"))
|
||||
private static readonly string[] BadWords = Util.GetStringList("badwords_gen5");
|
||||
private static readonly Dictionary<string, int>.AlternateLookup<ReadOnlySpan<char>> Words = GetDictionary(BadWords)
|
||||
.GetAlternateLookup<ReadOnlySpan<char>>();
|
||||
|
||||
public static string GetPattern(int index) => BadWords[index];
|
||||
|
||||
private static Dictionary<string, int> GetDictionary(ReadOnlySpan<string> input)
|
||||
{
|
||||
var result = new Dictionary<string, int>(input.Length);
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
result[input[i]] = i;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a phrase contains filtered content.
|
||||
/// </summary>
|
||||
/// <param name="message">Phrase to check</param>
|
||||
/// <param name="match">Blocked word that filters the phrase.</param>
|
||||
/// <returns>Boolean result if the message is filtered or not.</returns>
|
||||
public static bool IsFiltered(ReadOnlySpan<char> message, [NotNullWhen(true)] out string? match)
|
||||
public static bool IsFiltered(ReadOnlySpan<char> message, out int match)
|
||||
{
|
||||
Span<char> clean = stackalloc char[message.Length];
|
||||
Normalize(message, clean);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -16,13 +15,16 @@ public static class WordFilterNX
|
||||
/// <remarks>No need to keep the original pattern strings around; the <see cref="Regex"/> object retrieves this via <see cref="Regex.ToString()"/></remarks>
|
||||
private static readonly Regex[] Regexes = WordFilter.LoadPatterns(Util.GetStringResource("badwords_switch"));
|
||||
|
||||
public static string GetPattern(int index) => Regexes[index].ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Due to some messages repeating (Trainer names), keep a list of repeated values for faster lookup.
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<string, string?>.AlternateLookup<ReadOnlySpan<char>> Lookup =
|
||||
new ConcurrentDictionary<string, string?>().GetAlternateLookup<ReadOnlySpan<char>>();
|
||||
private static readonly ConcurrentDictionary<string, int>.AlternateLookup<ReadOnlySpan<char>> Lookup =
|
||||
new ConcurrentDictionary<string, int>().GetAlternateLookup<ReadOnlySpan<char>>();
|
||||
|
||||
private const int MAX_COUNT = (1 << 17) - 1; // arbitrary cap for max dictionary size
|
||||
private const int NoMatch = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if a phrase contains filtered content.
|
||||
@@ -31,15 +33,15 @@ public static class WordFilterNX
|
||||
/// <param name="regMatch">Matching regex that filters the phrase.</param>
|
||||
/// <param name="original">Earliest context to check.</param>
|
||||
/// <returns>Boolean result if the message is filtered or not.</returns>
|
||||
public static bool IsFiltered(ReadOnlySpan<char> message, [NotNullWhen(true)] out string? regMatch, EntityContext original)
|
||||
public static bool IsFiltered(ReadOnlySpan<char> message, out int regMatch, EntityContext original)
|
||||
{
|
||||
regMatch = null;
|
||||
regMatch = NoMatch;
|
||||
if (IsSpeciesName(message, original))
|
||||
return false;
|
||||
|
||||
// Check dictionary
|
||||
if (Lookup.TryGetValue(message, out regMatch))
|
||||
return regMatch is not null;
|
||||
return regMatch is not NoMatch;
|
||||
|
||||
// not in dictionary, check patterns
|
||||
if (WordFilter.TryMatch(message, Regexes, out regMatch))
|
||||
@@ -51,7 +53,7 @@ public static bool IsFiltered(ReadOnlySpan<char> message, [NotNullWhen(true)] ou
|
||||
// didn't match any pattern, cache result
|
||||
if ((Lookup.Dictionary.Count & ~MAX_COUNT) != 0)
|
||||
Lookup.Dictionary.Clear(); // reset
|
||||
Lookup.TryAdd(message, regMatch = null);
|
||||
Lookup.TryAdd(message, regMatch = NoMatch);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Word filter contexts used by different <see cref="EntityContext"/>.
|
||||
/// </summary>
|
||||
public enum WordFilterType
|
||||
{
|
||||
/// <summary>
|
||||
/// No strict filtering is applied.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 5 word filter, used for games like Black/White and Black 2/White 2.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="WordFilter5"/>
|
||||
/// </remarks>
|
||||
Gen5,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 6 and 7 word filter, used for games like X/Y, Sun/Moon, Ultra Sun/Ultra Moon, and Sword/Shield.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See <see cref="WordFilter3DS"/>
|
||||
/// </remarks>
|
||||
Nintendo3DS,
|
||||
|
||||
/// <summary>
|
||||
/// Generation 8+ word filter.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See <see cref="WordFilterNX"/>
|
||||
/// </remarks>
|
||||
NintendoSwitch,
|
||||
}
|
||||
|
||||
public static class WordFilterTypeExtensions
|
||||
{
|
||||
public static WordFilterType GetName(EntityContext type) => type.GetConsole() switch
|
||||
{
|
||||
GameConsole.NX => WordFilterType.NintendoSwitch,
|
||||
_ => type.Generation() switch
|
||||
{
|
||||
5 => WordFilterType.Gen5,
|
||||
6 or 7 => WordFilterType.Nintendo3DS,
|
||||
_ => WordFilterType.None,
|
||||
},
|
||||
};
|
||||
|
||||
public static Type GetType(WordFilterType type) => type switch
|
||||
{
|
||||
WordFilterType.Gen5 => typeof(WordFilter5),
|
||||
WordFilterType.Nintendo3DS => typeof(WordFilter3DS),
|
||||
WordFilterType.NintendoSwitch => typeof(WordFilterNX),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, $"Invalid {nameof(WordFilterType)} value."),
|
||||
};
|
||||
}
|
||||
@@ -47,7 +47,6 @@ public static void Initialize(LegalitySettings settings)
|
||||
|
||||
public static IReadOnlyList<string> MoveStrings { get; private set; } = Util.GetMovesList(GameLanguage.DefaultLanguage);
|
||||
public static IReadOnlyList<string> SpeciesStrings { get; private set; } = Util.GetSpeciesList(GameLanguage.DefaultLanguage);
|
||||
public static string GetMoveName(ushort move) => move >= MoveStrings.Count ? LegalityCheckStrings.L_AError : MoveStrings[move];
|
||||
|
||||
public static void ChangeLocalizationStrings(IReadOnlyList<string> moves, IReadOnlyList<string> species)
|
||||
{
|
||||
|
||||
@@ -168,4 +168,9 @@ public enum CheckIdentifier : byte
|
||||
/// The <see cref="CheckResult"/> pertains to the <see cref="PKM"/> <see cref="StorageSlotType"/>.
|
||||
/// </summary>
|
||||
SlotType,
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="CheckResult"/> pertains to the Current Handler (not OT) of the <see cref="PKM"/> data.
|
||||
/// </summary>
|
||||
Handler,
|
||||
}
|
||||
|
||||
@@ -1,16 +1,51 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Result of a Legality Check
|
||||
/// </summary>
|
||||
[System.Diagnostics.DebuggerDisplay($"{{{nameof(Identifier)}}}: {{{nameof(Comment)}}}")]
|
||||
// ReSharper disable once NotAccessedPositionalProperty.Global
|
||||
public readonly record struct CheckResult(Severity Judgement, CheckIdentifier Identifier, string Comment)
|
||||
[System.Diagnostics.DebuggerDisplay($"{{{nameof(Identifier)}}}: {{{nameof(Result)}}}")]
|
||||
[StructLayout(LayoutKind.Explicit, Size = 8)]
|
||||
public readonly record struct CheckResult
|
||||
{
|
||||
/// <summary> Indicates whether the result is valid. </summary>
|
||||
public bool Valid => Judgement != Severity.Invalid;
|
||||
public string Rating => Judgement.Description();
|
||||
|
||||
internal CheckResult(CheckIdentifier i) : this(Severity.Valid, i, LegalityCheckStrings.L_AValid) { }
|
||||
/// <summary> Indicates if the result isn't a generic "Valid" result, and might be worth displaying to the user. </summary>
|
||||
public bool IsNotGeneric() => Result != LegalityCheckResultCode.Valid;
|
||||
|
||||
public string Format(string format) => string.Format(format, Rating, Comment);
|
||||
/// <summary> Indicates the severity of the result. </summary>
|
||||
[field: FieldOffset(0)] public required Severity Judgement { get; init; }
|
||||
|
||||
/// <summary> Identifier for the check group that produced this result. </summary>
|
||||
[field: FieldOffset(1)] public required CheckIdentifier Identifier { get; init; }
|
||||
|
||||
/// <summary> Result code for the check, indicating the analysis performed/flagged to arrive at the <see cref="Judgement"/>. </summary>
|
||||
[field: FieldOffset(2)] public required LegalityCheckResultCode Result { get; init; }
|
||||
|
||||
#region Hint Parameters used for Human-readable messages
|
||||
/// <summary> Raw value used for hints, or storing a 32-bit number hint. </summary>
|
||||
[field: FieldOffset(4)] public uint Value { get; init; }
|
||||
/// <summary> First argument used for hints, or storing a 16-bit number hint. </summary>
|
||||
[field: FieldOffset(4)] public ushort Argument { get; init; }
|
||||
/// <summary> Second argument used for hints, or storing a 16-bit number hint. </summary>
|
||||
[field: FieldOffset(6)] public ushort Argument2 { get; init; }
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Simple method to create a valid result with the given identifier.
|
||||
/// </summary>
|
||||
public static CheckResult GetValid(CheckIdentifier ident) => Get(Severity.Valid, ident, LegalityCheckResultCode.Valid);
|
||||
|
||||
/// <summary>
|
||||
/// Simple method to create a result with the given parameters.
|
||||
/// </summary>
|
||||
public static CheckResult Get(Severity judge, CheckIdentifier ident, LegalityCheckResultCode code, uint value = 0) => new()
|
||||
{
|
||||
Judgement = judge,
|
||||
Identifier = ident,
|
||||
Result = code,
|
||||
Value = value,
|
||||
};
|
||||
}
|
||||
|
||||
458
PKHeX.Core/Legality/Structures/LegalityCheckResultCode.cs
Normal file
458
PKHeX.Core/Legality/Structures/LegalityCheckResultCode.cs
Normal file
@@ -0,0 +1,458 @@
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the specific type of legality result that was generated during a legality check.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When a result is generated, instead of storing the string directly, an instance of this enum is stored instead.
|
||||
/// The string is only fetched from <see cref="LegalityCheckLocalization"/> when needed for display.
|
||||
/// </remarks>
|
||||
public enum LegalityCheckResultCode : ushort
|
||||
{
|
||||
// General Strings
|
||||
/// <summary>Default text for indicating validity.</summary>
|
||||
Valid,
|
||||
/// <summary>Default text for indicating an error has occurred.</summary>
|
||||
Error,
|
||||
|
||||
// The order of the enum is important only for humanization; keep sorted by handling, with groups by category and functionality.
|
||||
|
||||
// Ability
|
||||
AbilityCapsuleUsed,
|
||||
AbilityPatchUsed,
|
||||
AbilityPatchRevertUsed,
|
||||
AbilityFlag,
|
||||
AbilityHiddenFail,
|
||||
AbilityHiddenUnavailable,
|
||||
AbilityMismatch,
|
||||
AbilityMismatch3,
|
||||
AbilityMismatchFlag,
|
||||
AbilityMismatchGift,
|
||||
AbilityMismatchPID,
|
||||
AbilityUnexpected,
|
||||
|
||||
// Awakened Values
|
||||
AwakenedCap,
|
||||
|
||||
// Ball
|
||||
BallAbility,
|
||||
BallEggCherish,
|
||||
BallEggMaster,
|
||||
BallEnc,
|
||||
BallEncMismatch,
|
||||
BallHeavy,
|
||||
BallSpecies,
|
||||
BallSpeciesPass,
|
||||
BallUnavailable,
|
||||
|
||||
// Contest
|
||||
ContestZero,
|
||||
ContestZeroSheen,
|
||||
|
||||
// Date & Timestamps
|
||||
DateOutsideConsoleWindow,
|
||||
DateTimeClockInvalid,
|
||||
DateOutsideDistributionWindow,
|
||||
|
||||
// Egg
|
||||
EggContest,
|
||||
EggEXP,
|
||||
EggHatchCycles,
|
||||
EggLocation,
|
||||
EggLocationInvalid,
|
||||
EggLocationNone,
|
||||
EggLocationPalPark,
|
||||
EggLocationTrade,
|
||||
EggLocationTradeFail,
|
||||
EggMetLocationFail,
|
||||
EggNature,
|
||||
EggPokeathlon,
|
||||
EggPP,
|
||||
EggPPUp,
|
||||
EggRelearnFlags,
|
||||
EggShinyLeaf,
|
||||
EggShinyPokeStar,
|
||||
EggSpecies,
|
||||
EggUnhatched,
|
||||
|
||||
// Encounter
|
||||
EncCondition,
|
||||
EncConditionBadRNGFrame,
|
||||
EncConditionBadSpecies,
|
||||
EncGift,
|
||||
EncGiftEggEvent,
|
||||
EncGiftIVMismatch,
|
||||
EncGiftNicknamed,
|
||||
EncGiftNotFound,
|
||||
EncGiftPIDMismatch,
|
||||
EncGiftShinyMismatch,
|
||||
EncGiftVersionNotDistributed,
|
||||
EncGiftRegionNotDistributed,
|
||||
EncInvalid,
|
||||
EncMasteryInitial,
|
||||
EncTradeChangedNickname,
|
||||
EncTradeChangedOT,
|
||||
EncTradeIndexBad,
|
||||
EncTradeMatch,
|
||||
EncTradeUnchanged,
|
||||
EncStaticPIDShiny,
|
||||
EncTypeMatch,
|
||||
EncTypeMismatch,
|
||||
EncUnreleased,
|
||||
EncUnreleasedEMewJP,
|
||||
|
||||
// E-Reader
|
||||
EReaderAmerica,
|
||||
EReaderInvalid,
|
||||
EReaderJapan,
|
||||
|
||||
// Effort Values
|
||||
Effort2Remaining,
|
||||
EffortAbove252,
|
||||
EffortAbove510,
|
||||
EffortAllEqual,
|
||||
EffortCap100,
|
||||
EffortEgg,
|
||||
EffortShouldBeZero,
|
||||
EffortEXPIncreased,
|
||||
|
||||
// Evolution
|
||||
EvoInvalid,
|
||||
EvoTradeRequired,
|
||||
|
||||
// Fateful
|
||||
FatefulGiftMissing,
|
||||
FatefulInvalid,
|
||||
FatefulMissing,
|
||||
FatefulMystery,
|
||||
FatefulMysteryMissing,
|
||||
|
||||
// Favorite Marking
|
||||
FavoriteMarkingUnavailable,
|
||||
|
||||
// Form
|
||||
FormArgumentNotAllowed,
|
||||
FormArgumentValid,
|
||||
FormArgumentInvalid,
|
||||
FormBattle,
|
||||
FormEternal,
|
||||
FormEternalInvalid,
|
||||
FormInvalidGame,
|
||||
FormInvalidNature,
|
||||
FormItemMatches,
|
||||
FormItemInvalid,
|
||||
FormParty,
|
||||
FormPikachuCosplay,
|
||||
FormPikachuCosplayInvalid,
|
||||
FormPikachuEventInvalid,
|
||||
FormInvalidExpect_0,
|
||||
FormValid,
|
||||
FormVivillon,
|
||||
FormVivillonEventPre,
|
||||
FormVivillonInvalid,
|
||||
FormVivillonNonNative,
|
||||
|
||||
// Generation 1 & 2
|
||||
G1CatchRateChain,
|
||||
G1CatchRateEvo,
|
||||
G1CatchRateItem,
|
||||
G1CatchRateMatchPrevious,
|
||||
G1CatchRateMatchTradeback,
|
||||
G1CatchRateNone,
|
||||
G1CharNick,
|
||||
G1CharOT,
|
||||
G1OTGender,
|
||||
G1Stadium,
|
||||
G1Type1Fail,
|
||||
G1Type2Fail,
|
||||
G1TypeMatch1,
|
||||
G1TypeMatch2,
|
||||
G1TypeMatchPorygon,
|
||||
G1TypePorygonFail,
|
||||
G1TypePorygonFail1,
|
||||
G1TypePorygonFail2,
|
||||
G2InvalidTileTreeNotFound,
|
||||
G2TreeID,
|
||||
G2OTGender,
|
||||
|
||||
// Generation 3+
|
||||
G3EReader,
|
||||
G3OTGender,
|
||||
G4InvalidTileR45Surf,
|
||||
G5IVAll30,
|
||||
G5PIDShinyGrotto,
|
||||
G5SparkleInvalid,
|
||||
G5SparkleRequired,
|
||||
|
||||
// Gender
|
||||
GenderInvalidNone,
|
||||
|
||||
// Geography
|
||||
GeoBadOrder,
|
||||
GeoHardwareInvalid,
|
||||
GeoHardwareRange,
|
||||
GeoHardwareValid,
|
||||
GeoMemoryMissing,
|
||||
GeoNoCountryHT,
|
||||
GeoNoRegion,
|
||||
|
||||
// Hints
|
||||
|
||||
// Hyper Training
|
||||
HyperPerfectAll,
|
||||
HyperPerfectOne,
|
||||
HyperPerfectUnavailable,
|
||||
|
||||
// Item
|
||||
ItemEgg,
|
||||
ItemUnreleased,
|
||||
|
||||
// IVs
|
||||
IVNotCorrect,
|
||||
|
||||
// Level
|
||||
LevelEXPThreshold,
|
||||
LevelEXPTooHigh,
|
||||
LevelMetBelow,
|
||||
LevelMetGift,
|
||||
LevelMetGiftFail,
|
||||
LevelMetSane,
|
||||
|
||||
// Markings
|
||||
MarkValueShouldBeZero,
|
||||
MarkValueUnusedBitsPresent,
|
||||
|
||||
// Memory
|
||||
MemoryArgBadHT,
|
||||
MemoryHTFlagInvalid,
|
||||
MemoryHTLanguage,
|
||||
MemoryIndexArgHT,
|
||||
MemoryIndexFeelHTLEQ9,
|
||||
MemoryIndexIntensityHT1,
|
||||
MemoryIndexLinkHT,
|
||||
MemoryIndexVar_H1,
|
||||
MemoryMissingHT,
|
||||
MemoryMissingOT,
|
||||
MemorySocialZero,
|
||||
MemoryStatAffectionHT0,
|
||||
MemoryStatAffectionOT0,
|
||||
MemoryStatFriendshipHT0,
|
||||
|
||||
// Met Detail
|
||||
MetDetailTimeOfDay,
|
||||
|
||||
// Moves - General
|
||||
MoveKeldeoMismatch,
|
||||
MovesShouldMatchRelearnMoves,
|
||||
|
||||
// Moves - Shop & Alpha
|
||||
MoveShopAlphaMoveShouldBeOther,
|
||||
MoveShopAlphaMoveShouldBeZero,
|
||||
|
||||
// Nickname
|
||||
NickFlagEggNo,
|
||||
NickFlagEggYes,
|
||||
NickInvalidChar,
|
||||
NickLengthLong,
|
||||
NickLengthShort,
|
||||
NickMatchLanguage,
|
||||
NickMatchLanguageEgg,
|
||||
NickMatchLanguageEggFail,
|
||||
NickMatchLanguageFail,
|
||||
NickMatchLanguageFlag,
|
||||
NickMatchNoOthers,
|
||||
NickMatchNoOthersFail,
|
||||
|
||||
// Original Trainer
|
||||
OTLanguage,
|
||||
OTLong,
|
||||
OTShort,
|
||||
OTSuspicious,
|
||||
OT_IDEqual,
|
||||
OT_IDs0,
|
||||
OT_SID0,
|
||||
OT_SID0Invalid,
|
||||
OT_TID0,
|
||||
OT_IDInvalid,
|
||||
|
||||
// PID & Encryption Constant
|
||||
PIDEncryptWurmple,
|
||||
PIDEncryptZero,
|
||||
PIDEqualsEC,
|
||||
PIDGenderMatch,
|
||||
PIDGenderMismatch,
|
||||
PIDNatureMatch,
|
||||
PIDNatureMismatch,
|
||||
PIDTypeMismatch,
|
||||
PIDZero,
|
||||
|
||||
// Ribbons
|
||||
RibbonAllValid,
|
||||
RibbonEgg,
|
||||
|
||||
// Stats
|
||||
StatDynamaxInvalid,
|
||||
StatIncorrectHeight,
|
||||
StatIncorrectHeightCopy,
|
||||
StatIncorrectHeightValue,
|
||||
StatIncorrectWeight,
|
||||
StatIncorrectWeightValue,
|
||||
StatInvalidHeightWeight,
|
||||
StatGigantamaxInvalid,
|
||||
StatGigantamaxValid,
|
||||
StatNatureInvalid,
|
||||
StatBattleVersionInvalid,
|
||||
StatNobleInvalid,
|
||||
StatAlphaInvalid,
|
||||
|
||||
// Storage
|
||||
StoredSourceEgg,
|
||||
|
||||
// Super Training
|
||||
SuperComplete,
|
||||
SuperDistro,
|
||||
SuperEgg,
|
||||
SuperNoComplete,
|
||||
SuperNoUnlocked,
|
||||
SuperUnavailable,
|
||||
SuperUnused,
|
||||
|
||||
// Tera Type
|
||||
TeraTypeIncorrect,
|
||||
TeraTypeMismatch,
|
||||
|
||||
// Trading
|
||||
TradeNotAvailable,
|
||||
|
||||
// Trainer IDs
|
||||
TrainerIDNoSeed,
|
||||
|
||||
// Transfer
|
||||
TransferBad,
|
||||
TransferCurrentHandlerInvalid,
|
||||
TransferEgg,
|
||||
TransferEggLocationTransporter,
|
||||
TransferEggMetLevel,
|
||||
TransferEggVersion,
|
||||
TransferFlagIllegal,
|
||||
TransferHandlerFlagRequired,
|
||||
TransferHandlerMismatchName,
|
||||
TransferHandlerMismatchGender,
|
||||
TransferHandlerMismatchLanguage,
|
||||
TransferMet,
|
||||
TransferNotPossible,
|
||||
TransferMetLocation,
|
||||
TransferNature,
|
||||
TransferObedienceLevel,
|
||||
TransferKoreanGen4,
|
||||
TransferEncryptGen6BitFlip,
|
||||
TransferEncryptGen6Equals,
|
||||
TransferEncryptGen6Xor,
|
||||
TransferTrackerMissing,
|
||||
TransferTrackerShouldBeZero,
|
||||
TrashBytesExpected,
|
||||
TrashBytesMismatchInitial,
|
||||
TrashBytesMissingTerminator,
|
||||
TrashBytesShouldBeEmpty,
|
||||
|
||||
// Bulk Cross-Comparison
|
||||
BulkCloneDetectedDetails,
|
||||
BulkCloneDetectedTracker,
|
||||
BulkSharingEncryptionConstantGenerationSame,
|
||||
BulkSharingEncryptionConstantGenerationDifferent,
|
||||
BulkSharingEncryptionConstantEncounterType,
|
||||
BulkSharingPIDGenerationDifferent,
|
||||
BulkSharingPIDGenerationSame,
|
||||
BulkSharingPIDEncounterType,
|
||||
BulkDuplicateMysteryGiftEggReceived,
|
||||
BulkSharingTrainerIDs,
|
||||
BulkSharingTrainerVersion,
|
||||
|
||||
// Formattable Argument Present: 1 Number
|
||||
FirstWithArgument,
|
||||
ContestSheenGEQ_0 = FirstWithArgument,
|
||||
MemoryStatFriendshipOTBaseEvent_0,
|
||||
ContestSheenLEQ_0,
|
||||
EggFMetLevel_0,
|
||||
EffortUntrainedCap_0,
|
||||
EvoTradeReqOutsider_0,
|
||||
FormArgumentLEQ_0,
|
||||
FormArgumentGEQ_0,
|
||||
FormInvalidRange_0,
|
||||
FormInvalidRangeLEQ_0,
|
||||
HyperTrainLevelGEQ_0, // level
|
||||
IVAllEqual_0,
|
||||
IVFlawlessCountGEQ_0, // count
|
||||
MarkValueOutOfRange_0, // unknown value
|
||||
MemoryStatSocialLEQ_0,
|
||||
MemoryStatFullness_0,
|
||||
MemoryStatFullnessLEQ_0,
|
||||
MemoryStatEnjoyment_0,
|
||||
StatIncorrectCP_0, // value
|
||||
WordFilterTooManyNumbers_0, // count
|
||||
PokerusDaysLEQ_0, // days
|
||||
PokerusStrainUnobtainable_0, // strain
|
||||
MovePPExpectHealed_0, // move slot
|
||||
MovePPTooHigh_0, // move slot
|
||||
MovePPUpsTooHigh_0, // move slot
|
||||
MemoryHTGender_0, // gender value
|
||||
|
||||
// Single Argument: Move ID
|
||||
FirstWithMove,
|
||||
MoveTechRecordFlagMissing_0 = FirstWithMove, // move ID
|
||||
MoveShopAlphaMoveShouldBeMastered_0, // move
|
||||
MoveShopMasterInvalid_0, // move ID
|
||||
MoveShopMasterNotLearned_0, // move ID
|
||||
MoveShopPurchaseInvalid_0, // move ID
|
||||
|
||||
// One Argument: Language
|
||||
FirstWithLanguage,
|
||||
OTLanguageShouldBe_0 = FirstWithLanguage, // language
|
||||
OTLanguageShouldBeLeq_0, // language
|
||||
EncGiftLanguageNotDistributed_0, // language
|
||||
OTLanguageCannotPlayOnVersion_0, // language
|
||||
|
||||
// Multiple Arguments: Memories
|
||||
FirstWithMemory,
|
||||
MemoryValid_H = FirstWithMemory,
|
||||
MemoryArgBadCatch_H,
|
||||
MemoryArgBadHatch_H,
|
||||
MemoryArgBadID_H,
|
||||
MemoryArgBadLocation_H,
|
||||
MemoryArgBadOTEgg_H,
|
||||
MemoryArgSpecies_H,
|
||||
MemoryCleared_H,
|
||||
MemoryFeelInvalid_H,
|
||||
FirstMemoryWithValue,
|
||||
MemoryArgBadSpecies_H1 = FirstMemoryWithValue,
|
||||
MemoryArgBadMove_H1,
|
||||
MemoryArgBadItem_H1,
|
||||
MemoryIndexID_H1,
|
||||
MemoryIndexFeel_H1,
|
||||
MemoryIndexIntensity_H1,
|
||||
MemoryIndexIntensityMin_H1,
|
||||
|
||||
// One/Two Arguments: Special
|
||||
FirstComplex,
|
||||
RibbonFInvalid_0 = FirstComplex, // generated string
|
||||
WordFilterFlaggedPattern_01, // filter, pattern
|
||||
WordFilterInvalidCharacter_0, // filter, pattern
|
||||
|
||||
AwakenedStatGEQ_01,// value, statName
|
||||
GanbaruStatLEQ_01, // value, statName
|
||||
OTLanguageCannotTransferToConsoleRegion_0, // ConsoleRegion
|
||||
EncTradeShouldHaveEvolvedToSpecies_0, // species
|
||||
MoveEvoFCombination_0, // species
|
||||
HintEvolvesToSpecies_0, // species
|
||||
|
||||
RibbonMarkingInvalid_0, // ribbon
|
||||
RibbonMarkingAffixed_0, // ribbon
|
||||
RibbonMissing_0, // ribbon
|
||||
|
||||
StoredSlotSourceInvalid_0, // StorageSlotType
|
||||
HintEvolvesToRareForm_0, // bool
|
||||
|
||||
OTLanguageShouldBe_0or1, // language,language
|
||||
|
||||
MAX,
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary> Severity indication of the associated <see cref="CheckResult"/> </summary>
|
||||
@@ -25,19 +23,3 @@ public enum Severity : sbyte
|
||||
/// </summary>
|
||||
Valid = 1,
|
||||
}
|
||||
|
||||
public static partial class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a Check result Severity determination (Valid/Invalid/etc.) to the localized string.
|
||||
/// </summary>
|
||||
/// <param name="s"><see cref="Severity"/> value to convert to string.</param>
|
||||
/// <returns>Localized <see cref="string"/>.</returns>
|
||||
public static string Description(this Severity s) => s switch
|
||||
{
|
||||
Severity.Invalid => L_SInvalid,
|
||||
Severity.Fishy => L_SFishy,
|
||||
Severity.Valid => L_SValid,
|
||||
_ => L_SNotImplemented,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -15,8 +15,8 @@ public override void Verify(LegalityAnalysis data)
|
||||
data.AddLine(result);
|
||||
}
|
||||
|
||||
private CheckResult VALID => GetValid(LAbilityFlag);
|
||||
private CheckResult INVALID => GetInvalid(LAbilityMismatch);
|
||||
private CheckResult VALID => GetValid(AbilityFlag);
|
||||
private CheckResult INVALID => GetInvalid(AbilityMismatch);
|
||||
|
||||
private enum AbilityState : byte
|
||||
{
|
||||
@@ -33,7 +33,7 @@ private CheckResult VerifyAbility(LegalityAnalysis data)
|
||||
int ability = pk.Ability;
|
||||
int abilIndex = abilities.GetIndexOfAbility(ability);
|
||||
if (abilIndex < 0)
|
||||
return GetInvalid(LAbilityUnexpected);
|
||||
return GetInvalid(AbilityUnexpected);
|
||||
|
||||
byte format = pk.Format;
|
||||
if (format >= 6)
|
||||
@@ -59,7 +59,7 @@ private CheckResult VerifyAbility(LegalityAnalysis data)
|
||||
var evos = data.Info.EvoChainsAllGens;
|
||||
if (!AbilityChangeRules.IsAbilityCapsulePossible(evos))
|
||||
return INVALID;
|
||||
return GetValid(LAbilityCapsuleUsed);
|
||||
return GetValid(AbilityCapsuleUsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,12 +73,12 @@ private CheckResult VerifyAbility(LegalityAnalysis data)
|
||||
if (pk.AbilityNumber == 4)
|
||||
{
|
||||
if (AbilityChangeRules.IsAbilityPatchPossible(data.Info.EvoChainsAllGens))
|
||||
return GetValid(LAbilityPatchUsed);
|
||||
return GetValid(AbilityPatchUsed);
|
||||
}
|
||||
else if (enc.Ability == AbilityPermission.OnlyHidden)
|
||||
{
|
||||
if (AbilityChangeRules.IsAbilityPatchRevertPossible(data.Info.EvoChainsAllGens, pk.AbilityNumber))
|
||||
return GetValid(LAbilityPatchRevertUsed);
|
||||
return GetValid(AbilityPatchRevertUsed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ private CheckResult VerifyAbility(LegalityAnalysis data, IPersonalAbility12 abil
|
||||
if (eabil >= 0)
|
||||
{
|
||||
if ((data.Entity.AbilityNumber == 4) != (eabil == AbilityPermission.OnlyHidden))
|
||||
return GetInvalid(LAbilityHiddenFail);
|
||||
return GetInvalid(AbilityHiddenFail);
|
||||
if (eabil > 0)
|
||||
return VerifyFixedAbility(data, abilities, AbilityState.CanMismatch, eabil, abilIndex);
|
||||
}
|
||||
@@ -129,7 +129,7 @@ private CheckResult VerifyAbility345(LegalityAnalysis data, IEncounterTemplate e
|
||||
if (encounterAbility >= 0)
|
||||
{
|
||||
if ((pk.AbilityNumber == 4) != (encounterAbility == AbilityPermission.OnlyHidden))
|
||||
return GetInvalid(LAbilityHiddenFail);
|
||||
return GetInvalid(AbilityHiddenFail);
|
||||
if (encounterAbility > 0)
|
||||
return VerifyFixedAbility(data, abilities, state, encounterAbility, abilIndex);
|
||||
}
|
||||
@@ -148,14 +148,14 @@ private CheckResult VerifyFixedAbility(LegalityAnalysis data, IPersonalAbility12
|
||||
if (enc.Generation >= 6)
|
||||
{
|
||||
if (IsAbilityCapsuleModified(pk, encounterAbility, data.Info.EvoChainsAllGens, enc.Context))
|
||||
return GetValid(LAbilityCapsuleUsed);
|
||||
return GetValid(AbilityCapsuleUsed);
|
||||
if (pk.AbilityNumber != 1 << encounterAbility.GetSingleValue())
|
||||
return INVALID;
|
||||
return VALID;
|
||||
}
|
||||
|
||||
if ((pk.AbilityNumber == 4) != (encounterAbility == AbilityPermission.OnlyHidden))
|
||||
return GetInvalid(LAbilityHiddenFail);
|
||||
return GetInvalid(AbilityHiddenFail);
|
||||
|
||||
bool hasEvolved = enc.Species != pk.Species;
|
||||
if (hasEvolved && state != AbilityState.CanMismatch)
|
||||
@@ -187,7 +187,7 @@ private CheckResult VerifyFixedAbility(LegalityAnalysis data, IPersonalAbility12
|
||||
return CheckMatch(pk, abilities, enc.Generation, AbilityState.MustMatch, enc);
|
||||
|
||||
if (IsAbilityCapsuleModified(pk, encounterAbility, data.Info.EvoChainsAllGens, enc.Context))
|
||||
return GetValid(LAbilityCapsuleUsed);
|
||||
return GetValid(AbilityCapsuleUsed);
|
||||
|
||||
return INVALID;
|
||||
}
|
||||
@@ -248,7 +248,7 @@ private AbilityState VerifyAbilityGen3Transfer(LegalityAnalysis data, IPersonalA
|
||||
// If we reach here, it has not evolved in Gen4/5 games or has an invalid ability.
|
||||
// The ability does not need to match the PIDAbility, but only Gen3 ability is allowed.
|
||||
if (pk.Ability != pers.Ability1) // Not evolved in Gen4/5, but doesn't have Gen3 only ability
|
||||
data.AddLine(GetInvalid(LAbilityMismatch3)); // probably bad to do this here
|
||||
data.AddLine(GetInvalid(AbilityMismatch3)); // probably bad to do this here
|
||||
|
||||
return AbilityState.CanMismatch;
|
||||
}
|
||||
@@ -260,14 +260,14 @@ private CheckResult VerifyAbilityMG(LegalityAnalysis data, MysteryGift g, IPerso
|
||||
|
||||
var pk = data.Entity;
|
||||
if (g is PGT) // Ranger Manaphy
|
||||
return (pk.Format >= 6 ? (pk.AbilityNumber == 1) : (pk.AbilityNumber < 4)) ? VALID : GetInvalid(LAbilityMismatchGift);
|
||||
return (pk.Format >= 6 ? (pk.AbilityNumber == 1) : (pk.AbilityNumber < 4)) ? VALID : GetInvalid(AbilityMismatchGift);
|
||||
|
||||
var permit = g.Ability;
|
||||
if (permit == AbilityPermission.Any12H)
|
||||
return VALID;
|
||||
int abilNumber = pk.AbilityNumber;
|
||||
if (permit == AbilityPermission.Any12)
|
||||
return abilNumber == 4 ? GetInvalid(LAbilityMismatchGift) : VALID;
|
||||
return abilNumber == 4 ? GetInvalid(AbilityMismatchGift) : VALID;
|
||||
|
||||
// Only remaining matches are fixed index abilities
|
||||
int cardAbilIndex = (int)permit;
|
||||
@@ -277,7 +277,7 @@ private CheckResult VerifyAbilityMG(LegalityAnalysis data, MysteryGift g, IPerso
|
||||
// Can still match if the ability was changed via ability capsule...
|
||||
// However, it can't change to/from Hidden Abilities.
|
||||
if (abilNumber == 4 || permit == AbilityPermission.OnlyHidden)
|
||||
return GetInvalid(LAbilityHiddenFail);
|
||||
return GetInvalid(AbilityHiddenFail);
|
||||
|
||||
// Ability can be flipped 0/1 if Ability Capsule is available, is not Hidden Ability, and Abilities are different.
|
||||
if (pk.Format >= 6)
|
||||
@@ -285,10 +285,10 @@ private CheckResult VerifyAbilityMG(LegalityAnalysis data, MysteryGift g, IPerso
|
||||
// Maybe was evolved after using ability capsule.
|
||||
var evos = data.Info.EvoChainsAllGens;
|
||||
if (AbilityChangeRules.IsAbilityCapsulePossible(evos))
|
||||
return GetValid(LAbilityCapsuleUsed);
|
||||
return GetValid(AbilityCapsuleUsed);
|
||||
}
|
||||
|
||||
return pk.Format < 6 ? GetInvalid(LAbilityMismatchPID) : INVALID;
|
||||
return pk.Format < 6 ? GetInvalid(AbilityMismatchPID) : INVALID;
|
||||
}
|
||||
|
||||
private CheckResult VerifyAbilityPCD(LegalityAnalysis data, IPersonalAbility12 abilities, PCD pcd)
|
||||
@@ -305,7 +305,7 @@ private CheckResult VerifyAbilityPCD(LegalityAnalysis data, IPersonalAbility12 a
|
||||
return CheckMatch(pk, abilities, 4, AbilityState.MustMatch, pcd); // evolved, must match
|
||||
}
|
||||
if (pk.AbilityNumber < 4) // Ability Capsule can change between 1/2
|
||||
return GetValid(LAbilityCapsuleUsed);
|
||||
return GetValid(AbilityCapsuleUsed);
|
||||
}
|
||||
|
||||
if (pcd.Species != pk.Species)
|
||||
@@ -322,7 +322,7 @@ private CheckResult VerifyAbility5(LegalityAnalysis data, IEncounterTemplate enc
|
||||
// Eggs and Encounter Slots have not yet checked for Hidden Ability potential.
|
||||
return enc switch
|
||||
{
|
||||
EncounterEgg5 egg when pk.AbilityNumber == 4 && !egg.Ability.CanBeHidden() => GetInvalid(LAbilityHiddenUnavailable),
|
||||
EncounterEgg5 egg when pk.AbilityNumber == 4 && !egg.Ability.CanBeHidden() => GetInvalid(AbilityHiddenUnavailable),
|
||||
_ => CheckMatch(data.Entity, abilities, 5, pk.Format == 5 ? AbilityState.MustMatch : AbilityState.CanMismatch, enc),
|
||||
};
|
||||
}
|
||||
@@ -335,7 +335,7 @@ private CheckResult VerifyAbility6(LegalityAnalysis data, IEncounterTemplate enc
|
||||
|
||||
return enc switch
|
||||
{
|
||||
EncounterEgg6 egg when !egg.Ability.CanBeHidden() => GetInvalid(LAbilityHiddenUnavailable),
|
||||
EncounterEgg6 egg when !egg.Ability.CanBeHidden() => GetInvalid(AbilityHiddenUnavailable),
|
||||
_ => VALID,
|
||||
};
|
||||
}
|
||||
@@ -348,7 +348,7 @@ private CheckResult VerifyAbility7(LegalityAnalysis data, IEncounterTemplate enc
|
||||
|
||||
return enc switch
|
||||
{
|
||||
EncounterEgg7 egg when !egg.Ability.CanBeHidden() => GetInvalid(LAbilityHiddenUnavailable),
|
||||
EncounterEgg7 egg when !egg.Ability.CanBeHidden() => GetInvalid(AbilityHiddenUnavailable),
|
||||
_ => VALID,
|
||||
};
|
||||
}
|
||||
@@ -361,7 +361,7 @@ private CheckResult VerifyAbility8BDSP(LegalityAnalysis data, IEncounterTemplate
|
||||
|
||||
return enc switch
|
||||
{
|
||||
EncounterEgg8b egg when !egg.Ability.CanBeHidden() => GetInvalid(LAbilityHiddenUnavailable),
|
||||
EncounterEgg8b egg when !egg.Ability.CanBeHidden() => GetInvalid(AbilityHiddenUnavailable),
|
||||
_ => VALID,
|
||||
};
|
||||
}
|
||||
@@ -377,7 +377,7 @@ private CheckResult VerifyAbility8BDSP(LegalityAnalysis data, IEncounterTemplate
|
||||
private CheckResult CheckMatch(PKM pk, IPersonalAbility12 abilities, byte generation, AbilityState state, IEncounterTemplate enc)
|
||||
{
|
||||
if (generation is (3 or 4) && pk.AbilityNumber == 4)
|
||||
return GetInvalid(LAbilityHiddenUnavailable);
|
||||
return GetInvalid(AbilityHiddenUnavailable);
|
||||
|
||||
// other cases of hidden ability already flagged, all that is left is 1/2 mismatching
|
||||
if (state != AbilityState.MustMatch)
|
||||
@@ -394,7 +394,7 @@ private CheckResult CheckMatch(PKM pk, IPersonalAbility12 abilities, byte genera
|
||||
// Must not have the Ability bit flag set.
|
||||
// Shadow encounters set a random ability index; don't bother checking if it's a re-battle for ability bit flipping.
|
||||
if (abit && enc is not IShadow3)
|
||||
return GetInvalid(LAbilityMismatchFlag, CheckIdentifier.PID);
|
||||
return GetInvalid(CheckIdentifier.PID, AbilityMismatchFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -402,7 +402,7 @@ private CheckResult CheckMatch(PKM pk, IPersonalAbility12 abilities, byte genera
|
||||
// Version value check isn't factually correct, but there are no C/XD gifts with (Version!=15) that have two abilities.
|
||||
// Pikachu, Celebi, Ho-Oh
|
||||
if (pk.Version != GameVersion.CXD && abit != ((pk.EncryptionConstant & 1) == 1))
|
||||
return GetInvalid(LAbilityMismatchPID, CheckIdentifier.PID);
|
||||
return GetInvalid(CheckIdentifier.PID, AbilityMismatchPID);
|
||||
}
|
||||
}
|
||||
else if (pk.Format >= 6)
|
||||
@@ -421,7 +421,7 @@ private CheckResult GetPIDAbilityMatch(PKM pk, IPersonalAbility abilities)
|
||||
var index = pk.AbilityNumber >> 1;
|
||||
var abil = abilities.GetAbilityAtIndex(index);
|
||||
if (abil != pk.Ability)
|
||||
return GetInvalid(LAbilityMismatchPID);
|
||||
return GetInvalid(AbilityMismatchPID);
|
||||
|
||||
return VALID;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -25,11 +25,11 @@ public override void Verify(LegalityAnalysis data)
|
||||
// Can't obtain EVs in the game; only AVs.
|
||||
int sum = pb7.EVTotal;
|
||||
if (sum != 0)
|
||||
data.AddLine(GetInvalid(LEffortShouldBeZero));
|
||||
data.AddLine(GetInvalid(EffortShouldBeZero));
|
||||
|
||||
// Check that all AVs are within the allowed cap
|
||||
if (!pb7.AwakeningAllValid())
|
||||
data.AddLine(GetInvalid(LAwakenedCap));
|
||||
data.AddLine(GetInvalid(AwakenedCap));
|
||||
|
||||
// Gather all AVs. When leveling up, AVs are "randomly" granted, so a mon must be at or above.
|
||||
Span<byte> required = stackalloc byte[6];
|
||||
@@ -39,16 +39,16 @@ public override void Verify(LegalityAnalysis data)
|
||||
|
||||
// For each stat, ensure the current AV is at least the required minimum
|
||||
if (current[0] < required[0])
|
||||
data.AddLine(GetInvalid(string.Format(LAwakenedShouldBeValue, required[0], nameof(IAwakened.AV_HP))));
|
||||
data.AddLine(GetInvalid(Identifier, AwakenedStatGEQ_01, required[0], 0)); // HP
|
||||
if (current[1] < required[1])
|
||||
data.AddLine(GetInvalid(string.Format(LAwakenedShouldBeValue, required[1], nameof(IAwakened.AV_ATK))));
|
||||
data.AddLine(GetInvalid(Identifier, AwakenedStatGEQ_01, required[1], 1)); // Atk
|
||||
if (current[2] < required[2])
|
||||
data.AddLine(GetInvalid(string.Format(LAwakenedShouldBeValue, required[2], nameof(IAwakened.AV_DEF))));
|
||||
data.AddLine(GetInvalid(Identifier, AwakenedStatGEQ_01, required[2], 2)); // Def
|
||||
if (current[3] < required[3])
|
||||
data.AddLine(GetInvalid(string.Format(LAwakenedShouldBeValue, required[3], nameof(IAwakened.AV_SPA))));
|
||||
data.AddLine(GetInvalid(Identifier, AwakenedStatGEQ_01, required[3], 4)); // SpA
|
||||
if (current[4] < required[4])
|
||||
data.AddLine(GetInvalid(string.Format(LAwakenedShouldBeValue, required[4], nameof(IAwakened.AV_SPD))));
|
||||
data.AddLine(GetInvalid(Identifier, AwakenedStatGEQ_01, required[4], 5)); // SpD
|
||||
if (current[5] < required[5])
|
||||
data.AddLine(GetInvalid(string.Format(LAwakenedShouldBeValue, required[5], nameof(IAwakened.AV_SPE))));
|
||||
data.AddLine(GetInvalid(Identifier, AwakenedStatGEQ_01, required[5], 3)); // Speed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static PKHeX.Core.Ball;
|
||||
using static PKHeX.Core.BallVerificationResult;
|
||||
|
||||
@@ -180,8 +180,8 @@ private static BallVerificationResult VerifyBallEggGen9(EncounterEgg9 enc, Ball
|
||||
private CheckResult Localize(BallVerificationResult value)
|
||||
{
|
||||
bool valid = value.IsValid();
|
||||
string msg = value.GetMessage();
|
||||
return Get(msg, valid ? Severity.Valid : Severity.Invalid);
|
||||
var msg = value.GetMessage();
|
||||
return Get(valid ? Severity.Valid : Severity.Invalid, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,17 +210,17 @@ public static class BallVerificationResultExtensions
|
||||
_ => false,
|
||||
};
|
||||
|
||||
public static string GetMessage(this BallVerificationResult value) => value switch
|
||||
public static LegalityCheckResultCode GetMessage(this BallVerificationResult value) => value switch
|
||||
{
|
||||
ValidEncounter => LBallEnc,
|
||||
ValidInheritedSpecies => LBallSpeciesPass,
|
||||
BadEncounter => LBallEncMismatch,
|
||||
BadCaptureHeavy => LBallHeavy,
|
||||
BadInheritAbility => LBallAbility,
|
||||
BadInheritSpecies => LBallSpecies,
|
||||
BadInheritCherish => LBallEggCherish,
|
||||
BadInheritMaster => LBallEggMaster,
|
||||
BadOutOfRange => LBallUnavailable,
|
||||
ValidEncounter => BallEnc,
|
||||
ValidInheritedSpecies => BallSpeciesPass,
|
||||
BadEncounter => BallEncMismatch,
|
||||
BadCaptureHeavy => BallHeavy,
|
||||
BadInheritAbility => BallAbility,
|
||||
BadInheritSpecies => BallSpecies,
|
||||
BadInheritCherish => BallEggCherish,
|
||||
BadInheritMaster => BallEggMaster,
|
||||
BadOutOfRange => BallUnavailable,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -17,7 +17,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
// Colo starters are already hard-verified. No need to check them here.
|
||||
|
||||
if (pk.OriginalTrainerGender == 1)
|
||||
data.AddLine(GetInvalid(LG3OTGender, CheckIdentifier.Trainer));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Trainer, G3OTGender));
|
||||
|
||||
// Trainer ID is checked in another verifier. Don't duplicate it here.
|
||||
}
|
||||
@@ -34,7 +34,7 @@ private static void VerifyStarterXD(LegalityAnalysis data)
|
||||
|
||||
bool valid = MethodCXD.TryGetSeedStarterXD(pk, out var seed);
|
||||
if (!valid)
|
||||
data.AddLine(GetInvalid(LEncConditionBadRNGFrame, CheckIdentifier.PID));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.PID, EncConditionBadRNGFrame));
|
||||
else
|
||||
data.Info.PIDIV = new PIDIV(PIDType.CXD, seed);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -24,7 +24,7 @@ private CheckResult VerifyConsoleRegion(IRegionOrigin pk)
|
||||
{
|
||||
var consoleRegion = pk.ConsoleRegion;
|
||||
if (consoleRegion >= 7)
|
||||
return GetInvalid(LGeoHardwareRange);
|
||||
return GetInvalid(GeoHardwareRange);
|
||||
|
||||
return Verify3DSDataPresent(pk, consoleRegion);
|
||||
}
|
||||
@@ -32,7 +32,7 @@ private CheckResult VerifyConsoleRegion(IRegionOrigin pk)
|
||||
private CheckResult Verify3DSDataPresent(IRegionOrigin pk, byte consoleRegion)
|
||||
{
|
||||
if (!Locale3DS.IsConsoleRegionCountryValid(consoleRegion, pk.Country))
|
||||
return GetInvalid(LGeoHardwareInvalid);
|
||||
return GetValid(LGeoHardwareValid);
|
||||
return GetInvalid(GeoHardwareInvalid);
|
||||
return GetValid(GeoHardwareValid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using static PKHeX.Core.ContestStatGranting;
|
||||
using static PKHeX.Core.ContestStatInfo;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -29,14 +29,14 @@ public override void Verify(LegalityAnalysis data)
|
||||
if (correlation == None)
|
||||
{
|
||||
// We're only here because we have contest stat values. We aren't permitted to have any, so flag it.
|
||||
data.AddLine(GetInvalid(LContestZero));
|
||||
data.AddLine(GetInvalid(ContestZero));
|
||||
}
|
||||
else if (correlation == NoSheen)
|
||||
{
|
||||
// We can get contest stat values, but we can't get any for Sheen.
|
||||
// Any combination of non-sheen is ok, but nonzero sheen is illegal.
|
||||
if (s.ContestSheen != 0)
|
||||
data.AddLine(GetInvalid(LContestZeroSheen));
|
||||
data.AddLine(GetInvalid(ContestZeroSheen));
|
||||
}
|
||||
else if (correlation == CorrelateSheen)
|
||||
{
|
||||
@@ -50,12 +50,12 @@ public override void Verify(LegalityAnalysis data)
|
||||
var minSheen = CalculateMinimumSheen(s, initial, pk, method);
|
||||
|
||||
if (s.ContestSheen < minSheen)
|
||||
data.AddLine(GetInvalid(string.Format(LContestSheenTooLow_0, minSheen)));
|
||||
data.AddLine(GetInvalid(ContestSheenGEQ_0, minSheen));
|
||||
|
||||
// Check for sheen values that are too high.
|
||||
var maxSheen = CalculateMaximumSheen(s, pk.Nature, initial, gen3);
|
||||
if (s.ContestSheen > maxSheen)
|
||||
data.AddLine(GetInvalid(string.Format(LContestSheenTooHigh_0, maxSheen)));
|
||||
data.AddLine(GetInvalid(ContestSheenLEQ_0, maxSheen));
|
||||
}
|
||||
else if (correlation == Mixed)
|
||||
{
|
||||
@@ -65,7 +65,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
var initial = GetReferenceTemplate(data.Info.EncounterMatch);
|
||||
var maxSheen = CalculateMaximumSheen(s, pk.Nature, initial, gen3);
|
||||
if (s.ContestSheen > maxSheen)
|
||||
data.AddLine(GetInvalid(string.Format(LContestSheenTooHigh_0, maxSheen)));
|
||||
data.AddLine(GetInvalid(ContestSheenLEQ_0, maxSheen));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -16,7 +16,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
if (pk.IsEgg)
|
||||
{
|
||||
if (pk.EVTotal is not 0)
|
||||
data.AddLine(GetInvalid(LEffortEgg));
|
||||
data.AddLine(GetInvalid(EffortEgg));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,24 +28,24 @@ public override void Verify(LegalityAnalysis data)
|
||||
|
||||
int sum = pk.EVTotal;
|
||||
if (sum > EffortValues.Max510) // format >= 3
|
||||
data.AddLine(GetInvalid(LEffortAbove510));
|
||||
data.AddLine(GetInvalid(EffortAbove510));
|
||||
|
||||
var enc = data.EncounterMatch;
|
||||
Span<int> evs = stackalloc int[6];
|
||||
pk.GetEVs(evs);
|
||||
|
||||
if (format >= 6 && IsAnyAboveHardLimit6(evs))
|
||||
data.AddLine(GetInvalid(LEffortAbove252));
|
||||
data.AddLine(GetInvalid(EffortAbove252));
|
||||
else if (format < 5) // 3/4
|
||||
VerifyGainedEVs34(data, enc, evs, pk);
|
||||
|
||||
// Only one of the following can be true: 0, 508, and x%6!=0
|
||||
if (sum == 0 && !enc.IsWithinEncounterRange(pk))
|
||||
data.AddLine(Get(LEffortEXPIncreased, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, EffortEXPIncreased));
|
||||
else if (sum == EffortValues.MaxEffective)
|
||||
data.AddLine(Get(LEffort2Remaining, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, Effort2Remaining));
|
||||
else if (evs[0] != 0 && !evs.ContainsAnyExcept(evs[0]))
|
||||
data.AddLine(Get(LEffortAllEqual, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, EffortAllEqual));
|
||||
}
|
||||
|
||||
private void VerifyGainedEVs34(LegalityAnalysis data, IEncounterTemplate enc, ReadOnlySpan<int> evs, PKM pk)
|
||||
@@ -58,14 +58,14 @@ private void VerifyGainedEVs34(LegalityAnalysis data, IEncounterTemplate enc, Re
|
||||
{
|
||||
// Cannot EV train at level 100 -- Certain events are distributed at level 100.
|
||||
// EVs can only be increased by vitamins to a max of 100.
|
||||
data.AddLine(GetInvalid(LEffortCap100));
|
||||
data.AddLine(GetInvalid(EffortCap100));
|
||||
}
|
||||
else // Check for gained EVs without gaining EXP -- don't check Gen5+ which have wings to boost above 100.
|
||||
{
|
||||
var growth = PersonalTable.HGSS[enc.Species].EXPGrowth;
|
||||
var baseEXP = Experience.GetEXP(enc.LevelMin, growth);
|
||||
if (baseEXP == pk.EXP)
|
||||
data.AddLine(GetInvalid(string.Format(LEffortUntrainedCap, EffortValues.MaxVitamins34)));
|
||||
data.AddLine(GetInvalid(EffortUntrainedCap_0, EffortValues.MaxVitamins34));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static PKHeX.Core.Species;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -26,79 +27,79 @@ private CheckResult VerifyFormArgument(LegalityAnalysis data, IFormArgument f)
|
||||
|
||||
var unusedMask = pk.Format == 6 ? 0xFFFF_FF00 : 0xFF00_0000;
|
||||
if ((arg & unusedMask) != 0)
|
||||
return GetInvalid(LFormArgumentHigh);
|
||||
return GetInvalid(FormArgumentLEQ_0);
|
||||
|
||||
return (Species)pk.Species switch
|
||||
{
|
||||
// Transfer Edge Cases -- Bank wipes the form but keeps old FormArgument value.
|
||||
Furfrou when pk is { Context: EntityContext.Gen7, Form: 0 } &&
|
||||
((enc.Generation == 6 && f.FormArgument <= byte.MaxValue) || IsFormArgumentDayCounterValid(f, 5, true))
|
||||
=> GetValid(LFormArgumentValid),
|
||||
=> GetValid(FormArgumentValid),
|
||||
|
||||
Furfrou when pk.Form != 0 => !IsFormArgumentDayCounterValid(f, 5, true) ? GetInvalid(LFormArgumentInvalid) : GetValid(LFormArgumentValid),
|
||||
Furfrou when pk.Form != 0 => !IsFormArgumentDayCounterValid(f, 5, true) ? GetInvalid(FormArgumentInvalid) : GetValid(FormArgumentValid),
|
||||
Hoopa when pk.Form == 1 => data.Info.EvoChainsAllGens switch
|
||||
{
|
||||
{ HasVisitedGen9: true } when arg == 0 => GetValid(LFormArgumentValid), // Value not applied on form change, and reset when reverted.
|
||||
{ HasVisitedGen6: true } when IsFormArgumentDayCounterValid(f, 3) => GetValid(LFormArgumentValid), // 0-3 via OR/AS
|
||||
{ HasVisitedGen7: true } when IsFormArgumentDayCounterValid(f, 3) && f.FormArgumentRemain != 0 => GetValid(LFormArgumentValid), // 1-3 via Gen7
|
||||
_ => GetInvalid(LFormArgumentInvalid),
|
||||
{ HasVisitedGen9: true } when arg == 0 => GetValid(FormArgumentValid), // Value not applied on form change, and reset when reverted.
|
||||
{ HasVisitedGen6: true } when IsFormArgumentDayCounterValid(f, 3) => GetValid(FormArgumentValid), // 0-3 via OR/AS
|
||||
{ HasVisitedGen7: true } when IsFormArgumentDayCounterValid(f, 3) && f.FormArgumentRemain != 0 => GetValid(FormArgumentValid), // 1-3 via Gen7
|
||||
_ => GetInvalid(FormArgumentInvalid),
|
||||
},
|
||||
Yamask when pk.Form == 1 => arg switch
|
||||
{
|
||||
not 0 when pk.IsEgg => GetInvalid(LFormArgumentNotAllowed),
|
||||
> 9_999 => GetInvalid(LFormArgumentHigh),
|
||||
_ => GetValid(LFormArgumentValid),
|
||||
not 0 when pk.IsEgg => GetInvalid(FormArgumentNotAllowed),
|
||||
> 9_999 => GetInvalid(FormArgumentLEQ_0, 9999),
|
||||
_ => GetValid(FormArgumentValid),
|
||||
},
|
||||
Basculin when pk.Form is 2 => arg switch
|
||||
{
|
||||
not 0 when pk.IsEgg => GetInvalid(LFormArgumentNotAllowed),
|
||||
> 9_999 => GetInvalid(LFormArgumentHigh),
|
||||
_ => GetValid(LFormArgumentValid),
|
||||
not 0 when pk.IsEgg => GetInvalid(FormArgumentNotAllowed),
|
||||
> 9_999 => GetInvalid(FormArgumentLEQ_0, 9999),
|
||||
_ => GetValid(FormArgumentValid),
|
||||
},
|
||||
Qwilfish when pk.Form is 1 => arg switch
|
||||
{
|
||||
not 0 when pk.IsEgg => GetInvalid(LFormArgumentNotAllowed),
|
||||
not 0 when pk.CurrentLevel < 25 => GetInvalid(LFormArgumentHigh), // Can't get requisite move
|
||||
> 9_999 => GetInvalid(LFormArgumentHigh),
|
||||
_ => GetValid(LFormArgumentValid),
|
||||
not 0 when pk.IsEgg => GetInvalid(FormArgumentNotAllowed),
|
||||
not 0 when pk.CurrentLevel < 25 => GetInvalid(FormArgumentLEQ_0, 0), // Can't get requisite move
|
||||
> 9_999 => GetInvalid(FormArgumentLEQ_0, 9999),
|
||||
_ => GetValid(FormArgumentValid),
|
||||
},
|
||||
Overqwil => arg switch
|
||||
{
|
||||
> 9_999 => GetInvalid(LFormArgumentHigh),
|
||||
0 when enc.Species == (ushort)Overqwil => GetValid(LFormArgumentValid),
|
||||
< 20 when !data.Info.EvoChainsAllGens.HasVisitedGen9 || pk.CurrentLevel < (pk is IHomeTrack { HasTracker: true } ? 15 : 28) => GetInvalid(LFormArgumentLow),
|
||||
>= 20 when !data.Info.EvoChainsAllGens.HasVisitedPLA || pk.CurrentLevel < 25 => GetInvalid(LFormArgumentLow),
|
||||
_ when pk is IHomeTrack { HasTracker: false } and PA8 { CurrentLevel: < 25 } => GetInvalid(LEvoInvalid),
|
||||
_ => GetValid(LFormArgumentValid),
|
||||
> 9_999 => GetInvalid(FormArgumentLEQ_0, 9999),
|
||||
0 when enc.Species == (ushort)Overqwil => GetValid(FormArgumentValid),
|
||||
< 20 when !data.Info.EvoChainsAllGens.HasVisitedGen9 || pk.CurrentLevel < (pk is IHomeTrack { HasTracker: true } ? 15 : 28) => GetInvalid(FormArgumentGEQ_0, 20),
|
||||
>= 20 when !data.Info.EvoChainsAllGens.HasVisitedPLA || pk.CurrentLevel < 25 => GetInvalid(FormArgumentLEQ_0, 0),
|
||||
_ when pk is IHomeTrack { HasTracker: false } and PA8 { CurrentLevel: < 25 } => GetInvalid(EvoInvalid),
|
||||
_ => GetValid(FormArgumentValid),
|
||||
},
|
||||
Stantler => arg switch
|
||||
{
|
||||
not 0 when pk.IsEgg => GetInvalid(LFormArgumentNotAllowed),
|
||||
not 0 when pk.CurrentLevel < 31 => GetInvalid(LFormArgumentHigh),
|
||||
> 9_999 => GetInvalid(LFormArgumentHigh),
|
||||
_ => arg == 0 || HasVisitedPLA(data, Stantler) ? GetValid(LFormArgumentValid) : GetInvalid(LFormArgumentNotAllowed),
|
||||
not 0 when pk.IsEgg => GetInvalid(FormArgumentNotAllowed),
|
||||
not 0 when pk.CurrentLevel < 31 => GetInvalid(FormArgumentLEQ_0, 0),
|
||||
> 9_999 => GetInvalid(FormArgumentLEQ_0, 9999),
|
||||
_ => arg == 0 || HasVisitedPLA(data, Stantler) ? GetValid(FormArgumentValid) : GetInvalid(FormArgumentNotAllowed),
|
||||
},
|
||||
Primeape => arg switch
|
||||
{
|
||||
> 9_999 => GetInvalid(LFormArgumentHigh),
|
||||
_ => arg == 0 || HasVisitedSV(data, Primeape) ? GetValid(LFormArgumentValid) : GetInvalid(LFormArgumentNotAllowed),
|
||||
> 9_999 => GetInvalid(FormArgumentLEQ_0, 9999),
|
||||
_ => arg == 0 || HasVisitedSV(data, Primeape) ? GetValid(FormArgumentValid) : GetInvalid(FormArgumentNotAllowed),
|
||||
},
|
||||
Bisharp => arg switch
|
||||
{
|
||||
> 9_999 => GetInvalid(LFormArgumentHigh),
|
||||
_ => arg == 0 || HasVisitedSV(data, Bisharp) ? GetValid(LFormArgumentValid) : GetInvalid(LFormArgumentNotAllowed),
|
||||
> 9_999 => GetInvalid(FormArgumentLEQ_0, 9999),
|
||||
_ => arg == 0 || HasVisitedSV(data, Bisharp) ? GetValid(FormArgumentValid) : GetInvalid(FormArgumentNotAllowed),
|
||||
},
|
||||
Gimmighoul => arg switch
|
||||
{
|
||||
// When leveled up, the game copies the save file's current coin count to the arg (clamped to <=999). If >=999, evolution is triggered.
|
||||
// Without being leveled up at least once, it cannot have a form arg value.
|
||||
>= 999 => GetInvalid(LFormArgumentHigh),
|
||||
0 => GetValid(LFormArgumentValid),
|
||||
_ => pk.CurrentLevel != pk.MetLevel ? GetValid(LFormArgumentValid) : GetInvalid(LFormArgumentNotAllowed),
|
||||
>= 999 => GetInvalid(FormArgumentLEQ_0, 999),
|
||||
0 => GetValid(FormArgumentValid),
|
||||
_ => pk.CurrentLevel != pk.MetLevel ? GetValid(FormArgumentValid) : GetInvalid(FormArgumentNotAllowed),
|
||||
},
|
||||
Runerigus => VerifyFormArgumentRange(enc.Species, Runerigus, arg, 49, 9999),
|
||||
Alcremie => VerifyFormArgumentRange(enc.Species, Alcremie, arg, 0, (uint)AlcremieDecoration.Ribbon),
|
||||
Wyrdeer when enc.Species != (int)Wyrdeer && pk.CurrentLevel < 31 => GetInvalid(LEvoInvalid),
|
||||
Alcremie => VerifyFormArgumentRange(enc.Species, Alcremie, arg, 0, (ushort)AlcremieDecoration.Ribbon),
|
||||
Wyrdeer when enc.Species != (int)Wyrdeer && pk.CurrentLevel < 31 => GetInvalid(EvoInvalid),
|
||||
Wyrdeer => VerifyFormArgumentRange(enc.Species, Wyrdeer, arg, 20, 9999),
|
||||
Basculegion => VerifyFormArgumentRange(enc.Species, Basculegion, arg, 294, 9999),
|
||||
Annihilape => VerifyFormArgumentRange(enc.Species, Annihilape, arg, 20, 9999),
|
||||
@@ -108,17 +109,17 @@ private CheckResult VerifyFormArgument(LegalityAnalysis data, IFormArgument f)
|
||||
{
|
||||
// Starter Legend has '1' when present in party, to differentiate.
|
||||
// Cannot be traded to other games.
|
||||
EncounterStatic9 { StarterBoxLegend: true } x when ParseSettings.ActiveTrainer is { } tr && (tr is not SAV9SV sv || sv.Version != x.Version) => GetInvalid(LTradeNotAvailable),
|
||||
EncounterStatic9 { StarterBoxLegend: true } x when ParseSettings.ActiveTrainer is { } tr && (tr is not SAV9SV sv || sv.Version != x.Version) => GetInvalid(TradeNotAvailable),
|
||||
EncounterStatic9 { StarterBoxLegend: true } => arg switch
|
||||
{
|
||||
< EncounterStatic9.RideLegendFormArg => GetInvalid(LFormArgumentLow),
|
||||
EncounterStatic9.RideLegendFormArg => !data.IsStoredSlot(StorageSlotType.Ride) ? GetInvalid(LFormParty) : GetValid(LFormArgumentValid),
|
||||
> EncounterStatic9.RideLegendFormArg => GetInvalid(LFormArgumentHigh),
|
||||
< EncounterStatic9.RideLegendFormArg => GetInvalid(FormArgumentGEQ_0, EncounterStatic9.RideLegendFormArg),
|
||||
EncounterStatic9.RideLegendFormArg => !data.IsStoredSlot(StorageSlotType.Ride) ? GetInvalid(FormParty) : GetValid(FormArgumentValid),
|
||||
> EncounterStatic9.RideLegendFormArg => GetInvalid(FormArgumentLEQ_0, EncounterStatic9.RideLegendFormArg),
|
||||
},
|
||||
_ => arg switch
|
||||
{
|
||||
not 0 => GetInvalid(LFormArgumentNotAllowed),
|
||||
_ => GetValid(LFormArgumentValid),
|
||||
not 0 => GetInvalid(FormArgumentNotAllowed),
|
||||
_ => GetValid(FormArgumentValid),
|
||||
},
|
||||
},
|
||||
_ => VerifyFormArgumentNone(pk, f),
|
||||
@@ -137,22 +138,22 @@ private CheckResult VerifyFormArgument(LegalityAnalysis data, IFormArgument f)
|
||||
/// <param name="value">Current Form Argument value</param>
|
||||
/// <param name="min">Minimum value allowed</param>
|
||||
/// <param name="max">Maximum value allowed</param>
|
||||
private CheckResult VerifyFormArgumentRange(ushort encSpecies, Species check, uint value, uint min, uint max)
|
||||
private CheckResult VerifyFormArgumentRange(ushort encSpecies, Species check, uint value, [ConstantExpected] ushort min, [ConstantExpected] ushort max)
|
||||
{
|
||||
// If was never the Form Argument accruing species (never evolved from it), then it must be zero.
|
||||
if (encSpecies == (ushort)check)
|
||||
{
|
||||
if (value == 0)
|
||||
return GetValid(LFormArgumentValid);
|
||||
return GetInvalid(LFormArgumentNotAllowed);
|
||||
return GetValid(FormArgumentValid);
|
||||
return GetInvalid(FormArgumentNotAllowed);
|
||||
}
|
||||
|
||||
// Evolved, must be within the range.
|
||||
if (value < min)
|
||||
return GetInvalid(LFormArgumentLow);
|
||||
return GetInvalid(FormArgumentGEQ_0, min);
|
||||
if (value > max)
|
||||
return GetInvalid(LFormArgumentHigh);
|
||||
return GetValid(LFormArgumentValid);
|
||||
return GetInvalid(FormArgumentLEQ_0, max);
|
||||
return GetValid(FormArgumentValid);
|
||||
}
|
||||
|
||||
private CheckResult VerifyFormArgumentNone(PKM pk, IFormArgument f)
|
||||
@@ -162,26 +163,26 @@ private CheckResult VerifyFormArgumentNone(PKM pk, IFormArgument f)
|
||||
if (f.FormArgument != 0)
|
||||
{
|
||||
if (pk is { Species: (int)Furfrou, Form: 0 } && (f.FormArgument & ~0xFF_00_00u) == 0)
|
||||
return GetValid(LFormArgumentValid);
|
||||
return GetInvalid(LFormArgumentNotAllowed);
|
||||
return GetValid(FormArgumentValid);
|
||||
return GetInvalid(FormArgumentNotAllowed);
|
||||
}
|
||||
return GetValid(LFormArgumentValid);
|
||||
return GetValid(FormArgumentValid);
|
||||
}
|
||||
|
||||
if (f.FormArgument != 0)
|
||||
{
|
||||
if (pk is { Species: (int)Furfrou, Form: 0 } && (f.FormArgument & ~0xFFu) == 0)
|
||||
return GetValid(LFormArgumentValid);
|
||||
return GetInvalid(LFormArgumentNotAllowed);
|
||||
return GetValid(FormArgumentValid);
|
||||
return GetInvalid(FormArgumentNotAllowed);
|
||||
}
|
||||
|
||||
// Stored separately from main form argument value
|
||||
if (pk6.FormArgumentRemain != 0)
|
||||
return GetInvalid(LFormArgumentNotAllowed);
|
||||
return GetInvalid(FormArgumentNotAllowed);
|
||||
if (pk6.FormArgumentElapsed != 0)
|
||||
return GetInvalid(LFormArgumentNotAllowed);
|
||||
return GetInvalid(FormArgumentNotAllowed);
|
||||
|
||||
return GetValid(LFormArgumentValid);
|
||||
return GetValid(FormArgumentValid);
|
||||
}
|
||||
|
||||
private static bool IsFormArgumentDayCounterValid(IFormArgument f, uint maxSeed, bool canRefresh = false)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static PKHeX.Core.Species;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -22,7 +22,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
FormArg.Verify(data);
|
||||
}
|
||||
|
||||
private CheckResult VALID => GetValid(LFormValid);
|
||||
private CheckResult VALID => GetValid(FormValid);
|
||||
|
||||
private CheckResult VerifyForm(LegalityAnalysis data)
|
||||
{
|
||||
@@ -36,10 +36,9 @@ private CheckResult VerifyForm(LegalityAnalysis data)
|
||||
|
||||
var species = pk.Species;
|
||||
var enc = data.EncounterMatch;
|
||||
var Info = data.Info;
|
||||
|
||||
if (!pi.IsFormWithinRange(form) && !FormInfo.IsValidOutOfBoundsForm(species, form, enc.Generation))
|
||||
return GetInvalid(string.Format(LFormInvalidRange, count - 1, form));
|
||||
return GetInvalid(FormInvalidRange_0, (ushort)(count - 1));
|
||||
|
||||
switch ((Species)species)
|
||||
{
|
||||
@@ -48,44 +47,48 @@ private CheckResult VerifyForm(LegalityAnalysis data)
|
||||
{
|
||||
if (form == 0)
|
||||
break; // Regular Pikachu, OK.
|
||||
return GetInvalid(LFormPikachuCosplay);
|
||||
return GetInvalid(FormPikachuCosplay);
|
||||
}
|
||||
if (form != s6.Form)
|
||||
return GetInvalid(LFormPikachuCosplayInvalid);
|
||||
return GetInvalid(FormPikachuCosplayInvalid);
|
||||
if (pk.Format != 6)
|
||||
return GetInvalid(LTransferBad); // Can't transfer.
|
||||
return GetInvalid(TransferBad); // Can't transfer.
|
||||
break;
|
||||
|
||||
// LGP/E: Can't get the other game's Starter form.
|
||||
case Pikachu when form is not 0 && ParseSettings.ActiveTrainer is SAV7b {Version:GameVersion.GE}:
|
||||
case Eevee when form is not 0 && ParseSettings.ActiveTrainer is SAV7b {Version:GameVersion.GP}:
|
||||
return GetInvalid(LFormBattle);
|
||||
return GetInvalid(FormBattle);
|
||||
|
||||
case Pikachu when enc.Generation >= 7: // Cap
|
||||
var expectForm = enc is EncounterInvalid or IEncounterEgg ? 0 : enc.Form;
|
||||
if (form != expectForm)
|
||||
{
|
||||
bool gift = enc is MysteryGift g && g.Form != form;
|
||||
var msg = gift ? LFormPikachuEventInvalid : LFormInvalidGame;
|
||||
var msg = gift ? FormPikachuEventInvalid : FormInvalidGame;
|
||||
return GetInvalid(msg);
|
||||
}
|
||||
break;
|
||||
|
||||
case Unown when enc.Generation == 2 && form >= 26:
|
||||
return GetInvalid(string.Format(LFormInvalidRange, "Z", form == 26 ? "!" : "?"));
|
||||
case Unown when enc.Generation == 3 && form != EntityPID.GetUnownForm3(pk.EncryptionConstant):
|
||||
return GetInvalid(string.Format(LFormInvalidExpect_0, EntityPID.GetUnownForm3(pk.EncryptionConstant)));
|
||||
case Dialga or Palkia or Giratina or Arceus when form > 0 && pk is PA8: // can change forms with key items
|
||||
return GetInvalid(FormInvalidRangeLEQ_0, 25);
|
||||
case Unown when enc.Generation == 3:
|
||||
var expectUnown = EntityPID.GetUnownForm3(pk.EncryptionConstant);
|
||||
if (expectUnown != form)
|
||||
return GetInvalid(FormInvalidExpect_0, expectUnown);
|
||||
break;
|
||||
|
||||
case Dialga or Palkia or Giratina or Arceus when form > 0 && pk is PA8: // can change forms with key items
|
||||
break;
|
||||
case Dialga when pk.Format >= 9 && ((form == 1) != (pk.HeldItem == 1777)): // Origin Forme Dialga with Adamant Crystal
|
||||
case Palkia when pk.Format >= 9 && ((form == 1) != (pk.HeldItem == 1778)): // Origin Forme Palkia with Lustrous Globe
|
||||
case Giratina when pk.Format >= 9 && ((form == 1) != (pk.HeldItem == 1779)): // Origin Forme Giratina with Griseous Core
|
||||
case Giratina when pk.Format <= 8 && ((form == 1) != (pk.HeldItem == 0112)): // Origin Forme Giratina with Griseous Orb
|
||||
return GetInvalid(LFormItemInvalid);
|
||||
return GetInvalid(FormItemInvalid);
|
||||
|
||||
case Arceus:
|
||||
var arceus = FormItem.GetFormArceus(pk.HeldItem, pk.Format);
|
||||
return arceus != form ? GetInvalid(LFormItemInvalid) : GetValid(LFormItem);
|
||||
return arceus != form ? GetInvalid(FormItemInvalid) : GetValid(FormItemMatches);
|
||||
case Keldeo when enc.Generation != 5 || pk.Format >= 8:
|
||||
// can mismatch in Gen5 via B/W tutor and transfer up
|
||||
// can mismatch in Gen8+ as the form activates in battle when knowing the move; outside of battle can be either state.
|
||||
@@ -93,93 +96,93 @@ private CheckResult VerifyForm(LegalityAnalysis data)
|
||||
bool hasSword = pk.HasMove((int) Move.SecretSword);
|
||||
bool isSword = pk.Form == 1;
|
||||
if (isSword != hasSword)
|
||||
return GetInvalid(LMoveKeldeoMismatch);
|
||||
return GetInvalid(MoveKeldeoMismatch);
|
||||
break;
|
||||
case Genesect:
|
||||
var genesect = FormItem.GetFormGenesect(pk.HeldItem);
|
||||
return genesect != form ? GetInvalid(LFormItemInvalid) : GetValid(LFormItem);
|
||||
return genesect != form ? GetInvalid(FormItemInvalid) : GetValid(FormItemMatches);
|
||||
case Greninja:
|
||||
if (form > 1) // Ash Battle Bond active
|
||||
return GetInvalid(LFormBattle);
|
||||
return GetInvalid(FormBattle);
|
||||
if (form != 0 && enc is not MysteryGift) // Form can not be bred for, MysteryGift already checked
|
||||
return GetInvalid(string.Format(LFormInvalidRange, 0, form));
|
||||
return GetInvalid(FormInvalidRange_0, 0);
|
||||
break;
|
||||
|
||||
case Scatterbug or Spewpa or Vivillon when enc.Context is EntityContext.Gen9:
|
||||
if (form > 18 && enc.Form != form) // Pokéball
|
||||
return GetInvalid(LFormVivillonEventPre);
|
||||
return GetInvalid(FormVivillonEventPre);
|
||||
if (form != 18 && enc is IEncounterEgg) // Fancy
|
||||
return GetInvalid(LFormVivillonNonNative);
|
||||
return GetInvalid(FormVivillonNonNative);
|
||||
break;
|
||||
case Scatterbug or Spewpa:
|
||||
if (form > Vivillon3DS.MaxWildFormID) // Fancy & Pokéball
|
||||
return GetInvalid(LFormVivillonEventPre);
|
||||
return GetInvalid(FormVivillonEventPre);
|
||||
if (pk is not IRegionOrigin tr)
|
||||
break;
|
||||
if (!Vivillon3DS.IsPatternValid(form, tr.ConsoleRegion))
|
||||
return GetInvalid(LFormVivillonInvalid);
|
||||
return GetInvalid(FormVivillonInvalid);
|
||||
if (!Vivillon3DS.IsPatternNative(form, tr.Country, tr.Region))
|
||||
data.AddLine(Get(LFormVivillonNonNative, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, FormVivillonNonNative));
|
||||
break;
|
||||
case Vivillon:
|
||||
if (form > Vivillon3DS.MaxWildFormID) // Fancy & Pokéball
|
||||
{
|
||||
if (enc is not MysteryGift)
|
||||
return GetInvalid(LFormVivillonInvalid);
|
||||
return GetValid(LFormVivillon);
|
||||
return GetInvalid(FormVivillonInvalid);
|
||||
return GetValid(FormVivillon);
|
||||
}
|
||||
if (pk is not IRegionOrigin trv)
|
||||
break;
|
||||
if (!Vivillon3DS.IsPatternValid(form, trv.ConsoleRegion))
|
||||
return GetInvalid(LFormVivillonInvalid);
|
||||
return GetInvalid(FormVivillonInvalid);
|
||||
if (!Vivillon3DS.IsPatternNative(form, trv.Country, trv.Region))
|
||||
data.AddLine(Get(LFormVivillonNonNative, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, FormVivillonNonNative));
|
||||
break;
|
||||
|
||||
case Floette when form == 5: // Floette Eternal Flower -- Never Released
|
||||
if (enc is not MysteryGift)
|
||||
return GetInvalid(LFormEternalInvalid);
|
||||
return GetValid(LFormEternal);
|
||||
return GetInvalid(FormEternalInvalid);
|
||||
return GetValid(FormEternal);
|
||||
case Meowstic when form != pk.Gender:
|
||||
return GetInvalid(LGenderInvalidNone);
|
||||
return GetInvalid(GenderInvalidNone);
|
||||
|
||||
case Silvally:
|
||||
var silvally = FormItem.GetFormSilvally(pk.HeldItem);
|
||||
return silvally != form ? GetInvalid(LFormItemInvalid) : GetValid(LFormItem);
|
||||
return silvally != form ? GetInvalid(FormItemInvalid) : GetValid(FormItemMatches);
|
||||
|
||||
// Form doesn't exist in SM; cannot originate from that game.
|
||||
case Rockruff when enc.Generation == 7 && form == 1 && pk.SM:
|
||||
case Lycanroc when enc.Generation == 7 && form == 2 && pk.SM:
|
||||
return GetInvalid(LFormInvalidGame);
|
||||
return GetInvalid(FormInvalidGame);
|
||||
|
||||
// Toxel encounters have already been checked for the nature-specific evolution criteria.
|
||||
case Toxtricity when enc.Species == (int)Toxtricity:
|
||||
// The game enforces the Nature for Toxtricity encounters too!
|
||||
if (pk.Form != ToxtricityUtil.GetAmpLowKeyResult(pk.Nature))
|
||||
return GetInvalid(LFormInvalidNature);
|
||||
return GetInvalid(FormInvalidNature);
|
||||
break;
|
||||
|
||||
// Ogerpon's form changes depending on its held mask
|
||||
case Ogerpon when (form & 3) != FormItem.GetFormOgerpon(pk.HeldItem):
|
||||
return GetInvalid(LFormItemInvalid);
|
||||
return GetInvalid(FormItemInvalid);
|
||||
|
||||
// Impossible Egg forms
|
||||
case Rotom when pk.IsEgg && form != 0:
|
||||
case Furfrou when pk.IsEgg && form != 0:
|
||||
return GetInvalid(LEggSpecies);
|
||||
return GetInvalid(EggSpecies);
|
||||
|
||||
// Party Only Forms
|
||||
case Shaymin:
|
||||
case Furfrou:
|
||||
case Hoopa:
|
||||
if (form != 0 && !data.IsStoredSlot(StorageSlotType.Party) && pk.Format <= 6) // has form but stored in box
|
||||
return GetInvalid(LFormParty);
|
||||
return GetInvalid(FormParty);
|
||||
break;
|
||||
}
|
||||
|
||||
var format = pk.Format;
|
||||
if (FormInfo.IsBattleOnlyForm(species, form, format))
|
||||
return GetInvalid(LFormBattle);
|
||||
return GetInvalid(FormBattle);
|
||||
|
||||
return VALID;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -19,7 +19,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
// D/P/Pt & HG/SS Shedinja glitch -- only generation 4 spawns
|
||||
bool ignore = pk is { Format: 4, Species: (int)Species.Shedinja } && pk.MetLevel != pk.CurrentLevel;
|
||||
if (!ignore)
|
||||
data.AddLine(GetInvalid(LGenderInvalidNone));
|
||||
data.AddLine(GetInvalid(GenderInvalidNone));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
if (gen is 3 or 4 or 5)
|
||||
{
|
||||
// Gender-PID & Nature-PID relationship check
|
||||
var result = IsValidGenderPID(data) ? GetValid(LPIDGenderMatch) : GetInvalid(LPIDGenderMismatch);
|
||||
var result = IsValidGenderPID(data) ? GetValid(PIDGenderMatch) : GetInvalid(PIDGenderMismatch);
|
||||
data.AddLine(result);
|
||||
|
||||
if (gen != 5)
|
||||
@@ -38,15 +38,15 @@ public override void Verify(LegalityAnalysis data)
|
||||
|
||||
// Check fixed gender cases
|
||||
if ((pi.OnlyFemale && gender != 1) || (pi.OnlyMale && gender != 0))
|
||||
data.AddLine(GetInvalid(LGenderInvalidNone));
|
||||
data.AddLine(GetInvalid(GenderInvalidNone));
|
||||
}
|
||||
|
||||
private static void VerifyNaturePID(LegalityAnalysis data)
|
||||
{
|
||||
var pk = data.Entity;
|
||||
var result = GetExpectedNature(pk) == pk.Nature
|
||||
? GetValid(LPIDNatureMatch, CheckIdentifier.Nature)
|
||||
: GetInvalid(LPIDNatureMismatch, CheckIdentifier.Nature);
|
||||
? GetValid(CheckIdentifier.Nature, PIDNatureMatch)
|
||||
: GetInvalid(CheckIdentifier.Nature, PIDNatureMismatch);
|
||||
data.AddLine(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -18,7 +18,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
return;
|
||||
var enc = data.EncounterMatch;
|
||||
bool valid = IsGroundTileValid(enc, e);
|
||||
var result = !valid ? GetInvalid(LEncTypeMismatch) : GetValid(LEncTypeMatch);
|
||||
var result = !valid ? GetInvalid(EncTypeMismatch) : GetValid(EncTypeMatch);
|
||||
data.AddLine(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -68,22 +68,22 @@ private void VerifyTradeState(LegalityAnalysis data)
|
||||
VerifyGeoLocationData(data, t, data.Entity);
|
||||
|
||||
if (pk.VC && pk is PK7 {Geo1_Country: 0}) // VC transfers set Geo1 Country
|
||||
data.AddLine(GetInvalid(LGeoMemoryMissing));
|
||||
data.AddLine(GetInvalid(GeoMemoryMissing));
|
||||
|
||||
if (!pk.IsUntraded)
|
||||
{
|
||||
// Can't have HT details even as a Link Trade egg, except in some games.
|
||||
if (pk.IsEgg && !EggStateLegality.IsValidHTEgg(pk))
|
||||
data.AddLine(GetInvalid(LMemoryArgBadHT));
|
||||
data.AddLine(GetInvalid(MemoryArgBadHT));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pk.CurrentHandler != 0) // Badly edited; PKHeX doesn't trip this.
|
||||
data.AddLine(GetInvalid(LMemoryHTFlagInvalid));
|
||||
data.AddLine(GetInvalid(MemoryHTFlagInvalid));
|
||||
else if (pk.HandlingTrainerFriendship != 0)
|
||||
data.AddLine(GetInvalid(LMemoryStatFriendshipHT0));
|
||||
data.AddLine(GetInvalid(MemoryStatFriendshipHT0));
|
||||
else if (pk is IAffection {HandlingTrainerAffection: not 0})
|
||||
data.AddLine(GetInvalid(LMemoryStatAffectionHT0));
|
||||
data.AddLine(GetInvalid(MemoryStatAffectionHT0));
|
||||
|
||||
// Don't check trade evolutions if Untraded. The Evolution Chain already checks for trade evolutions.
|
||||
}
|
||||
@@ -106,7 +106,7 @@ private void VerifyHandlerState(LegalityAnalysis data, bool neverOT)
|
||||
{
|
||||
// generally disable this check if it's being edited inside a blank save file's environment.
|
||||
if (tr is not SaveFile { State.Exportable: false })
|
||||
data.AddLine(GetInvalid(LTransferCurrentHandlerInvalid));
|
||||
data.AddLine(GetInvalid(TransferCurrentHandlerInvalid));
|
||||
// if there's no HT data yet specified, don't bother checking further.
|
||||
// blank save exports will be injected and fixed later, and not-blanks will have been flagged by the above.
|
||||
if (pk.IsUntraded)
|
||||
@@ -118,9 +118,9 @@ private void VerifyHandlerState(LegalityAnalysis data, bool neverOT)
|
||||
}
|
||||
|
||||
if (current != 1 && (enc.Context != pk.Context || neverOT))
|
||||
data.AddLine(GetInvalid(LTransferHTFlagRequired));
|
||||
data.AddLine(GetInvalid(TransferHandlerFlagRequired));
|
||||
if (!pk.IsUntraded && IsUntradeableEncounter(enc)) // Starter, untradeable
|
||||
data.AddLine(GetInvalid(LTransferCurrentHandlerInvalid));
|
||||
data.AddLine(GetInvalid(TransferCurrentHandlerInvalid));
|
||||
}
|
||||
|
||||
public static bool IsHandlerStateCorrect(IEncounterTemplate enc, PKM pk, byte current, byte expect)
|
||||
@@ -141,15 +141,15 @@ private void CheckHandlingTrainerEquals(LegalityAnalysis data, PKM pk, ITrainerI
|
||||
ht = ht[..len];
|
||||
|
||||
if (!ht.SequenceEqual(tr.OT))
|
||||
data.AddLine(GetInvalid(LTransferHTMismatchName));
|
||||
data.AddLine(GetInvalid(TransferHandlerMismatchName));
|
||||
if (pk.HandlingTrainerGender != tr.Gender)
|
||||
data.AddLine(GetInvalid(LTransferHTMismatchGender));
|
||||
data.AddLine(GetInvalid(TransferHandlerMismatchGender));
|
||||
|
||||
// If the format exposes a language, check if it matches.
|
||||
// Can be mismatched as the game only checks OT/Gender equivalence -- if it matches, don't update everything else.
|
||||
// Statistically unlikely that players will play in different languages, but it's technically possible.
|
||||
if (pk is IHandlerLanguage h && h.HandlingTrainerLanguage != tr.Language)
|
||||
data.AddLine(Get(LTransferHTMismatchLanguage, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, TransferHandlerMismatchLanguage));
|
||||
}
|
||||
|
||||
private static bool IsUntradeableEncounter(IEncounterTemplate enc) => enc switch
|
||||
@@ -188,8 +188,9 @@ private void VerifyOTFriendship(LegalityAnalysis data, bool neverOT, byte genera
|
||||
// If none match, then it is not a valid OT friendship.
|
||||
var fs = pk.OriginalTrainerFriendship;
|
||||
var enc = data.Info.EncounterMatch;
|
||||
if (GetBaseFriendship(enc) != fs)
|
||||
data.AddLine(GetInvalid(LMemoryStatFriendshipOTBaseEvent));
|
||||
var expect = GetBaseFriendship(enc);
|
||||
if (fs != expect)
|
||||
data.AddLine(GetInvalid(MemoryStatFriendshipOTBaseEvent_0, expect));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,20 +200,22 @@ private void VerifyOTFriendshipVC12(LegalityAnalysis data, PKM pk)
|
||||
// Since some evolutions have different base friendship values, check all possible evolutions for a match.
|
||||
// If none match, then it is not a valid OT friendship.
|
||||
// VC transfers use S/M personal info
|
||||
var any = IsMatchFriendship(data.Info.EvoChainsAllGens.Gen7, pk.OriginalTrainerFriendship);
|
||||
var any = IsMatchFriendship(data.Info.EvoChainsAllGens.Gen7, pk.OriginalTrainerFriendship, out var hint);
|
||||
if (!any)
|
||||
data.AddLine(GetInvalid(LMemoryStatFriendshipOTBaseEvent));
|
||||
data.AddLine(GetInvalid(MemoryStatFriendshipOTBaseEvent_0, hint));
|
||||
}
|
||||
|
||||
private static bool IsMatchFriendship(EvoCriteria[] evos, int fs)
|
||||
private static bool IsMatchFriendship(ReadOnlySpan<EvoCriteria> evos, byte current, out byte expect)
|
||||
{
|
||||
expect = 0; // will be overridden on the first loop
|
||||
var pt = PersonalTable.USUM;
|
||||
foreach (var z in evos)
|
||||
{
|
||||
if (!pt.IsPresentInGame(z.Species, z.Form))
|
||||
continue;
|
||||
var entry = pt.GetFormEntry(z.Species, z.Form);
|
||||
if (entry.BaseFriendship == fs)
|
||||
expect = entry.BaseFriendship;
|
||||
if (expect == current)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -228,7 +231,7 @@ private void VerifyOTAffection(LegalityAnalysis data, bool neverOT, int origin,
|
||||
// Can gain affection in Gen6 via the Contest glitch applying affection to OT rather than HT.
|
||||
// VC encounters cannot obtain OT affection since they can't visit Gen6.
|
||||
if ((origin <= 2 && a.OriginalTrainerAffection != 0) || IsInvalidContestAffection(a))
|
||||
data.AddLine(GetInvalid(LMemoryStatAffectionOT0));
|
||||
data.AddLine(GetInvalid(MemoryStatAffectionOT0));
|
||||
}
|
||||
else if (neverOT)
|
||||
{
|
||||
@@ -237,17 +240,17 @@ private void VerifyOTAffection(LegalityAnalysis data, bool neverOT, int origin,
|
||||
if (pk is { IsUntraded: true, XY: true })
|
||||
{
|
||||
if (a.OriginalTrainerAffection != 0)
|
||||
data.AddLine(GetInvalid(LMemoryStatAffectionOT0));
|
||||
data.AddLine(GetInvalid(MemoryStatAffectionOT0));
|
||||
}
|
||||
else if (IsInvalidContestAffection(a))
|
||||
{
|
||||
data.AddLine(GetInvalid(LMemoryStatAffectionOT0));
|
||||
data.AddLine(GetInvalid(MemoryStatAffectionOT0));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a.OriginalTrainerAffection != 0)
|
||||
data.AddLine(GetInvalid(LMemoryStatAffectionOT0));
|
||||
data.AddLine(GetInvalid(MemoryStatAffectionOT0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,18 +263,18 @@ private void VerifyHTMisc(LegalityAnalysis data)
|
||||
var pk = data.Entity;
|
||||
var htGender = pk.HandlingTrainerGender;
|
||||
if (htGender > 1 || (pk.IsUntraded && htGender != 0))
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryHTGender, htGender)));
|
||||
data.AddLine(GetInvalid(MemoryHTGender_0, htGender));
|
||||
}
|
||||
|
||||
private void VerifyGeoLocationData(LegalityAnalysis data, IGeoTrack t, PKM pk)
|
||||
{
|
||||
var valid = t.GetValidity();
|
||||
if (valid == GeoValid.CountryAfterPreviousEmpty)
|
||||
data.AddLine(GetInvalid(LGeoBadOrder));
|
||||
data.AddLine(GetInvalid(GeoBadOrder));
|
||||
else if (valid == GeoValid.RegionWithoutCountry)
|
||||
data.AddLine(GetInvalid(LGeoNoRegion));
|
||||
data.AddLine(GetInvalid(GeoNoRegion));
|
||||
if (t.Geo1_Country != 0 && pk.IsUntraded) // traded
|
||||
data.AddLine(GetInvalid(LGeoNoCountryHT));
|
||||
data.AddLine(GetInvalid(GeoNoCountryHT));
|
||||
}
|
||||
|
||||
// OR/AS contests mistakenly apply 20 affection to the OT instead of the current handler's value
|
||||
@@ -314,13 +317,13 @@ public static bool GetCanOTHandle(IEncounterTemplate enc, PKM pk, byte generatio
|
||||
_ => false,
|
||||
};
|
||||
|
||||
private static int GetBaseFriendship(IEncounterTemplate enc) => enc switch
|
||||
private static byte GetBaseFriendship(IEncounterTemplate enc) => enc switch
|
||||
{
|
||||
IFixedOTFriendship f => f.OriginalTrainerFriendship,
|
||||
_ => GetBaseFriendship(enc.Context, enc.Species, enc.Form),
|
||||
};
|
||||
|
||||
private static int GetBaseFriendship(EntityContext context, ushort species, byte form) => context switch
|
||||
private static byte GetBaseFriendship(EntityContext context, ushort species, byte form) => context switch
|
||||
{
|
||||
EntityContext.Gen6 => PersonalTable.AO[species].BaseFriendship,
|
||||
EntityContext.Gen7 => PersonalTable.USUM[species].BaseFriendship,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -20,21 +20,21 @@ public override void Verify(LegalityAnalysis data)
|
||||
|
||||
if (!t.IsHyperTrainingAvailable())
|
||||
{
|
||||
data.AddLine(GetInvalid(LHyperPerfectUnavailable));
|
||||
data.AddLine(GetInvalid(HyperPerfectUnavailable));
|
||||
return;
|
||||
}
|
||||
|
||||
var minLevel = t.GetHyperTrainMinLevel(data.Info.EvoChainsAllGens, pk.Context);
|
||||
if (pk.CurrentLevel < minLevel)
|
||||
{
|
||||
data.AddLine(GetInvalid(string.Format(LHyperTooLow_0, minLevel)));
|
||||
data.AddLine(GetInvalid(HyperTrainLevelGEQ_0, (ushort)minLevel));
|
||||
return;
|
||||
}
|
||||
|
||||
int max = pk.MaxIV;
|
||||
if (pk.IVTotal == max * 6)
|
||||
{
|
||||
data.AddLine(GetInvalid(LHyperPerfectAll));
|
||||
data.AddLine(GetInvalid(HyperPerfectAll));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
}
|
||||
|
||||
if (IsFlawlessHyperTrained(pk, t, max))
|
||||
data.AddLine(GetInvalid(LHyperPerfectOne));
|
||||
data.AddLine(GetInvalid(HyperPerfectOne));
|
||||
}
|
||||
|
||||
public static bool IsFlawlessHyperTrained(PKM pk, IHyperTrain t, int max)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -30,7 +30,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
var pk = data.Entity;
|
||||
var hp = pk.IV_HP;
|
||||
if (hp < 30 && AllIVsEqual(pk, hp))
|
||||
data.AddLine(Get(string.Format(LIVAllEqual_0, hp), Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, IVAllEqual_0, (ushort)hp));
|
||||
}
|
||||
|
||||
private static bool AllIVsEqual(PKM pk, int hp) => pk.IV_ATK == hp
|
||||
@@ -51,7 +51,7 @@ private void VerifyIVsMystery(LegalityAnalysis data, MysteryGift g)
|
||||
{
|
||||
bool valid = Legal.GetIsFixedIVSequenceValidSkipRand(IVs, data.Entity);
|
||||
if (!valid)
|
||||
data.AddLine(GetInvalid(LEncGiftIVMismatch));
|
||||
data.AddLine(GetInvalid(EncGiftIVMismatch));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -80,12 +80,12 @@ private void VerifyIVsFlawless(LegalityAnalysis data, IFlawlessIVCount s)
|
||||
private void VerifyIVsFlawless(LegalityAnalysis data, int count)
|
||||
{
|
||||
if (data.Entity.FlawlessIVCount < count)
|
||||
data.AddLine(GetInvalid(string.Format(LIVF_COUNT0_31, count)));
|
||||
data.AddLine(GetInvalid(IVFlawlessCountGEQ_0, (ushort)count));
|
||||
}
|
||||
|
||||
private void VerifyIVsGoTransfer(LegalityAnalysis data, IPogoSlot g)
|
||||
{
|
||||
if (!g.GetIVsValid(data.Entity))
|
||||
data.AddLine(GetInvalid(LIVNotCorrect));
|
||||
data.AddLine(GetInvalid(IVNotCorrect));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -14,10 +14,10 @@ public override void Verify(LegalityAnalysis data)
|
||||
var pk = data.Entity;
|
||||
var item = pk.HeldItem;
|
||||
if (pk.IsEgg && item != 0)
|
||||
data.AddLine(GetInvalid(LItemEgg));
|
||||
data.AddLine(GetInvalid(ItemEgg));
|
||||
|
||||
if (!ItemRestrictions.IsHeldItemAllowed(item, context: pk.Context))
|
||||
data.AddLine(GetInvalid(LItemUnreleased));
|
||||
data.AddLine(GetInvalid(ItemUnreleased));
|
||||
else if (item == 175 && pk is G3PKM g3) // Enigma Berry
|
||||
VerifyEnigmaGen3(data, g3);
|
||||
}
|
||||
@@ -27,7 +27,7 @@ private void VerifyEnigmaGen3(LegalityAnalysis data, G3PKM g3)
|
||||
// A Pokémon holding this Berry cannot be traded to Pokémon Colosseum or Pokémon XD: Gale of Darkness,
|
||||
// nor can it be stored in Pokémon Box Ruby & Sapphire.
|
||||
if (g3 is CK3 or XK3 || ParseSettings.ActiveTrainer is SAV3RSBox)
|
||||
data.AddLine(GetInvalid(LItemUnreleased));
|
||||
data.AddLine(GetInvalid(ItemUnreleased));
|
||||
else
|
||||
VerifyEReaderBerry(data);
|
||||
}
|
||||
@@ -42,10 +42,10 @@ private void VerifyEReaderBerry(LegalityAnalysis data)
|
||||
|
||||
private CheckResult GetEReaderCheckResult(EReaderBerryMatch status) => status switch
|
||||
{
|
||||
EReaderBerryMatch.NoMatch => GetInvalid(LEReaderInvalid),
|
||||
EReaderBerryMatch.NoData => GetInvalid(LItemUnreleased),
|
||||
EReaderBerryMatch.InvalidUSA => GetInvalid(LEReaderAmerica),
|
||||
EReaderBerryMatch.InvalidJPN => GetInvalid(LEReaderJapan),
|
||||
EReaderBerryMatch.NoMatch => GetInvalid(EReaderInvalid),
|
||||
EReaderBerryMatch.NoData => GetInvalid(ItemUnreleased),
|
||||
EReaderBerryMatch.InvalidUSA => GetInvalid(EReaderAmerica),
|
||||
EReaderBerryMatch.InvalidJPN => GetInvalid(EReaderJapan),
|
||||
_ => default,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static PKHeX.Core.GameVersion;
|
||||
using static PKHeX.Core.LanguageID;
|
||||
|
||||
@@ -20,7 +20,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
var enc = data.EncounterMatch;
|
||||
if (!IsValidLanguageID(currentLanguage, maxLanguageID, pk, enc))
|
||||
{
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, $"<={maxLanguageID}", currentLanguage)));
|
||||
data.AddLine(GetInvalid(Identifier, OTLanguageShouldBeLeq_0, (byte)maxLanguageID, (byte)currentLanguage));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -30,9 +30,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
)
|
||||
{
|
||||
bool kor = currentLanguage == Korean;
|
||||
var msgpkm = kor ? L_XKorean : L_XKoreanNon;
|
||||
var msgsav = kor ? L_XKoreanNon : L_XKorean;
|
||||
data.AddLine(GetInvalid(string.Format(LTransferOriginFInvalid0_1, msgpkm, msgsav)));
|
||||
data.AddLine(GetInvalid(TransferKoreanGen4));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -40,11 +38,11 @@ public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
// Korean Crystal does not exist, neither do Korean VC1
|
||||
if (pk is { Korean: true, Version: not (GD or SI) })
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, $"!={currentLanguage}", currentLanguage)));
|
||||
data.AddLine(GetInvalid(OTLanguageCannotPlayOnVersion_0, (byte)pk.Version));
|
||||
|
||||
// Japanese VC is language locked; cannot obtain Japanese-Blue version as other languages.
|
||||
if (pk is { Japanese: false, Version: BU })
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, nameof(Japanese), currentLanguage)));
|
||||
data.AddLine(GetInvalid(OTLanguageCannotPlayOnVersion_0, (byte)pk.Version));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -16,9 +16,9 @@ public override void Verify(LegalityAnalysis data)
|
||||
return;
|
||||
|
||||
if (pa.IsNoble)
|
||||
data.AddLine(GetInvalid(LStatNobleInvalid));
|
||||
data.AddLine(GetInvalid(StatNobleInvalid));
|
||||
if (pa.IsAlpha != data.EncounterMatch is IAlphaReadOnly { IsAlpha: true })
|
||||
data.AddLine(GetInvalid(LStatAlphaInvalid));
|
||||
data.AddLine(GetInvalid(StatAlphaInvalid));
|
||||
|
||||
CheckScalars(data, pa);
|
||||
CheckGanbaru(data, pa);
|
||||
@@ -36,7 +36,7 @@ private static void CheckGanbaru(LegalityAnalysis data, PA8 pa)
|
||||
if (gv <= max)
|
||||
continue;
|
||||
|
||||
data.AddLine(GetInvalid(LGanbaruStatTooHigh, CheckIdentifier.GVs));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.GVs, GanbaruStatLEQ_01, max, (ushort)i));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -47,14 +47,14 @@ private void CheckScalars(LegalityAnalysis data, PA8 pa)
|
||||
if (pa.IsAlpha && data.EncounterMatch is EncounterSlot8a)
|
||||
{
|
||||
if (pa.HeightScalar != 255)
|
||||
data.AddLine(GetInvalid(LStatIncorrectHeightValue));
|
||||
data.AddLine(GetInvalid(StatIncorrectHeightValue, 255));
|
||||
if (pa.WeightScalar != 255)
|
||||
data.AddLine(GetInvalid(LStatIncorrectWeightValue));
|
||||
data.AddLine(GetInvalid(StatIncorrectWeightValue, 255));
|
||||
}
|
||||
|
||||
// No way to mutate the display height scalar value. Must match!
|
||||
if (pa.HeightScalar != pa.Scale)
|
||||
data.AddLine(GetInvalid(LStatIncorrectHeightCopy, CheckIdentifier.Encounter));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Encounter, StatIncorrectHeightCopy));
|
||||
}
|
||||
|
||||
private static void CheckLearnset(LegalityAnalysis data, PA8 pa)
|
||||
@@ -188,7 +188,7 @@ private void VerifyTutorMoveIndex(LegalityAnalysis data, PA8 pa, int i, IPermitR
|
||||
if (permit.IsRecordPermitted(i))
|
||||
return; // If it has been legally purchased, then any mastery state is legal.
|
||||
|
||||
data.AddLine(GetInvalid(string.Format(LMoveShopPurchaseInvalid_0, ParseSettings.MoveStrings[permit.RecordPermitIndexes[i]])));
|
||||
data.AddLine(GetInvalid(MoveShopPurchaseInvalid_0, permit.RecordPermitIndexes[i]));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -204,9 +204,9 @@ private void VerifyTutorMoveIndex(LegalityAnalysis data, PA8 pa, int i, IPermitR
|
||||
if (data.EncounterMatch is (IMoveset m and IMasteryInitialMoveShop8) && m.Moves.Contains(move))
|
||||
return; // Previously checked.
|
||||
if (!permit.IsRecordPermitted(i))
|
||||
data.AddLine(GetInvalid(string.Format(LMoveShopMasterInvalid_0, ParseSettings.MoveStrings[move])));
|
||||
data.AddLine(GetInvalid(MoveShopMasterInvalid_0, move));
|
||||
else if (!CanLearnMoveByLevelUp(data, pa, i, moves))
|
||||
data.AddLine(GetInvalid(string.Format(LMoveShopMasterNotLearned_0, ParseSettings.MoveStrings[move])));
|
||||
data.AddLine(GetInvalid(MoveShopMasterNotLearned_0, move));
|
||||
}
|
||||
|
||||
private static bool CanLearnMoveByLevelUp(LegalityAnalysis data, PA8 pa, int i, ReadOnlySpan<ushort> moves)
|
||||
@@ -229,12 +229,12 @@ private void VerifyAlphaMove(LegalityAnalysis data, PA8 pa, ushort alphaMove, IP
|
||||
{
|
||||
if (!pa.IsAlpha || data.EncounterMatch is EncounterSlot8a { Type: SlotType8a.Landmark })
|
||||
{
|
||||
data.AddLine(GetInvalid(LMoveShopAlphaMoveShouldBeZero));
|
||||
data.AddLine(GetInvalid(MoveShopAlphaMoveShouldBeZero));
|
||||
return;
|
||||
}
|
||||
if (!CanMasterMoveFromMoveShop(alphaMove, permit))
|
||||
{
|
||||
data.AddLine(GetInvalid(LMoveShopAlphaMoveShouldBeOther));
|
||||
data.AddLine(GetInvalid(MoveShopAlphaMoveShouldBeOther));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ private void VerifyAlphaMove(LegalityAnalysis data, PA8 pa, ushort alphaMove, IP
|
||||
var masteredIndex = permit.RecordPermitIndexes.IndexOf(alphaMove);
|
||||
// Index is already >= 0, implicitly via the above call not returning false.
|
||||
if (!pa.GetMasteredRecordFlag(masteredIndex))
|
||||
data.AddLine(GetInvalid(LMoveShopAlphaMoveShouldBeMastered));
|
||||
data.AddLine(GetInvalid(MoveShopAlphaMoveShouldBeMastered_0, alphaMove));
|
||||
}
|
||||
|
||||
private void VerifyAlphaMoveZero(LegalityAnalysis data)
|
||||
@@ -256,7 +256,7 @@ private void VerifyAlphaMoveZero(LegalityAnalysis data)
|
||||
|
||||
var pi = PersonalTable.LA.GetFormEntry(enc.Species, enc.Form);
|
||||
if (!pi.HasMoveShop) // must have had a tutor flag
|
||||
data.AddLine(GetInvalid(LMoveShopAlphaMoveShouldBeOther));
|
||||
data.AddLine(GetInvalid(MoveShopAlphaMoveShouldBeOther));
|
||||
}
|
||||
|
||||
private static bool CanMasterMoveFromMoveShop(ushort move, IPermitRecord permit)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -17,12 +17,12 @@ public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
if (!IsMetLevelMatchEncounter(gift, pk))
|
||||
{
|
||||
data.AddLine(GetInvalid(LLevelMetGift));
|
||||
data.AddLine(GetInvalid(LevelMetGift));
|
||||
return;
|
||||
}
|
||||
if (gift.Level > pk.CurrentLevel)
|
||||
{
|
||||
data.AddLine(GetInvalid(LLevelMetGiftFail));
|
||||
data.AddLine(GetInvalid(LevelMetGiftFail, gift.Level));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
if (pk.CurrentLevel != enc.LevelMin)
|
||||
{
|
||||
data.AddLine(GetInvalid(string.Format(LEggFMetLevel_0, enc.LevelMin)));
|
||||
data.AddLine(GetInvalid(EggFMetLevel_0, enc.LevelMin));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
? 125 // Gen2 Dizzy Punch gifts always have 125 EXP, even if it's more than the Lv5 exp required.
|
||||
: Experience.GetEXP(enc.LevelMin, data.PersonalInfo.EXPGrowth);
|
||||
if (reqEXP != pk.EXP)
|
||||
data.AddLine(GetInvalid(LEggEXP));
|
||||
data.AddLine(GetInvalid(EggEXP, reqEXP));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -48,15 +48,15 @@ public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
var expect = Experience.GetEXP(100, data.PersonalInfo.EXPGrowth);
|
||||
if (pk.EXP != expect)
|
||||
data.AddLine(GetInvalid(LLevelEXPTooHigh));
|
||||
data.AddLine(GetInvalid(Identifier, LevelEXPTooHigh, expect));
|
||||
}
|
||||
|
||||
if (lvl < pk.MetLevel)
|
||||
data.AddLine(GetInvalid(LLevelMetBelow));
|
||||
data.AddLine(GetInvalid(LevelMetBelow, lvl));
|
||||
else if (!enc.IsWithinEncounterRange(pk) && lvl != 100 && pk.EXP == Experience.GetEXP(lvl, data.PersonalInfo.EXPGrowth))
|
||||
data.AddLine(Get(LLevelEXPThreshold, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, LevelEXPThreshold));
|
||||
else
|
||||
data.AddLine(GetValid(LLevelMetSane));
|
||||
data.AddLine(GetValid(LevelMetSane));
|
||||
}
|
||||
|
||||
private static bool IsMetLevelMatchEncounter(MysteryGift gift, PKM pk)
|
||||
@@ -81,25 +81,21 @@ public void VerifyG1(LegalityAnalysis data)
|
||||
if (pk.IsEgg)
|
||||
{
|
||||
if (pk.CurrentLevel != EncounterEgg2.Level)
|
||||
data.AddLine(GetInvalid(string.Format(LEggFMetLevel_0, EncounterEgg2.Level)));
|
||||
data.AddLine(GetInvalid(EggFMetLevel_0, EncounterEgg2.Level));
|
||||
return;
|
||||
}
|
||||
if (pk.MetLocation != 0) // crystal
|
||||
{
|
||||
var lvl = pk.CurrentLevel;
|
||||
if (lvl < pk.MetLevel)
|
||||
data.AddLine(GetInvalid(LLevelMetBelow));
|
||||
data.AddLine(GetInvalid(LevelMetBelow, lvl));
|
||||
}
|
||||
|
||||
if (IsTradeEvolutionRequired(data, enc))
|
||||
{
|
||||
// Pokémon has been traded illegally between games without evolving.
|
||||
// Trade evolution species IDs for Gen1 are sequential dex numbers.
|
||||
var names = ParseSettings.SpeciesStrings;
|
||||
var species = enc.Species;
|
||||
var evolved = names[species + 1];
|
||||
var unevolved = names[species];
|
||||
data.AddLine(GetInvalid(string.Format(LEvoTradeReqOutsider, unevolved, evolved)));
|
||||
data.AddLine(GetInvalid(EvoTradeReqOutsider_0, enc.Species + 1u));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static PKHeX.Core.RibbonIndex;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -25,13 +25,12 @@ public override void Verify(LegalityAnalysis data)
|
||||
if (pk.IsEgg && pk is IRibbonSetAffixed a && a.AffixedRibbon != -1)
|
||||
{
|
||||
// Disallow affixed values on eggs.
|
||||
var affix = (RibbonIndex)a.AffixedRibbon;
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, GetRibbonNameSafe(affix))));
|
||||
data.AddLine(GetInvalid(RibbonMarkingAffixed_0, (ushort)(RibbonIndex)a.AffixedRibbon));
|
||||
}
|
||||
|
||||
// Some encounters come with a fixed Mark, and we've not yet checked if it's missing.
|
||||
if (data.EncounterMatch is IEncounterMarkExtra extra && extra.IsMissingExtraMark(pk, out var missing))
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingFInvalid_0, GetRibbonNameSafe(missing))));
|
||||
data.AddLine(GetInvalid(RibbonMissing_0, (ushort)missing));
|
||||
}
|
||||
|
||||
private void VerifyNoMarksPresent(LegalityAnalysis data, IRibbonIndex m)
|
||||
@@ -39,7 +38,7 @@ private void VerifyNoMarksPresent(LegalityAnalysis data, IRibbonIndex m)
|
||||
for (var mark = MarkLunchtime; mark <= MarkSlump; mark++)
|
||||
{
|
||||
if (m.GetRibbon((int)mark))
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingFInvalid_0, GetRibbonNameSafe(mark))));
|
||||
data.AddLine(GetInvalid(RibbonMarkingInvalid_0, (ushort)mark));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,14 +53,14 @@ private void VerifyMarksPresent(LegalityAnalysis data, IRibbonIndex m)
|
||||
|
||||
if (hasOne)
|
||||
{
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingFInvalid_0, GetRibbonNameSafe(mark))));
|
||||
data.AddLine(GetInvalid(RibbonMarkingInvalid_0, (ushort)mark));
|
||||
return;
|
||||
}
|
||||
|
||||
bool result = MarkRules.IsEncounterMarkValid(mark, data.Entity, data.EncounterMatch);
|
||||
if (!result)
|
||||
{
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingFInvalid_0, GetRibbonNameSafe(mark))));
|
||||
data.AddLine(GetInvalid(RibbonMarkingInvalid_0, (ushort)mark));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,14 +68,6 @@ private void VerifyMarksPresent(LegalityAnalysis data, IRibbonIndex m)
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetRibbonNameSafe(RibbonIndex index)
|
||||
{
|
||||
if (index >= MAX_COUNT)
|
||||
return index.ToString();
|
||||
var expect = $"Ribbon{index}";
|
||||
return RibbonStrings.GetName(expect);
|
||||
}
|
||||
|
||||
private void VerifyAffixedRibbonMark(LegalityAnalysis data, IRibbonIndex m)
|
||||
{
|
||||
if (m is not IRibbonSetAffixed a)
|
||||
@@ -90,7 +81,7 @@ private void VerifyAffixedRibbonMark(LegalityAnalysis data, IRibbonIndex m)
|
||||
var max = MarkRules.GetMaxAffixValue(data.Info.EvoChainsAllGens);
|
||||
if ((sbyte)max == -1 || affix > max)
|
||||
{
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, GetRibbonNameSafe(affix))));
|
||||
data.AddLine(GetInvalid(RibbonMarkingAffixed_0, (ushort)affix));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -114,7 +105,7 @@ private void VerifyShedinjaAffixed(LegalityAnalysis data, RibbonIndex affix, PKM
|
||||
if (affix.IsEncounterMark8())
|
||||
{
|
||||
if (!MarkRules.IsEncounterMarkValid(affix, pk, enc))
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, GetRibbonNameSafe(affix))));
|
||||
data.AddLine(GetInvalid(RibbonMarkingAffixed_0, (ushort)affix));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -129,10 +120,9 @@ private void VerifyShedinjaAffixed(LegalityAnalysis data, RibbonIndex affix, PKM
|
||||
clone.Species = (int) Species.Nincada;
|
||||
var args = new RibbonVerifierArguments(clone, enc, data.Info.EvoChainsAllGens);
|
||||
affix.Fix(args, true);
|
||||
var name = GetRibbonNameSafe(affix);
|
||||
bool invalid = RibbonVerifier.IsValidExtra(affix, args);
|
||||
var severity = invalid ? Severity.Invalid : Severity.Fishy;
|
||||
data.AddLine(Get(string.Format(LRibbonMarkingAffixedF_0, name), severity));
|
||||
data.AddLine(Get(severity, RibbonMarkingAffixed_0, (ushort)affix));
|
||||
}
|
||||
|
||||
private static bool IsMoveSetEvolvedShedinja(PKM pk)
|
||||
@@ -153,6 +143,6 @@ private void EnsureHasRibbon(LegalityAnalysis data, IRibbonIndex m, RibbonIndex
|
||||
{
|
||||
var hasRibbon = m.GetRibbonIndex(affix);
|
||||
if (!hasRibbon)
|
||||
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, GetRibbonNameSafe(affix))));
|
||||
data.AddLine(GetInvalid(RibbonMarkingAffixed_0, (ushort)affix));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static PKHeX.Core.CheckIdentifier;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -21,7 +21,7 @@ private void VerifyFavoriteMark(LegalityAnalysis data, PKM pk)
|
||||
{
|
||||
// Can only be toggled on in LGP/E, and is retained via transfer to HOME and into other games.
|
||||
if (pk is IFavorite { IsFavorite: true } && !data.Info.EvoChainsAllGens.HasVisitedLGPE)
|
||||
data.AddLine(GetInvalid(LFavoriteMarkingUnavailable));
|
||||
data.AddLine(GetInvalid(FavoriteMarkingUnavailable));
|
||||
}
|
||||
|
||||
private void VerifyMarkValue(LegalityAnalysis data, PKM pk)
|
||||
@@ -47,14 +47,14 @@ private void VerifyMarkValueDual(LegalityAnalysis data, IAppliedMarkings7 pk, us
|
||||
if (mv == 0)
|
||||
return;
|
||||
if (mv > Dual6)
|
||||
data.AddLine(GetInvalid(LMarkValueUnusedBitsPresent));
|
||||
data.AddLine(GetInvalid(MarkValueUnusedBitsPresent));
|
||||
|
||||
var count = pk.MarkingCount;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var value = pk.GetMarking(i);
|
||||
if (value is not (0 or MarkingColor.Blue or MarkingColor.Pink))
|
||||
data.AddLine(GetInvalid(string.Format(LMarkValueOutOfRange_0, i)));
|
||||
data.AddLine(GetInvalid(MarkValueOutOfRange_0, (ushort)i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ private void VerifyMarkValueSingle(LegalityAnalysis data, IAppliedMarkings3 pk,
|
||||
if (mv == 0)
|
||||
return;
|
||||
if (!IsMarkValueValid3456(pk, mv))
|
||||
data.AddLine(GetInvalid(LMarkValueUnusedBitsPresent));
|
||||
data.AddLine(GetInvalid(MarkValueUnusedBitsPresent));
|
||||
}
|
||||
|
||||
private static bool IsMarkValueValid3456(IAppliedMarkings3 pk, int value)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -22,18 +22,18 @@ private void VerifyMedalsRegular(LegalityAnalysis data)
|
||||
var Info = data.Info;
|
||||
uint value = train.SuperTrainBitFlags;
|
||||
if ((value & 3) != 0) // 2 unused flags
|
||||
data.AddLine(GetInvalid(LSuperUnused));
|
||||
data.AddLine(GetInvalid(SuperUnused));
|
||||
int TrainCount = train.SuperTrainingMedalCount();
|
||||
|
||||
if (pk.IsEgg)
|
||||
{
|
||||
// Can't have any super training data as an egg.
|
||||
if (TrainCount > 0)
|
||||
data.AddLine(GetInvalid(LSuperEgg));
|
||||
data.AddLine(GetInvalid(SuperEgg));
|
||||
if (train.SecretSuperTrainingUnlocked)
|
||||
data.AddLine(GetInvalid(LSuperNoUnlocked));
|
||||
data.AddLine(GetInvalid(SuperNoUnlocked));
|
||||
if (train.SecretSuperTrainingComplete)
|
||||
data.AddLine(GetInvalid(LSuperNoComplete));
|
||||
data.AddLine(GetInvalid(SuperNoComplete));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -41,11 +41,11 @@ private void VerifyMedalsRegular(LegalityAnalysis data)
|
||||
{
|
||||
// Can't have any super training data if it never visited Gen6.
|
||||
if (TrainCount > 0)
|
||||
data.AddLine(GetInvalid(LSuperUnavailable));
|
||||
data.AddLine(GetInvalid(SuperUnavailable));
|
||||
if (train.SecretSuperTrainingUnlocked)
|
||||
data.AddLine(GetInvalid(LSuperNoUnlocked));
|
||||
data.AddLine(GetInvalid(SuperNoUnlocked));
|
||||
if (train.SecretSuperTrainingComplete)
|
||||
data.AddLine(GetInvalid(LSuperNoComplete));
|
||||
data.AddLine(GetInvalid(SuperNoComplete));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -53,15 +53,15 @@ private void VerifyMedalsRegular(LegalityAnalysis data)
|
||||
{
|
||||
// Gen6->Gen7 transfer wipes the two Secret flags.
|
||||
if (train.SecretSuperTrainingUnlocked)
|
||||
data.AddLine(GetInvalid(LSuperNoUnlocked));
|
||||
data.AddLine(GetInvalid(SuperNoUnlocked));
|
||||
if (train.SecretSuperTrainingComplete)
|
||||
data.AddLine(GetInvalid(LSuperNoComplete));
|
||||
data.AddLine(GetInvalid(SuperNoComplete));
|
||||
return;
|
||||
}
|
||||
|
||||
// Only reach here if Format==6.
|
||||
if (TrainCount == 30 ^ train.SecretSuperTrainingComplete)
|
||||
data.AddLine(GetInvalid(LSuperComplete));
|
||||
data.AddLine(GetInvalid(SuperComplete));
|
||||
}
|
||||
|
||||
private void VerifyMedalsEvent(LegalityAnalysis data)
|
||||
@@ -69,6 +69,6 @@ private void VerifyMedalsEvent(LegalityAnalysis data)
|
||||
var pk = data.Entity;
|
||||
byte value = pk.Data[0x3A];
|
||||
if (value != 0)
|
||||
data.AddLine(GetInvalid(LSuperDistro));
|
||||
data.AddLine(GetInvalid(SuperDistro));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static PKHeX.Core.MemoryPermissions;
|
||||
using static PKHeX.Core.EntityContext;
|
||||
|
||||
@@ -85,7 +85,7 @@ private void VerifyHTLanguage(LegalityAnalysis data, MemorySource source)
|
||||
if (pk is not IHandlerLanguage h)
|
||||
return;
|
||||
if (!GetIsHTLanguageValid(data.EncounterMatch, pk, h.HandlingTrainerLanguage, source))
|
||||
data.AddLine(GetInvalid(LMemoryHTLanguage));
|
||||
data.AddLine(GetInvalid(MemoryHTLanguage));
|
||||
}
|
||||
|
||||
private static bool GetIsHTLanguageValid(IEncounterTemplate enc, PKM pk, byte language, MemorySource source)
|
||||
@@ -142,16 +142,16 @@ private CheckResult VerifyCommonMemory(PKM pk, int handler, LegalInfo info, Memo
|
||||
return VerifyMemoryHM6(info, mem, memory, hmIndex);
|
||||
|
||||
if (mem.IsInvalidGeneralLocationMemoryValue(memory.MemoryID, memory.Variable, info.EncounterMatch, pk))
|
||||
return GetInvalid(string.Format(LMemoryArgBadLocation, memory.Handler));
|
||||
return GetInvalid(MemoryArgBadLocation_H, memory.Handler);
|
||||
|
||||
if (mem.IsInvalidMiscMemory(memory.MemoryID, memory.Variable, (Species)pk.Species, pk.Version, handler))
|
||||
return GetInvalid(string.Format(LMemoryArgBadID, memory.Handler));
|
||||
return GetInvalid(MemoryArgBadID_H, memory.Handler);
|
||||
|
||||
switch (memory.MemoryID)
|
||||
{
|
||||
case 19 when pk.Species is (int)Species.Urshifu && memory.Variable is not 34: // tall building is the only location for evolving Urshifu
|
||||
case 19 when pk.Species is (int)Species.Runerigus && memory.Variable is not 72: // vast field is the only location for evolving Runerigus
|
||||
return GetInvalid(string.Format(LMemoryArgBadLocation, memory.Handler));
|
||||
return GetInvalid(MemoryArgBadLocation_H, memory.Handler);
|
||||
|
||||
// {0} saw {2} carrying {1} on its back. {4} that {3}.
|
||||
case 21 when mem.Context != Gen6 || !PersonalTable.AO.GetFormEntry(memory.Variable, 0).GetIsLearnHM(2): // Fly
|
||||
@@ -170,7 +170,7 @@ private CheckResult VerifyCommonMemory(PKM pk, int handler, LegalInfo info, Memo
|
||||
case 71 when !GetCanDynamaxTrainer(memory.Variable, 8, handler == 0 ? pk.Version : GameVersion.Any):
|
||||
// {0} battled {2} and Dynamaxed upon {1}’s instruction. {4} that {3}.
|
||||
case 72 when !PersonalTable.SWSH.IsSpeciesInGame(memory.Variable):
|
||||
return GetInvalid(string.Format(LMemoryArgBadSpecies, memory.Handler));
|
||||
return GetInvalid(MemoryArgBadSpecies_H1, memory.Handler, memory.Variable);
|
||||
|
||||
// Move
|
||||
// {0} studied about how to use {2} in a Box, thinking about {1}. {4} that {3}.
|
||||
@@ -181,23 +181,23 @@ private CheckResult VerifyCommonMemory(PKM pk, int handler, LegalInfo info, Memo
|
||||
// Species
|
||||
// With {1}, {0} went fishing, and they caught {2}. {4} that {3}.
|
||||
case 7 when !GetCanFishSpecies(memory.Variable, mem.Context, handler == 0 ? pk.Version : GameVersion.Any):
|
||||
return GetInvalid(string.Format(LMemoryArgBadSpecies, memory.Handler));
|
||||
return GetInvalid(MemoryArgBadSpecies_H1, memory.Handler, memory.Variable);
|
||||
|
||||
// {0} saw {1} paying attention to {2}. {4} that {3}.
|
||||
// {0} fought hard until it had to use Struggle when it battled at {1}’s side against {2}. {4} that {3}.
|
||||
// {0} was taken to a Pokémon Nursery by {1} and left with {2}. {4} that {3}.
|
||||
case 9 or 60 or 75 when mem.Context == Gen8 && !PersonalTable.SWSH.IsSpeciesInGame(memory.Variable):
|
||||
return GetInvalid(string.Format(LMemoryArgBadSpecies, memory.Handler));
|
||||
return GetInvalid(MemoryArgBadSpecies_H1, memory.Handler, memory.Variable);
|
||||
|
||||
// {0} had a great chat about {1} with the {2} that it was in a Box with. {4} that {3}.
|
||||
// {0} became good friends with the {2} in a Box, practiced moves with it, and talked about the day that {0} would be praised by {1}. {4} that {3}.
|
||||
// {0} got in a fight with the {2} that it was in a Box with about {1}. {4} that {3}.
|
||||
case 82 or 83 or 87 when !PersonalTable.SWSH.IsSpeciesInGame(memory.Variable):
|
||||
return GetInvalid(string.Format(LMemoryArgBadSpecies, memory.Handler));
|
||||
return GetInvalid(MemoryArgBadSpecies_H1, memory.Handler, memory.Variable);
|
||||
|
||||
// {0} had a very hard training session with {1}. {4} that {3}.
|
||||
case 53 when mem.Context == Gen8 && pk is IHyperTrain t && !t.IsHyperTrained():
|
||||
return GetInvalid(string.Format(LMemoryArgBadID, memory.Handler));
|
||||
return GetInvalid(MemoryArgBadID_H, memory.Handler);
|
||||
|
||||
// Item
|
||||
// {0} went to a Pokémon Center with {1} to buy {2}. {4} that {3}.
|
||||
@@ -215,7 +215,7 @@ private CheckResult VerifyCommonMemory(PKM pk, int handler, LegalInfo info, Memo
|
||||
// {0} was worried if {1} was looking for the {2} that it was holding in a Box. {4} that {3}.
|
||||
// When {0} was in a Box, it thought about the reason why {1} had it hold the {2}. {4} that {3}.
|
||||
case 84 or 88 when !Legal.HeldItems_SWSH.Contains(memory.Variable) || pk.IsEgg:
|
||||
return GetInvalid(string.Format(LMemoryArgBadItem, memory.Handler));
|
||||
return GetInvalid(Identifier, MemoryArgBadItem_H1, memory.Handler, memory.Variable);
|
||||
}
|
||||
|
||||
return VerifyCommonMemoryEtc(memory, mem);
|
||||
@@ -244,22 +244,25 @@ private CheckResult VerifyMemoryHM6(LegalInfo info, MemoryContext mem, MemoryVar
|
||||
return BadSpeciesMove(memory.Handler);
|
||||
}
|
||||
|
||||
private CheckResult BadSpeciesMove(string handler) => GetInvalid(string.Format(LMemoryArgBadMove, handler));
|
||||
private CheckResult BadSpeciesMove(byte handler) => GetInvalid(MemoryArgBadMove_H1, handler);
|
||||
|
||||
private CheckResult VerifyCommonMemoryEtc(MemoryVariableSet memory, MemoryContext context)
|
||||
{
|
||||
if (!context.CanHaveIntensity(memory.MemoryID, memory.Intensity))
|
||||
{
|
||||
var min = context.GetMinimumIntensity(memory.MemoryID);
|
||||
return GetInvalid(string.Format(LMemoryIndexIntensityMin, memory.Handler, min));
|
||||
return GetInvalid(Identifier, MemoryIndexIntensityMin_H1, memory.Handler, min);
|
||||
}
|
||||
|
||||
if (!context.CanHaveFeeling(memory.MemoryID, memory.Feeling, memory.Variable))
|
||||
return GetInvalid(string.Format(LMemoryFeelInvalid, memory.Handler));
|
||||
return GetInvalid(MemoryFeelInvalid_H, memory.Handler);
|
||||
|
||||
return GetValid(string.Format(LMemoryF_0_Valid, memory.Handler));
|
||||
return GetValid(MemoryValid_H, memory.Handler);
|
||||
}
|
||||
|
||||
private const ushort L_XOT = 0; // Original Trainer Memory
|
||||
private const ushort L_XHT = 1; // Handling Trainer Memory
|
||||
|
||||
/// <summary>
|
||||
/// Used for enforcing a fixed memory detail.
|
||||
/// </summary>
|
||||
@@ -272,19 +275,19 @@ private void VerifyOTMemoryIs(LegalityAnalysis data, byte m, byte i, ushort t, b
|
||||
{
|
||||
var pk = (ITrainerMemories)data.Entity;
|
||||
if (pk.OriginalTrainerMemory != m)
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryIndexID, L_XOT, m)));
|
||||
data.AddLine(GetInvalid(MemoryIndexID_H1, L_XOT, m));
|
||||
if (pk.OriginalTrainerMemoryIntensity != i)
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryIndexIntensity, L_XOT, i)));
|
||||
data.AddLine(GetInvalid(MemoryIndexIntensity_H1, L_XOT, i));
|
||||
if (pk.OriginalTrainerMemoryVariable != t)
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryIndexVar, L_XOT, t)));
|
||||
data.AddLine(GetInvalid(MemoryIndexVar_H1, L_XOT, t));
|
||||
if (pk.OriginalTrainerMemoryFeeling != f)
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryIndexFeel, L_XOT, f)));
|
||||
data.AddLine(GetInvalid(MemoryIndexFeel_H1, L_XOT, f));
|
||||
}
|
||||
|
||||
private void VerifyHTMemoryNone(LegalityAnalysis data, ITrainerMemories pk)
|
||||
{
|
||||
if (pk.HandlingTrainerMemory != 0 || pk.HandlingTrainerMemoryVariable != 0 || pk.HandlingTrainerMemoryIntensity != 0 || pk.HandlingTrainerMemoryFeeling != 0)
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryCleared, L_XHT)));
|
||||
data.AddLine(GetInvalid(MemoryCleared_H, L_XHT));
|
||||
}
|
||||
|
||||
private void VerifyOTMemory(LegalityAnalysis data)
|
||||
@@ -339,37 +342,37 @@ private void VerifyOTMemory(LegalityAnalysis data)
|
||||
// Bounds checking
|
||||
var mc = Memories.GetContext(context);
|
||||
if (!mc.CanObtainMemoryOT(pk.Version, memory))
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryArgBadID, L_XOT)));
|
||||
data.AddLine(GetInvalid(MemoryArgBadID_H, L_XOT));
|
||||
|
||||
// Verify memory if specific to OT
|
||||
switch (memory)
|
||||
{
|
||||
// No Memory
|
||||
case 0: // SW/SH trades don't set HT memories immediately, which is hilarious.
|
||||
data.AddLine(Get(LMemoryMissingOT, mc.Context == Gen8 ? Severity.Fishy : Severity.Invalid));
|
||||
data.AddLine(Get(mc.Context == Gen8 ? Severity.Fishy : Severity.Invalid, MemoryMissingOT));
|
||||
VerifyOTMemoryIs(data, 0, 0, 0, 0);
|
||||
return;
|
||||
|
||||
// {0} hatched from an Egg and saw {1} for the first time at... {2}. {4} that {3}.
|
||||
case 2 when !enc.IsEgg:
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryArgBadHatch, L_XOT)));
|
||||
data.AddLine(GetInvalid(MemoryArgBadHatch_H, L_XOT));
|
||||
break;
|
||||
|
||||
// {0} became {1}’s friend when it arrived via Link Trade at... {2}. {4} that {3}.
|
||||
case 4 when mc.Context == Gen6: // Gen8 applies this memory erroneously
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryArgBadOTEgg, L_XOT)));
|
||||
data.AddLine(GetInvalid(MemoryArgBadOTEgg_H, L_XOT));
|
||||
return;
|
||||
|
||||
// {0} went to the Pokémon Center in {2} with {1} and had its tired body healed there. {4} that {3}.
|
||||
case 6 when !mc.HasPokeCenter(pk.Version, mem.OriginalTrainerMemoryVariable):
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryArgBadLocation, L_XOT)));
|
||||
data.AddLine(GetInvalid(MemoryArgBadLocation_H, L_XOT));
|
||||
return;
|
||||
|
||||
// {0} was with {1} when {1} caught {2}. {4} that {3}.
|
||||
case 14:
|
||||
var result = GetCanBeCaptured(mem.OriginalTrainerMemoryVariable, mc.Context, pk.Version) // Any Game in the Handling Trainer's generation
|
||||
? GetValid(string.Format(LMemoryArgSpecies, L_XOT))
|
||||
: GetInvalid(string.Format(LMemoryArgBadSpecies, L_XOT));
|
||||
? GetValid(MemoryArgSpecies_H, L_XOT)
|
||||
: GetInvalid(MemoryArgBadSpecies_H1, L_XOT, mem.OriginalTrainerMemoryVariable);
|
||||
data.AddLine(result);
|
||||
return;
|
||||
}
|
||||
@@ -426,7 +429,7 @@ private void VerifyHTMemory(LegalityAnalysis data, EntityContext memoryGen)
|
||||
// Bounds checking
|
||||
var mc = Memories.GetContext(memoryGen);
|
||||
if (!mc.CanObtainMemoryHT(pk.Version, memory))
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryArgBadID, L_XHT)));
|
||||
data.AddLine(GetInvalid(MemoryArgBadID_H, L_XHT));
|
||||
|
||||
// Verify memory if specific to HT
|
||||
switch (memory)
|
||||
@@ -440,30 +443,30 @@ private void VerifyHTMemory(LegalityAnalysis data, EntityContext memoryGen)
|
||||
_ => Severity.Invalid,
|
||||
};
|
||||
if (severity != Severity.Valid)
|
||||
data.AddLine(Get(LMemoryMissingHT, severity));
|
||||
data.AddLine(Get(severity, MemoryMissingHT));
|
||||
VerifyHTMemoryNone(data, mem);
|
||||
return;
|
||||
|
||||
// {0} met {1} at... {2}. {1} threw a Poké Ball at it, and they started to travel together. {4} that {3}.
|
||||
case 1:
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryArgBadCatch, L_XHT)));
|
||||
data.AddLine(GetInvalid(MemoryArgBadCatch_H, L_XHT));
|
||||
return;
|
||||
|
||||
// {0} hatched from an Egg and saw {1} for the first time at... {2}. {4} that {3}.
|
||||
case 2:
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryArgBadHatch, L_XHT)));
|
||||
data.AddLine(GetInvalid(MemoryArgBadHatch_H, L_XHT));
|
||||
return;
|
||||
|
||||
// {0} went to the Pokémon Center in {2} with {1} and had its tired body healed there. {4} that {3}.
|
||||
case 6 when !mc.HasPokeCenter(GameVersion.Any, mem.HandlingTrainerMemoryVariable):
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryArgBadLocation, L_XHT)));
|
||||
data.AddLine(GetInvalid(MemoryArgBadLocation_H, L_XHT));
|
||||
return;
|
||||
|
||||
// {0} was with {1} when {1} caught {2}. {4} that {3}.
|
||||
case 14:
|
||||
var result = GetCanBeCaptured(mem.HandlingTrainerMemoryVariable, mc.Context, GameVersion.Any) // Any Game in the Handling Trainer's generation
|
||||
? GetValid(string.Format(LMemoryArgSpecies, L_XHT))
|
||||
: GetInvalid(string.Format(LMemoryArgBadSpecies, L_XHT));
|
||||
? GetValid(MemoryArgSpecies_H, L_XHT)
|
||||
: GetInvalid(MemoryArgBadSpecies_H1, L_XHT, mem.HandlingTrainerMemoryVariable);
|
||||
data.AddLine(result);
|
||||
return;
|
||||
}
|
||||
@@ -492,12 +495,12 @@ private void VerifyHTMemoryTransferTo7(LegalityAnalysis data, PKM pk, LegalInfo
|
||||
return;
|
||||
|
||||
if (mem.HandlingTrainerMemory != 4)
|
||||
data.AddLine(Severity.Invalid, LMemoryIndexLinkHT, CheckIdentifier.Memory);
|
||||
data.AddLine(GetInvalid(MemoryIndexLinkHT, L_XHT, 4));
|
||||
if (mem.HandlingTrainerMemoryVariable != 0)
|
||||
data.AddLine(Severity.Invalid, LMemoryIndexArgHT, CheckIdentifier.Memory);
|
||||
data.AddLine(GetInvalid(MemoryIndexArgHT, L_XHT, 0));
|
||||
if (mem.HandlingTrainerMemoryIntensity != 1)
|
||||
data.AddLine(Severity.Invalid, LMemoryIndexIntensityHT1, CheckIdentifier.Memory);
|
||||
if (mem.HandlingTrainerMemoryFeeling > 10)
|
||||
data.AddLine(Severity.Invalid, LMemoryIndexFeelHT09, CheckIdentifier.Memory);
|
||||
data.AddLine(GetInvalid(MemoryIndexIntensityHT1, L_XHT, 1));
|
||||
if (mem.HandlingTrainerMemoryFeeling >= 10)
|
||||
data.AddLine(GetInvalid(MemoryIndexFeelHTLEQ9, L_XHT));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public static void SetMaxContestStats(this PKM pk, IEncounterTemplate enc, Evolu
|
||||
_ => h.HasVisitedBDSP ? CorrelateSheen : None, // BD/SP Contests
|
||||
};
|
||||
|
||||
public static int CalculateMaximumSheen(IContestStatsReadOnly s, Nature nature, IContestStatsReadOnly initial, bool pokeBlock3)
|
||||
public static byte CalculateMaximumSheen(IContestStatsReadOnly s, Nature nature, IContestStatsReadOnly initial, bool pokeBlock3)
|
||||
{
|
||||
if (s.IsAnyContestStatMax())
|
||||
return MaxContestStat;
|
||||
@@ -86,10 +86,10 @@ public static int CalculateMaximumSheen(IContestStatsReadOnly s, Nature nature,
|
||||
return 59;
|
||||
|
||||
// Can get trash poffins by burning and spilling on purpose.
|
||||
return Math.Min(MaxContestStat, avg * HighestFeelPoffin8b);
|
||||
return (byte)Math.Min(MaxContestStat, avg * HighestFeelPoffin8b);
|
||||
}
|
||||
|
||||
public static int CalculateMinimumSheen(IContestStatsReadOnly s, IContestStatsReadOnly initial, INature pk, ContestStatGrantingSheen method) => method switch
|
||||
public static byte CalculateMinimumSheen(IContestStatsReadOnly s, IContestStatsReadOnly initial, INature pk, ContestStatGrantingSheen method) => method switch
|
||||
{
|
||||
ContestStatGrantingSheen.Gen8b => CalculateMinimumSheen8b(s, pk.Nature, initial),
|
||||
ContestStatGrantingSheen.Gen3 => CalculateMinimumSheen3(s, pk.Nature, initial),
|
||||
@@ -98,7 +98,7 @@ public static int CalculateMaximumSheen(IContestStatsReadOnly s, Nature nature,
|
||||
};
|
||||
|
||||
// BD/SP has a slightly better stat:sheen ratio than Gen4; prefer if it has visited.
|
||||
public static int CalculateMinimumSheen8b(IContestStatsReadOnly s, Nature nature, IContestStatsReadOnly initial)
|
||||
public static byte CalculateMinimumSheen8b(IContestStatsReadOnly s, Nature nature, IContestStatsReadOnly initial)
|
||||
{
|
||||
if (s.IsContestEqual(initial))
|
||||
return initial.ContestSheen;
|
||||
@@ -111,10 +111,10 @@ public static int CalculateMinimumSheen8b(IContestStatsReadOnly s, Nature nature
|
||||
avg = Math.Min(rawAvg, avg); // be generous
|
||||
avg = (BestSheenStat8b * avg) / MaxContestStat;
|
||||
|
||||
return Math.Clamp(avg, LowestFeelPoffin8b, BestSheenStat8b);
|
||||
return (byte)Math.Clamp(avg, LowestFeelPoffin8b, BestSheenStat8b);
|
||||
}
|
||||
|
||||
public static int CalculateMinimumSheen3(IContestStatsReadOnly s, Nature nature, IContestStatsReadOnly initial)
|
||||
public static byte CalculateMinimumSheen3(IContestStatsReadOnly s, Nature nature, IContestStatsReadOnly initial)
|
||||
{
|
||||
if (s.IsContestEqual(initial))
|
||||
return initial.ContestSheen;
|
||||
@@ -127,10 +127,10 @@ public static int CalculateMinimumSheen3(IContestStatsReadOnly s, Nature nature,
|
||||
avg = Math.Min(rawAvg, avg); // be generous
|
||||
|
||||
avg = (BestSheenStat3 * avg) / MaxContestStat;
|
||||
return Math.Clamp(avg, LowestFeelBlock3, BestSheenStat3);
|
||||
return (byte)Math.Clamp(avg, LowestFeelBlock3, BestSheenStat3);
|
||||
}
|
||||
|
||||
public static int CalculateMinimumSheen4(IContestStatsReadOnly s, Nature nature, IContestStatsReadOnly initial)
|
||||
public static byte CalculateMinimumSheen4(IContestStatsReadOnly s, Nature nature, IContestStatsReadOnly initial)
|
||||
{
|
||||
if (s.IsContestEqual(initial))
|
||||
return initial.ContestSheen;
|
||||
@@ -142,10 +142,10 @@ public static int CalculateMinimumSheen4(IContestStatsReadOnly s, Nature nature,
|
||||
var avg = Math.Max(1, (byte)nature % 6 == 0 ? rawAvg : GetAverageFeel(s, nature, initial));
|
||||
avg = Math.Min(rawAvg, avg); // be generous
|
||||
|
||||
return Math.Clamp(avg, LowestFeelPoffin4, MaxContestStat);
|
||||
return (byte)Math.Clamp(avg, LowestFeelPoffin4, MaxContestStat);
|
||||
}
|
||||
|
||||
private static int CalculateMaximumSheen3(IContestStatsReadOnly s, Nature nature, IContestStatsReadOnly initial)
|
||||
private static byte CalculateMaximumSheen3(IContestStatsReadOnly s, Nature nature, IContestStatsReadOnly initial)
|
||||
{
|
||||
// By using Enigma and Lansat and a 25 +1/-1, can get a +9/+19s at minimum RPM
|
||||
// By using Strib, Chilan, Niniku, or Topo, can get a black +2/2/2 & 83 block (6:83) at minimum RPM.
|
||||
@@ -164,7 +164,7 @@ private static int CalculateMaximumSheen3(IContestStatsReadOnly s, Nature nature
|
||||
|
||||
// Prefer the bad-black-block correlation if more than 3 stats have gains >= 2.
|
||||
var permit = has3 ? (sum * 83 / 6) : (sum * 19 / 9);
|
||||
return Math.Clamp(permit, LowestFeelBlock3, MaxContestStat);
|
||||
return (byte)Math.Clamp(permit, LowestFeelBlock3, MaxContestStat);
|
||||
}
|
||||
|
||||
private static int GetAverageFeel(IContestStatsReadOnly s, Nature nature, IContestStatsReadOnly initial)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static PKHeX.Core.CheckIdentifier;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -23,36 +22,36 @@ public override void Verify(LegalityAnalysis data)
|
||||
|
||||
// No egg have contest stats from the encounter.
|
||||
if (pk is IContestStatsReadOnly s && s.HasContestStats())
|
||||
data.AddLine(GetInvalid(LEggContest, Egg));
|
||||
data.AddLine(GetInvalid(Egg, EggContest));
|
||||
|
||||
// Cannot transfer eggs across contexts (must be hatched).
|
||||
var e = data.EncounterOriginal;
|
||||
if (e.Context != pk.Context)
|
||||
data.AddLine(GetInvalid(LTransferEggVersion, Egg));
|
||||
data.AddLine(GetInvalid(Egg, TransferEggVersion));
|
||||
|
||||
switch (pk)
|
||||
{
|
||||
// Side Game: No Eggs
|
||||
case SK2 or CK3 or XK3 or BK4 or RK4 when e.Context == pk.Context:
|
||||
data.AddLine(GetInvalid(LTransferEggVersion, Egg));
|
||||
data.AddLine(GetInvalid(Egg, TransferEggVersion));
|
||||
break;
|
||||
|
||||
// All Eggs are Japanese and flagged specially for localized string
|
||||
case PK3 when pk.Language != 1:
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, LanguageID.Japanese, (LanguageID)pk.Language), Egg));
|
||||
data.AddLine(GetInvalid(Egg, OTLanguageShouldBe_0, (byte)LanguageID.Japanese));
|
||||
break;
|
||||
|
||||
// Cannot obtain Shiny Leaf or Pokeathlon Stats as Egg
|
||||
case PK4 pk4:
|
||||
if (pk4.ShinyLeaf != 0)
|
||||
data.AddLine(GetInvalid(LEggShinyLeaf, Egg));
|
||||
data.AddLine(GetInvalid(Egg, EggShinyLeaf));
|
||||
if (pk4.PokeathlonStat != 0)
|
||||
data.AddLine(GetInvalid(LEggPokeathlon, Egg));
|
||||
data.AddLine(GetInvalid(Egg, EggPokeathlon));
|
||||
break;
|
||||
}
|
||||
|
||||
if (pk is IHomeTrack { HasTracker: true })
|
||||
data.AddLine(GetInvalid(LTransferTrackerShouldBeZero));
|
||||
data.AddLine(GetInvalid(TransferTrackerShouldBeZero));
|
||||
}
|
||||
|
||||
switch (pk)
|
||||
@@ -88,7 +87,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
if (s64.TryGetSeed(pk, out var seed))
|
||||
data.Info.PIDIV = new PIDIV(PIDType.Xoroshiro, seed);
|
||||
if (enc is IMasteryInitialMoveShop8 m && !m.IsForcedMasteryCorrect(pk))
|
||||
data.AddLine(GetInvalid(LEncMasteryInitial));
|
||||
data.AddLine(GetInvalid(EncMasteryInitial));
|
||||
}
|
||||
|
||||
VerifyMiscFatefulEncounter(data);
|
||||
@@ -111,7 +110,7 @@ private void VerifyCorrelation8b(LegalityAnalysis data, IStaticCorrelation8b s8b
|
||||
};
|
||||
|
||||
if (!valid)
|
||||
data.AddLine(GetInvalid(LPIDTypeMismatch));
|
||||
data.AddLine(GetInvalid(PIDTypeMismatch));
|
||||
}
|
||||
|
||||
private void VerifyCorrelation8(LegalityAnalysis data, IOverworldCorrelation8 z, PKM pk)
|
||||
@@ -132,7 +131,7 @@ private void VerifyCorrelation8(LegalityAnalysis data, IOverworldCorrelation8 z,
|
||||
};
|
||||
|
||||
if (!valid)
|
||||
data.AddLine(GetInvalid(LPIDTypeMismatch));
|
||||
data.AddLine(GetInvalid(PIDTypeMismatch));
|
||||
}
|
||||
|
||||
private void VerifyServerDate2000(LegalityAnalysis data, PKM pk, IEncounterable enc, IEncounterServerDate date)
|
||||
@@ -144,18 +143,18 @@ private void VerifyServerDate2000(LegalityAnalysis data, PKM pk, IEncounterable
|
||||
if (enc is WB8 { IsDateLockJapanese: true } or WA8 { IsDateLockJapanese: true })
|
||||
{
|
||||
if (actualDay < new DateOnly(2022, 5, 20) && pk.Language != (int)LanguageID.Japanese)
|
||||
data.AddLine(GetInvalid(LDateOutsideDistributionWindow));
|
||||
data.AddLine(GetInvalid(DateOutsideDistributionWindow));
|
||||
}
|
||||
|
||||
var result = date.IsWithinDistributionWindow(actualDay);
|
||||
if (result == EncounterServerDateCheck.Invalid)
|
||||
data.AddLine(GetInvalid(LDateOutsideDistributionWindow));
|
||||
data.AddLine(GetInvalid(DateOutsideDistributionWindow));
|
||||
}
|
||||
|
||||
private void VerifyStats7(LegalityAnalysis data, PK7 pk7)
|
||||
{
|
||||
if (pk7.ResortEventStatus >= ResortEventState.MAX)
|
||||
data.AddLine(GetInvalid(LTransferBad));
|
||||
data.AddLine(GetInvalid(TransferBad));
|
||||
}
|
||||
|
||||
private void VerifyMiscScaleValues(LegalityAnalysis data, PKM pk, IEncounterTemplate enc)
|
||||
@@ -168,20 +167,20 @@ private void VerifyMiscScaleValues(LegalityAnalysis data, PKM pk, IEncounterTemp
|
||||
{
|
||||
// Gen1-7 can have 0-0 if kept in PLA before HOME 3.0
|
||||
if (s2 is { HeightScalar: 0, WeightScalar: 0 } && !data.Info.EvoChainsAllGens.HasVisitedPLA && enc is not IPogoSlot)
|
||||
data.AddLine(Get(LStatInvalidHeightWeight, Severity.Invalid, Encounter));
|
||||
data.AddLine(Get(Encounter, Severity.Invalid, StatInvalidHeightWeight));
|
||||
}
|
||||
else if (CheckHeightWeightOdds(data.EncounterMatch))
|
||||
{
|
||||
if (s2 is { HeightScalar: 0, WeightScalar: 0 })
|
||||
{
|
||||
if (ParseSettings.Settings.HOMETransfer.ZeroHeightWeight != Severity.Valid)
|
||||
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.Settings.HOMETransfer.ZeroHeightWeight, Encounter));
|
||||
data.AddLine(Get(Encounter, ParseSettings.Settings.HOMETransfer.ZeroHeightWeight, StatInvalidHeightWeight));
|
||||
}
|
||||
}
|
||||
|
||||
// Check for Scale
|
||||
if (pk is IScaledSize3 s3 && IsHeightScaleMatchRequired(pk) && s2.HeightScalar != s3.Scale)
|
||||
data.AddLine(GetInvalid(LStatIncorrectHeightValue));
|
||||
data.AddLine(GetInvalid(StatIncorrectHeightValue));
|
||||
}
|
||||
|
||||
private void VerifyIsMovesetAllowed(LegalityAnalysis data, SK2 sk2)
|
||||
@@ -220,18 +219,18 @@ private static void VerifyStats5(LegalityAnalysis data, PK5 pk5)
|
||||
|
||||
// Cannot participate in Pokestar Studios as Egg
|
||||
if (pk5.IsEgg && pk5.PokeStarFame != 0)
|
||||
data.AddLine(GetInvalid(LEggShinyPokeStar, Egg));
|
||||
data.AddLine(GetInvalid(Egg, EggShinyPokeStar));
|
||||
|
||||
// Ensure NSparkle is only present on N's encounters.
|
||||
if (enc is EncounterStatic5N)
|
||||
{
|
||||
if (!pk5.NSparkle)
|
||||
data.AddLine(GetInvalid(LG5SparkleRequired, Fateful));
|
||||
data.AddLine(GetInvalid(Fateful, G5SparkleRequired));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pk5.NSparkle)
|
||||
data.AddLine(GetInvalid(LG5SparkleInvalid, Fateful));
|
||||
data.AddLine(GetInvalid(Fateful, G5SparkleInvalid));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,35 +247,35 @@ private void VerifyStats9(LegalityAnalysis data, PK9 pk9)
|
||||
VerifyTechRecordSV(data, pk9);
|
||||
|
||||
if (!pk9.IsBattleVersionValid(data.Info.EvoChainsAllGens))
|
||||
data.AddLine(GetInvalid(LStatBattleVersionInvalid));
|
||||
data.AddLine(GetInvalid(StatBattleVersionInvalid));
|
||||
if (!IsObedienceLevelValid(pk9, pk9.ObedienceLevel, pk9.MetLevel))
|
||||
data.AddLine(GetInvalid(LTransferObedienceLevel));
|
||||
data.AddLine(GetInvalid(TransferObedienceLevel));
|
||||
if (pk9.IsEgg)
|
||||
{
|
||||
if (pk9.TeraTypeOverride != (MoveType)TeraTypeUtil.OverrideNone)
|
||||
data.AddLine(GetInvalid(LTeraTypeIncorrect));
|
||||
data.AddLine(GetInvalid(TeraTypeIncorrect));
|
||||
}
|
||||
else if (pk9.Species == (int)Species.Terapagos)
|
||||
{
|
||||
if (!TeraTypeUtil.IsValidTerapagos((byte)pk9.TeraTypeOverride))
|
||||
data.AddLine(GetInvalid(LTeraTypeIncorrect));
|
||||
data.AddLine(GetInvalid(TeraTypeIncorrect));
|
||||
}
|
||||
else if (pk9.Species == (int)Species.Ogerpon)
|
||||
{
|
||||
if (!TeraTypeUtil.IsValidOgerpon((byte)pk9.TeraTypeOverride, pk9.Form))
|
||||
data.AddLine(GetInvalid(LTeraTypeIncorrect));
|
||||
data.AddLine(GetInvalid(TeraTypeIncorrect));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!TeraTypeUtil.IsValid((byte)pk9.TeraTypeOriginal))
|
||||
data.AddLine(GetInvalid(LTeraTypeIncorrect));
|
||||
data.AddLine(GetInvalid(TeraTypeIncorrect));
|
||||
}
|
||||
|
||||
var enc = data.EncounterOriginal;
|
||||
if (enc is EncounterEgg9 g)
|
||||
{
|
||||
if (!Tera9RNG.IsMatchTeraTypePersonalEgg(g.Species, g.Form, (byte)pk9.TeraTypeOriginal))
|
||||
data.AddLine(GetInvalid(LTeraTypeMismatch));
|
||||
data.AddLine(GetInvalid(TeraTypeMismatch));
|
||||
}
|
||||
else if (enc is ITeraRaid9)
|
||||
{
|
||||
@@ -286,15 +285,15 @@ private void VerifyStats9(LegalityAnalysis data, PK9 pk9)
|
||||
else if (enc is not { Context: EntityContext.Gen9 } || pk9 is { GO_HOME: true })
|
||||
{
|
||||
if (pk9.TeraTypeOverride == (MoveType)TeraTypeUtil.OverrideNone)
|
||||
data.AddLine(GetInvalid(LTeraTypeIncorrect));
|
||||
data.AddLine(GetInvalid(TeraTypeIncorrect));
|
||||
else if (GetTeraImportMatch(data.Info.EvoChainsAllGens.Gen9, pk9.TeraTypeOriginal, enc) == -1)
|
||||
data.AddLine(GetInvalid(LTeraTypeIncorrect));
|
||||
data.AddLine(GetInvalid(TeraTypeIncorrect));
|
||||
}
|
||||
else if (enc is EncounterStatic9 { StarterBoxLegend: true })
|
||||
{
|
||||
// Ride legends cannot be traded or transferred.
|
||||
if (pk9.CurrentHandler != 0 || pk9.Tracker != 0 || !pk9.IsUntraded)
|
||||
data.AddLine(GetInvalid(LTransferBad));
|
||||
data.AddLine(GetInvalid(TransferBad));
|
||||
}
|
||||
|
||||
if (!Locations9.IsAccessiblePreDLC(pk9.MetLocation))
|
||||
@@ -309,7 +308,7 @@ private void VerifyStats9(LegalityAnalysis data, PK9 pk9)
|
||||
// Safari and Sport are not obtainable in the base game.
|
||||
// For the learnset restricted cases, we need to check if the ball is available too.
|
||||
if (((BallUseLegality.WildPokeballs9PreDLC2 >> pk9.Ball) & 1) != 1)
|
||||
data.AddLine(GetInvalid(LBallUnavailable));
|
||||
data.AddLine(GetInvalid(BallUnavailable));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,11 +371,11 @@ private void VerifyMiscPokerus(LegalityAnalysis data)
|
||||
var days = pk.PokerusDays;
|
||||
bool strainValid = Pokerus.IsStrainValid(pk, data.Info.EncounterMatch, strain, days);
|
||||
if (!strainValid)
|
||||
data.AddLine(GetInvalid(string.Format(LPokerusStrainUnobtainable_0, strain)));
|
||||
data.AddLine(GetInvalid(PokerusStrainUnobtainable_0, (ushort)strain));
|
||||
|
||||
bool daysValid = Pokerus.IsDurationValid(strain, days, out var max);
|
||||
if (!daysValid)
|
||||
data.AddLine(GetInvalid(string.Format(LPokerusDaysTooHigh_0, max)));
|
||||
data.AddLine(GetInvalid(PokerusDaysLEQ_0, (ushort)max));
|
||||
}
|
||||
|
||||
public void VerifyMiscG1(LegalityAnalysis data)
|
||||
@@ -397,7 +396,7 @@ public void VerifyMiscG1(LegalityAnalysis data)
|
||||
_ => time is 1 or 2 or 3,
|
||||
};
|
||||
if (!valid)
|
||||
data.AddLine(new CheckResult(Severity.Invalid, Encounter, LMetDetailTimeOfDay));
|
||||
data.AddLine(GetInvalid(Encounter, MetDetailTimeOfDay));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -414,16 +413,16 @@ private void VerifyMiscG1Types(LegalityAnalysis data, PK1 pk1)
|
||||
// Can have any type combination of any species by using Conversion.
|
||||
if (!PersonalTable1.TypeIDExists(pk1.Type1))
|
||||
{
|
||||
data.AddLine(GetInvalid(LG1TypePorygonFail1));
|
||||
data.AddLine(GetInvalid(G1TypePorygonFail1));
|
||||
}
|
||||
if (!PersonalTable1.TypeIDExists(pk1.Type2))
|
||||
{
|
||||
data.AddLine(GetInvalid(LG1TypePorygonFail2));
|
||||
data.AddLine(GetInvalid(G1TypePorygonFail2));
|
||||
}
|
||||
else // Both types exist, ensure a Gen1 species has this combination
|
||||
{
|
||||
var matchSpecies = PersonalTable.RB.IsValidTypeCombination(pk1);
|
||||
var result = matchSpecies != -1 ? GetValid(LG1TypeMatchPorygon) : GetInvalid(LG1TypePorygonFail);
|
||||
var result = matchSpecies != -1 ? GetValid(G1TypeMatchPorygon) : GetInvalid(G1TypePorygonFail);
|
||||
data.AddLine(result);
|
||||
}
|
||||
}
|
||||
@@ -434,8 +433,8 @@ private void VerifyMiscG1Types(LegalityAnalysis data, PK1 pk1)
|
||||
if (!match2 && ParseSettings.AllowGBStadium2)
|
||||
match2 = (species is (int)Species.Magnemite or (int)Species.Magneton) && pk1.Type2 == 9; // Steel Magnemite via Stadium2
|
||||
|
||||
var first = match1 ? GetValid(LG1TypeMatch1) : GetInvalid(LG1Type1Fail);
|
||||
var second = match2 ? GetValid(LG1TypeMatch2) : GetInvalid(LG1Type2Fail);
|
||||
var first = match1 ? GetValid(G1TypeMatch1) : GetInvalid(G1Type1Fail);
|
||||
var second = match2 ? GetValid(G1TypeMatch2) : GetInvalid(G1Type2Fail);
|
||||
data.AddLine(first);
|
||||
data.AddLine(second);
|
||||
}
|
||||
@@ -454,7 +453,7 @@ private CheckResult GetWasTradeback(LegalityAnalysis data, PK1 pk1, TimeCapsuleE
|
||||
{
|
||||
var rate = pk1.CatchRate;
|
||||
if (PK1.IsCatchRateHeldItem(rate))
|
||||
return GetValid(LG1CatchRateMatchTradeback);
|
||||
return GetValid(G1CatchRateMatchTradeback);
|
||||
return GetWasNotTradeback(data, pk1, eval);
|
||||
}
|
||||
|
||||
@@ -462,20 +461,20 @@ private CheckResult GetWasNotTradeback(LegalityAnalysis data, PK1 pk1, TimeCapsu
|
||||
{
|
||||
var rate = pk1.CatchRate;
|
||||
if (MoveInfo.IsAnyFromGeneration(2, data.Info.Moves))
|
||||
return GetInvalid(LG1CatchRateItem);
|
||||
return GetInvalid(G1CatchRateItem);
|
||||
var e = data.EncounterMatch;
|
||||
if (e is EncounterGift1 { Version: GameVersion.Stadium } or EncounterTrade1)
|
||||
return GetValid(LG1CatchRateMatchPrevious); // Encounters detected by the catch rate, cant be invalid if match this encounters
|
||||
return GetValid(G1CatchRateMatchPrevious); // Encounters detected by the catch rate, cant be invalid if match this encounters
|
||||
|
||||
ushort species = pk1.Species;
|
||||
if (GBRestrictions.IsSpeciesNotAvailableCatchRate((byte)species) && rate == PersonalTable.RB[species].CatchRate)
|
||||
{
|
||||
if (species != (int)Species.Dragonite || rate != 45 || !(e.Version == GameVersion.BU || e.Version.Contains(GameVersion.YW)))
|
||||
return GetInvalid(LG1CatchRateEvo);
|
||||
return GetInvalid(G1CatchRateEvo);
|
||||
}
|
||||
if (!GBRestrictions.RateMatchesEncounter(e.Species, e.Version, rate))
|
||||
return GetInvalid(eval == TimeCapsuleEvaluation.Transferred12 ? LG1CatchRateChain : LG1CatchRateNone);
|
||||
return GetValid(LG1CatchRateMatchPrevious);
|
||||
return GetInvalid(eval == TimeCapsuleEvaluation.Transferred12 ? G1CatchRateChain : G1CatchRateNone);
|
||||
return GetValid(G1CatchRateMatchPrevious);
|
||||
}
|
||||
|
||||
private static void VerifyMiscFatefulEncounter(LegalityAnalysis data)
|
||||
@@ -509,7 +508,7 @@ private static void VerifyMiscFatefulEncounter(LegalityAnalysis data)
|
||||
return;
|
||||
}
|
||||
if (pk.FatefulEncounter)
|
||||
data.AddLine(GetInvalid(LFatefulInvalid, Fateful));
|
||||
data.AddLine(GetInvalid(Fateful, FatefulInvalid));
|
||||
}
|
||||
|
||||
private static void VerifyMiscEggCommon(LegalityAnalysis data)
|
||||
@@ -518,30 +517,17 @@ private static void VerifyMiscEggCommon(LegalityAnalysis data)
|
||||
|
||||
var enc = data.EncounterMatch;
|
||||
if (!EggStateLegality.GetIsEggHatchCyclesValid(pk, enc))
|
||||
data.AddLine(GetInvalid(LEggHatchCycles, Egg));
|
||||
data.AddLine(GetInvalid(Egg, EggHatchCycles));
|
||||
|
||||
if (pk.Format >= 6 && enc is IEncounterEgg && !MovesMatchRelearn(pk))
|
||||
{
|
||||
const int moveCount = 4;
|
||||
var sb = new StringBuilder(64);
|
||||
for (int i = 0; i < moveCount; i++)
|
||||
{
|
||||
var move = pk.GetRelearnMove(i);
|
||||
var name = ParseSettings.GetMoveName(move);
|
||||
sb.Append(name);
|
||||
if (i != moveCount - 1)
|
||||
sb.Append(", ");
|
||||
}
|
||||
var msg = string.Format(LMoveFExpect_0, sb);
|
||||
data.AddLine(GetInvalid(msg, Egg));
|
||||
}
|
||||
data.AddLine(GetInvalid(Egg, MovesShouldMatchRelearnMoves));
|
||||
|
||||
if (pk is ITechRecord record)
|
||||
{
|
||||
if (record.GetMoveRecordFlagAny())
|
||||
data.AddLine(GetInvalid(LEggRelearnFlags, Egg));
|
||||
data.AddLine(GetInvalid(Egg, EggRelearnFlags));
|
||||
if (pk.StatNature != pk.Nature)
|
||||
data.AddLine(GetInvalid(LEggNature, Egg));
|
||||
data.AddLine(GetInvalid(Egg, EggNature));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,14 +555,14 @@ private static void VerifyFatefulMysteryGift(LegalityAnalysis data, MysteryGift
|
||||
{
|
||||
var locToCheck = pk.IsEgg ? pk.MetLocation : pk.EggLocation;
|
||||
if (locToCheck is not (Locations.LinkTrade5 or Locations.LinkTrade5NPC))
|
||||
data.AddLine(GetInvalid(LPIDTypeMismatch, PID));
|
||||
data.AddLine(GetInvalid(PID, PIDTypeMismatch));
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldHave = g.FatefulEncounter;
|
||||
var result = pk.FatefulEncounter == shouldHave
|
||||
? GetValid(LFatefulMystery, Fateful)
|
||||
: GetInvalid(LFatefulMysteryMissing, Fateful);
|
||||
? GetValid(Fateful, FatefulMystery)
|
||||
: GetInvalid(Fateful, FatefulMysteryMissing);
|
||||
data.AddLine(result);
|
||||
}
|
||||
|
||||
@@ -585,21 +571,22 @@ private static void VerifyReceivability(LegalityAnalysis data, MysteryGift g)
|
||||
var pk = data.Entity;
|
||||
switch (g)
|
||||
{
|
||||
case PCD pcd when !pcd.CanBeReceivedByVersion(pk.Version) && pcd.Gift.PK.Version == 0:
|
||||
case WC6 wc6 when !wc6.CanBeReceivedByVersion(pk.Version) && !pk.WasTradedEgg:
|
||||
case WC7 wc7 when !wc7.CanBeReceivedByVersion(pk.Version) && !pk.WasTradedEgg:
|
||||
case WC8 wc8 when !wc8.CanBeReceivedByVersion(pk.Version):
|
||||
case WB8 wb8 when !wb8.CanBeReceivedByVersion(pk.Version, pk):
|
||||
case WA8 wa8 when !wa8.CanBeReceivedByVersion(pk.Version, pk):
|
||||
data.AddLine(GetInvalid(LEncGiftVersionNotDistributed, GameOrigin));
|
||||
data.AddLine(GetInvalid(GameOrigin, EncGiftVersionNotDistributed));
|
||||
return;
|
||||
case PGF pgf when pgf.RestrictLanguage != 0 && pk.Language != pgf.RestrictLanguage:
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, pgf.RestrictLanguage, pk.Language), CheckIdentifier.Language));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Language, EncGiftLanguageNotDistributed_0, (ushort)pgf.RestrictLanguage));
|
||||
return;
|
||||
case WC6 wc6 when wc6.RestrictLanguage != 0 && pk.Language != wc6.RestrictLanguage:
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, wc6.RestrictLanguage, pk.Language), CheckIdentifier.Language));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Language, EncGiftLanguageNotDistributed_0, (ushort)wc6.RestrictLanguage));
|
||||
return;
|
||||
case WC7 wc7 when wc7.RestrictLanguage != 0 && pk.Language != wc7.RestrictLanguage:
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, wc7.RestrictLanguage, pk.Language), CheckIdentifier.Language));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Language, EncGiftLanguageNotDistributed_0, (ushort)wc7.RestrictLanguage));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -608,15 +595,15 @@ private static void VerifyGift3Shiny(LegalityAnalysis data, EncounterGift3 g3)
|
||||
{
|
||||
// check for shiny locked gifts
|
||||
if (!g3.Shiny.IsValid(data.Entity))
|
||||
data.AddLine(GetInvalid(LEncGiftShinyMismatch, Fateful));
|
||||
data.AddLine(GetInvalid(Fateful, EncGiftShinyMismatch));
|
||||
}
|
||||
|
||||
private static void VerifyFatefulIngameActive(LegalityAnalysis data)
|
||||
{
|
||||
var pk = data.Entity;
|
||||
var result = pk.FatefulEncounter
|
||||
? GetValid(LFateful, Fateful)
|
||||
: GetInvalid(LFatefulMissing, Fateful);
|
||||
? GetValid(Fateful, Valid)
|
||||
: GetInvalid(Fateful, FatefulMissing);
|
||||
data.AddLine(result);
|
||||
}
|
||||
|
||||
@@ -635,7 +622,7 @@ public void VerifyVersionEvolution(LegalityAnalysis data)
|
||||
bool Sun() => ((uint)pk.Version & 1) == 0;
|
||||
bool Moon() => ((uint)pk.Version & 1) == 1;
|
||||
if (pk.IsUntraded)
|
||||
data.AddLine(GetInvalid(LEvoTradeRequired, Evolution));
|
||||
data.AddLine(GetInvalid(Evolution, EvoTradeRequired));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -645,21 +632,21 @@ private static void VerifyFullness(LegalityAnalysis data, PKM pk, IFullnessEnjoy
|
||||
if (pk.IsEgg)
|
||||
{
|
||||
if (fe.Fullness != 0)
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatFullness, "0"), Encounter));
|
||||
data.AddLine(GetInvalid(Encounter, MemoryStatFullness_0, 0));
|
||||
if (fe.Enjoyment != 0)
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatEnjoyment, "0"), Encounter));
|
||||
data.AddLine(GetInvalid(Encounter, MemoryStatEnjoyment_0, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pk.Format >= 8)
|
||||
{
|
||||
if (fe.Fullness > 245) // Exiting camp is -10, so a 255=>245 is max.
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatFullness, "<=245"), Encounter));
|
||||
data.AddLine(GetInvalid(Encounter, MemoryStatFullnessLEQ_0, 245));
|
||||
else if (fe.Fullness is not 0 && pk is not PK8) // BD/SP and PLA do not set this field, even via HOME.
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatFullness, "0"), Encounter));
|
||||
data.AddLine(GetInvalid(Encounter, MemoryStatFullness_0, 0));
|
||||
|
||||
if (fe.Enjoyment != 0)
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatEnjoyment, "0"), Encounter));
|
||||
data.AddLine(GetInvalid(Encounter, MemoryStatEnjoyment_0, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -673,7 +660,7 @@ private static void VerifyFullness(LegalityAnalysis data, PKM pk, IFullnessEnjoy
|
||||
return; // evolved
|
||||
|
||||
if (IsUnfeedable(pk.Species))
|
||||
data.AddLine(GetInvalid(string.Format(LMemoryStatFullness, "0"), Encounter));
|
||||
data.AddLine(GetInvalid(Encounter, MemoryStatFullness_0, 0));
|
||||
}
|
||||
|
||||
public static bool IsUnfeedable(ushort species) => species is
|
||||
@@ -688,16 +675,17 @@ private static void VerifyFullness(LegalityAnalysis data, PKM pk, IFullnessEnjoy
|
||||
private static void VerifyStats7b(LegalityAnalysis data, PB7 pb7)
|
||||
{
|
||||
VerifyAbsoluteSizes(data, pb7);
|
||||
var calc = pb7.CalcCP;
|
||||
if (pb7.Stat_CP != pb7.CalcCP && !IsStarterLGPE(pb7))
|
||||
data.AddLine(GetInvalid(LStatIncorrectCP, Encounter));
|
||||
data.AddLine(GetInvalid(Encounter, StatIncorrectCP_0, (uint)calc));
|
||||
|
||||
if (pb7.ReceivedTime is null)
|
||||
data.AddLine(GetInvalid(LDateTimeClockInvalid, Misc));
|
||||
data.AddLine(GetInvalid(Misc, DateTimeClockInvalid));
|
||||
|
||||
// HOME moving in and out will retain received date. ensure it matches if no HT data present.
|
||||
// Go Park captures will have different dates, as the GO met date is retained as Met Date.
|
||||
if (pb7.ReceivedDate is not { } date || !EncounterDate.IsValidDateSwitch(date) || (pb7.IsUntraded && data.EncounterOriginal is not EncounterSlot7GO && date != pb7.MetDate))
|
||||
data.AddLine(GetInvalid(LDateOutsideConsoleWindow, Misc));
|
||||
data.AddLine(GetInvalid(Misc, DateOutsideConsoleWindow));
|
||||
}
|
||||
|
||||
private static void VerifyAbsoluteSizes<T>(LegalityAnalysis data, T obj) where T : IScaledSizeValue
|
||||
@@ -715,10 +703,13 @@ private static void VerifyStats7b(LegalityAnalysis data, PB7 pb7)
|
||||
{
|
||||
// Unlike PLA, there is no way to force it to recalculate in-game.
|
||||
// The only encounter this applies to is Meltan, which cannot reach PLA for recalculation.
|
||||
if (obj.HeightAbsolute != enc.GetHomeHeightAbsolute())
|
||||
data.AddLine(GetInvalid(LStatIncorrectHeight, Encounter));
|
||||
if (obj.WeightAbsolute != enc.GetHomeWeightAbsolute())
|
||||
data.AddLine(GetInvalid(LStatIncorrectWeight, Encounter));
|
||||
var expectHeight = enc.GetHomeHeightAbsolute();
|
||||
if (obj.HeightAbsolute != expectHeight)
|
||||
data.AddLine(GetInvalid(Encounter, StatIncorrectHeight, BitConverter.SingleToUInt32Bits(expectHeight)));
|
||||
|
||||
var expectWeight = enc.GetHomeWeightAbsolute();
|
||||
if (obj.WeightAbsolute != expectWeight)
|
||||
data.AddLine(GetInvalid(Encounter, StatIncorrectWeight, BitConverter.SingleToUInt32Bits(expectWeight)));
|
||||
}
|
||||
|
||||
private static void VerifyFixedSizeMidAlpha(LegalityAnalysis data, PA8 pk)
|
||||
@@ -742,10 +733,13 @@ private static void VerifyFixedSizeMidAlpha(LegalityAnalysis data, PA8 pk)
|
||||
|
||||
private static void VerifyCalculatedSizes<T>(LegalityAnalysis data, T obj) where T : IScaledSizeValue
|
||||
{
|
||||
if (obj.HeightAbsolute != obj.CalcHeightAbsolute)
|
||||
data.AddLine(GetInvalid(LStatIncorrectHeight, Encounter));
|
||||
var expectHeight = obj.CalcHeightAbsolute;
|
||||
if (obj.HeightAbsolute != expectHeight)
|
||||
data.AddLine(GetInvalid(Encounter, StatIncorrectHeight, BitConverter.SingleToUInt32Bits(expectHeight)));
|
||||
|
||||
var expectWeight = obj.CalcWeightAbsolute;
|
||||
if (obj.WeightAbsolute != obj.CalcWeightAbsolute)
|
||||
data.AddLine(GetInvalid(LStatIncorrectWeight, Encounter));
|
||||
data.AddLine(GetInvalid(Encounter, StatIncorrectWeight, BitConverter.SingleToUInt32Bits(expectWeight)));
|
||||
}
|
||||
// ReSharper restore CompareOfFloatsByEqualityOperator
|
||||
|
||||
@@ -762,31 +756,31 @@ private void VerifyStats8(LegalityAnalysis data, PK8 pk8)
|
||||
if (pk8.IsEgg)
|
||||
{
|
||||
if (social != 0)
|
||||
data.AddLine(GetInvalid(LMemorySocialZero, Encounter));
|
||||
data.AddLine(GetInvalid(Encounter, MemorySocialZero));
|
||||
}
|
||||
else if (social > byte.MaxValue)
|
||||
{
|
||||
data.AddLine(GetInvalid(string.Format(LMemorySocialTooHigh_0, byte.MaxValue), Encounter));
|
||||
data.AddLine(GetInvalid(Encounter, MemoryStatSocialLEQ_0, 0));
|
||||
}
|
||||
|
||||
VerifyStatNature(data, pk8);
|
||||
|
||||
if (!pk8.IsBattleVersionValid(data.Info.EvoChainsAllGens))
|
||||
data.AddLine(GetInvalid(LStatBattleVersionInvalid));
|
||||
data.AddLine(GetInvalid(StatBattleVersionInvalid));
|
||||
|
||||
var enc = data.EncounterMatch;
|
||||
bool originGMax = enc is IGigantamaxReadOnly {CanGigantamax: true};
|
||||
if (originGMax != pk8.CanGigantamax)
|
||||
{
|
||||
bool ok = !pk8.IsEgg && Gigantamax.CanToggle(pk8.Species, pk8.Form, enc.Species, enc.Form);
|
||||
var chk = ok ? GetValid(LStatGigantamaxValid) : GetInvalid(LStatGigantamaxInvalid);
|
||||
var chk = ok ? GetValid(StatGigantamaxValid) : GetInvalid(StatGigantamaxInvalid);
|
||||
data.AddLine(chk);
|
||||
}
|
||||
|
||||
if (pk8.DynamaxLevel != 0)
|
||||
{
|
||||
if (!pk8.CanHaveDynamaxLevel(pk8) || pk8.DynamaxLevel > 10)
|
||||
data.AddLine(GetInvalid(LStatDynamaxInvalid));
|
||||
data.AddLine(GetInvalid(StatDynamaxInvalid));
|
||||
}
|
||||
|
||||
VerifyTechRecordSWSH(data, pk8);
|
||||
@@ -799,21 +793,21 @@ private void VerifyStats8a(LegalityAnalysis data, PA8 pa8)
|
||||
|
||||
var social = pa8.Sociability;
|
||||
if (social != 0)
|
||||
data.AddLine(GetInvalid(LMemorySocialZero, Encounter));
|
||||
data.AddLine(GetInvalid(Encounter, MemorySocialZero));
|
||||
|
||||
VerifyStatNature(data, pa8);
|
||||
|
||||
if (!pa8.IsBattleVersionValid(data.Info.EvoChainsAllGens))
|
||||
data.AddLine(GetInvalid(LStatBattleVersionInvalid));
|
||||
data.AddLine(GetInvalid(StatBattleVersionInvalid));
|
||||
|
||||
if (pa8.CanGigantamax)
|
||||
data.AddLine(GetInvalid(LStatGigantamaxInvalid));
|
||||
data.AddLine(GetInvalid(StatGigantamaxInvalid));
|
||||
|
||||
if (pa8.DynamaxLevel != 0)
|
||||
data.AddLine(GetInvalid(LStatDynamaxInvalid));
|
||||
data.AddLine(GetInvalid(StatDynamaxInvalid));
|
||||
|
||||
if (pa8.GetMoveRecordFlagAny() && !pa8.IsEgg) // already checked for eggs
|
||||
data.AddLine(GetInvalid(LEggRelearnFlags));
|
||||
data.AddLine(GetInvalid(EggRelearnFlags));
|
||||
|
||||
VerifyTechRecordSWSH(data, pa8);
|
||||
}
|
||||
@@ -822,28 +816,28 @@ private void VerifyStats8b(LegalityAnalysis data, PB8 pb8)
|
||||
{
|
||||
var social = pb8.Sociability;
|
||||
if (social != 0)
|
||||
data.AddLine(GetInvalid(LMemorySocialZero, Encounter));
|
||||
data.AddLine(GetInvalid(Encounter, MemorySocialZero));
|
||||
|
||||
if (pb8.IsDprIllegal)
|
||||
data.AddLine(GetInvalid(LTransferFlagIllegal));
|
||||
data.AddLine(GetInvalid(TransferFlagIllegal));
|
||||
if (pb8.Species is (int)Species.Spinda or (int)Species.Nincada && !pb8.BDSP)
|
||||
data.AddLine(GetInvalid(LTransferNotPossible));
|
||||
data.AddLine(GetInvalid(TransferNotPossible));
|
||||
if (pb8.Species is (int)Species.Spinda && pb8.Tracker != 0)
|
||||
data.AddLine(GetInvalid(LTransferTrackerShouldBeZero));
|
||||
data.AddLine(GetInvalid(TransferTrackerShouldBeZero));
|
||||
|
||||
VerifyStatNature(data, pb8);
|
||||
|
||||
if (!pb8.IsBattleVersionValid(data.Info.EvoChainsAllGens))
|
||||
data.AddLine(GetInvalid(LStatBattleVersionInvalid));
|
||||
data.AddLine(GetInvalid(StatBattleVersionInvalid));
|
||||
|
||||
if (pb8.CanGigantamax)
|
||||
data.AddLine(GetInvalid(LStatGigantamaxInvalid));
|
||||
data.AddLine(GetInvalid(StatGigantamaxInvalid));
|
||||
|
||||
if (pb8.DynamaxLevel != 0)
|
||||
data.AddLine(GetInvalid(LStatDynamaxInvalid));
|
||||
data.AddLine(GetInvalid(StatDynamaxInvalid));
|
||||
|
||||
if (pb8.GetMoveRecordFlagAny() && !pb8.IsEgg) // already checked for eggs
|
||||
data.AddLine(GetInvalid(LEggRelearnFlags));
|
||||
data.AddLine(GetInvalid(EggRelearnFlags));
|
||||
|
||||
VerifyTechRecordSWSH(data, pb8);
|
||||
}
|
||||
@@ -867,10 +861,10 @@ private static bool CheckHeightWeightOdds(IEncounterTemplate enc)
|
||||
if (statNature == pk.Nature)
|
||||
return;
|
||||
if (!statNature.IsMint())
|
||||
data.AddLine(GetInvalid(LStatNatureInvalid));
|
||||
data.AddLine(GetInvalid(StatNatureInvalid));
|
||||
}
|
||||
|
||||
private static string GetMoveName<T>(T pk, int index) where T : PKM, ITechRecord => ParseSettings.MoveStrings[pk.Permit.RecordPermitIndexes[index]];
|
||||
private static ushort GetMoveIndex<T>(T pk, int index) where T : PKM, ITechRecord => pk.Permit.RecordPermitIndexes[index];
|
||||
|
||||
private void VerifyTechRecordSWSH<T>(LegalityAnalysis data, T pk) where T : PKM, ITechRecord
|
||||
{
|
||||
@@ -882,7 +876,7 @@ private static bool CheckHeightWeightOdds(IEncounterTemplate enc)
|
||||
{
|
||||
if (!pk.GetMoveRecordFlag(i))
|
||||
continue;
|
||||
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(pk, i))));
|
||||
data.AddLine(GetInvalid(MoveTechRecordFlagMissing_0, GetMoveIndex(pk, i)));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -908,7 +902,7 @@ private static bool CheckHeightWeightOdds(IEncounterTemplate enc)
|
||||
continue;
|
||||
}
|
||||
|
||||
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(pk, i))));
|
||||
data.AddLine(GetInvalid(MoveTechRecordFlagMissing_0, GetMoveIndex(pk, i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -929,7 +923,7 @@ private void VerifyTechRecordSV(LegalityAnalysis data, PK9 pk)
|
||||
{
|
||||
if (!pk.GetMoveRecordFlag(i))
|
||||
continue;
|
||||
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(pk, i))));
|
||||
data.AddLine(GetInvalid(MoveTechRecordFlagMissing_0, GetMoveIndex(pk, i)));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -978,7 +972,7 @@ private void VerifyTechRecordSV(LegalityAnalysis data, PK9 pk)
|
||||
if (preEvoHas)
|
||||
continue;
|
||||
}
|
||||
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(pk, i))));
|
||||
data.AddLine(GetInvalid(MoveTechRecordFlagMissing_0, GetMoveIndex(pk, i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -22,9 +22,9 @@ private void VerifyEgg(LegalityAnalysis data)
|
||||
{
|
||||
var pk = data.Entity;
|
||||
if (pk.Move1_PPUps != 0 || pk.Move2_PPUps != 0 || pk.Move3_PPUps != 0 || pk.Move4_PPUps != 0)
|
||||
data.AddLine(GetInvalid(LEggPPUp, CheckIdentifier.Egg));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Egg, EggPPUp));
|
||||
if (!IsZeroMovePP(pk))
|
||||
data.AddLine(GetInvalid(LEggPP, CheckIdentifier.Egg));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Egg, EggPP));
|
||||
}
|
||||
|
||||
private static bool IsZeroMovePP(PKM pk)
|
||||
@@ -58,7 +58,7 @@ private void VerifyEntity(LegalityAnalysis data)
|
||||
for (int i = 0; i < ups.Length; i++)
|
||||
{
|
||||
if (ups[i] != 0)
|
||||
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, i + 1)));
|
||||
data.AddLine(GetInvalid(MovePPUpsTooHigh_0, (ushort)(i + 1)));
|
||||
}
|
||||
}
|
||||
else // Check specific move indexes
|
||||
@@ -66,7 +66,7 @@ private void VerifyEntity(LegalityAnalysis data)
|
||||
for (int i = 0; i < ups.Length; i++)
|
||||
{
|
||||
if (!Legal.IsPPUpAvailable(moves[i]) && ups[i] != 0)
|
||||
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, i + 1)));
|
||||
data.AddLine(GetInvalid(MovePPUpsTooHigh_0, (ushort)(i + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,9 +74,9 @@ private void VerifyEntity(LegalityAnalysis data)
|
||||
{
|
||||
var expect = pk.GetMovePP(moves[i], ups[i]);
|
||||
if (pp[i] > expect)
|
||||
data.AddLine(GetInvalid(string.Format(LMovePPTooHigh_0, i + 1)));
|
||||
data.AddLine(GetInvalid(MovePPTooHigh_0, (ushort)(i + 1)));
|
||||
else if (expectHeal && pp[i] != expect)
|
||||
data.AddLine(GetInvalid(string.Format(LMovePPExpectHealed_0, i + 1)));
|
||||
data.AddLine(GetInvalid(MovePPExpectHealed_0, (ushort)(i + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static PKHeX.Core.LanguageID;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -20,13 +20,13 @@ public override void Verify(LegalityAnalysis data)
|
||||
int len = pk.LoadString(pk.NicknameTrash, nickname);
|
||||
if (len == 0)
|
||||
{
|
||||
data.AddLine(GetInvalid(LNickLengthShort));
|
||||
data.AddLine(GetInvalid(NickLengthShort));
|
||||
return;
|
||||
}
|
||||
nickname = nickname[..len];
|
||||
if (nickname.Contains('\uffff') && pk is { Format: 4 })
|
||||
{
|
||||
data.AddLine(GetInvalid(LNickInvalidChar));
|
||||
data.AddLine(GetInvalid(NickInvalidChar, ushort.MaxValue));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
if (pk.VC)
|
||||
VerifyG1NicknameWithinBounds(data, nickname);
|
||||
else if (IsMysteryGiftNoNickname(enc))
|
||||
data.AddLine(Get(LEncGiftNicknamed, ParseSettings.Settings.Nickname.NicknamedMysteryGift(enc.Context)));
|
||||
data.AddLine(Get(ParseSettings.Settings.Nickname.NicknamedMysteryGift(enc.Context), EncGiftNicknamed));
|
||||
}
|
||||
|
||||
if (enc is IFixedTrainer t)
|
||||
@@ -68,10 +68,10 @@ public override void Verify(LegalityAnalysis data)
|
||||
if (ParseSettings.Settings.WordFilter.IsEnabled(pk.Format) && pk.IsNicknamed)
|
||||
{
|
||||
var mostRecentNicknameContext = pk.Format >= 8 ? pk.Context : enc.Context;
|
||||
if (WordFilter.IsFiltered(nickname, out var badPattern, pk.Context, mostRecentNicknameContext))
|
||||
data.AddLine(GetInvalid($"Word Filter: {badPattern}"));
|
||||
if (WordFilter.IsFiltered(nickname, pk.Context, mostRecentNicknameContext, out var type, out var badPattern))
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Nickname, WordFilterFlaggedPattern_01, (ushort)type, (ushort)badPattern));
|
||||
if (TrainerNameVerifier.ContainsTooManyNumbers(nickname, enc.Generation))
|
||||
data.AddLine(GetInvalid("Word Filter: Too many numbers."));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Nickname, WordFilterTooManyNumbers_0, (ushort)TrainerNameVerifier.GetMaxNumberCount(enc.Generation)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ private void VerifyFixedNicknameEncounter(LegalityAnalysis data, ILangNicknamedT
|
||||
return;
|
||||
|
||||
if (pk.IsNicknamed)
|
||||
data.AddLine(Get(LEncGiftNicknamed, Severity.Invalid));
|
||||
data.AddLine(Get(Severity.Invalid, EncGiftNicknamed));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -122,14 +122,14 @@ private void VerifyFixedNicknameEncounter(LegalityAnalysis data, ILangNicknamedT
|
||||
}
|
||||
|
||||
// Should have a nickname present.
|
||||
data.AddLine(GetInvalid(LNickMatchLanguageFail));
|
||||
data.AddLine(GetInvalid(NickMatchLanguageFail));
|
||||
return;
|
||||
}
|
||||
|
||||
// Encounter has a nickname, and PKM should have it.
|
||||
bool matches = nickname.SequenceEqual(encounterNickname);
|
||||
var severity = !matches || !pk.IsNicknamed ? Severity.Invalid : Severity.Valid;
|
||||
data.AddLine(Get(LEncGiftNicknamed, severity));
|
||||
data.AddLine(Get(severity, EncGiftNicknamed));
|
||||
}
|
||||
|
||||
private void VerifyHomeGiftNickname(LegalityAnalysis data, IEncounterTemplate enc, ILangNick pk, ReadOnlySpan<char> nickname)
|
||||
@@ -141,14 +141,14 @@ private void VerifyHomeGiftNickname(LegalityAnalysis data, IEncounterTemplate en
|
||||
// Can't nickname everything.
|
||||
if (enc.Species == (int) Species.Melmetal)
|
||||
{
|
||||
data.AddLine(GetInvalid(LEncGiftNicknamed));
|
||||
data.AddLine(GetInvalid(EncGiftNicknamed));
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the nickname does not match species name
|
||||
var orig = SpeciesName.GetSpeciesNameGeneration(enc.Species, pk.Language, enc.Generation);
|
||||
if (nickname.SequenceEqual(orig))
|
||||
data.AddLine(GetInvalid(LNickMatchLanguageFlag));
|
||||
data.AddLine(GetInvalid(NickMatchLanguageFlag));
|
||||
}
|
||||
|
||||
private bool VerifyUnNicknamedEncounter(LegalityAnalysis data, PKM pk, ReadOnlySpan<char> nickname, IEncounterTemplate enc)
|
||||
@@ -161,29 +161,29 @@ private bool VerifyUnNicknamedEncounter(LegalityAnalysis data, PKM pk, ReadOnlyS
|
||||
// Setting the nickname to the same as the species name does not set the Nickname flag (equals unmodified, no flag)
|
||||
if (!SpeciesName.IsNicknamed(pk.Species, nickname, pk.Language, pk.Format))
|
||||
{
|
||||
data.AddLine(Get(LNickMatchLanguageFlag, Severity.Invalid));
|
||||
data.AddLine(Get(Severity.Invalid, NickMatchLanguageFlag));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (SpeciesName.TryGetSpeciesAnyLanguage(nickname, out var species, pk.Format))
|
||||
{
|
||||
var msg = species == pk.Species ? LNickMatchLanguageFlag : LNickMatchNoOthersFail;
|
||||
data.AddLine(Get(msg, ParseSettings.Settings.Nickname.NicknamedAnotherSpecies));
|
||||
var msg = species == pk.Species ? NickMatchLanguageFlag : NickMatchNoOthersFail;
|
||||
data.AddLine(Get(ParseSettings.Settings.Nickname.NicknamedAnotherSpecies, msg));
|
||||
return true;
|
||||
}
|
||||
if (pk.Format <= 7 && StringConverter.HasEastAsianScriptCharacters(nickname) && pk is not PB7) // East Asian Scripts
|
||||
{
|
||||
data.AddLine(GetInvalid(LNickInvalidChar));
|
||||
data.AddLine(GetInvalid(NickInvalidChar));
|
||||
return true;
|
||||
}
|
||||
if (nickname.Length > Legal.GetMaxLengthNickname(enc.Generation, (LanguageID)pk.Language))
|
||||
{
|
||||
int length = GetForeignNicknameLength(pk, enc, enc.Generation);
|
||||
var severe = (length != 0 && nickname.Length <= length) ? Severity.Fishy : Severity.Invalid;
|
||||
data.AddLine(Get(LNickLengthLong, severe));
|
||||
data.AddLine(Get(severe, NickLengthLong));
|
||||
return true;
|
||||
}
|
||||
data.AddLine(GetValid(LNickMatchNoOthers));
|
||||
data.AddLine(GetValid(NickMatchNoOthers));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -198,13 +198,13 @@ private void VerifyUnNicknamed(LegalityAnalysis data, PKM pk, ReadOnlySpan<char>
|
||||
if (pk.Format < 3)
|
||||
{
|
||||
// pk1/pk2 IsNicknamed getter checks for match, logic should only reach here if matches.
|
||||
data.AddLine(GetValid(LNickMatchLanguage));
|
||||
data.AddLine(GetValid(NickMatchLanguage));
|
||||
}
|
||||
else
|
||||
{
|
||||
var enc = data.EncounterOriginal;
|
||||
bool valid = IsNicknameValid(pk, enc, nickname);
|
||||
var result = valid ? GetValid(LNickMatchLanguage) : GetInvalid(LNickMatchLanguageFail);
|
||||
var result = valid ? GetValid(NickMatchLanguage) : GetInvalid(NickMatchLanguageFail);
|
||||
data.AddLine(result);
|
||||
}
|
||||
}
|
||||
@@ -318,18 +318,18 @@ private static void VerifyNicknameEgg(LegalityAnalysis data)
|
||||
|
||||
bool flagState = EggStateLegality.IsNicknameFlagSet(enc, pk);
|
||||
if (pk.IsNicknamed != flagState)
|
||||
data.AddLine(GetInvalid(flagState ? LNickFlagEggYes : LNickFlagEggNo, CheckIdentifier.Egg));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Egg, flagState ? NickFlagEggYes : NickFlagEggNo));
|
||||
|
||||
Span<char> nickname = stackalloc char[pk.TrashCharCountNickname];
|
||||
int len = pk.LoadString(pk.NicknameTrash, nickname);
|
||||
nickname = nickname[..len];
|
||||
|
||||
if (pk.Format == 2 && !SpeciesName.IsNicknamedAnyLanguage(0, nickname, 2))
|
||||
data.AddLine(GetValid(LNickMatchLanguageEgg, CheckIdentifier.Egg));
|
||||
data.AddLine(GetValid(CheckIdentifier.Egg, NickMatchLanguageEgg));
|
||||
else if (!nickname.SequenceEqual(SpeciesName.GetEggName(pk.Language, enc.Generation)))
|
||||
data.AddLine(GetInvalid(LNickMatchLanguageEggFail, CheckIdentifier.Egg));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Egg, NickMatchLanguageEggFail));
|
||||
else
|
||||
data.AddLine(GetValid(LNickMatchLanguageEgg, CheckIdentifier.Egg));
|
||||
data.AddLine(GetValid(CheckIdentifier.Egg, NickMatchLanguageEgg));
|
||||
}
|
||||
|
||||
private static void VerifyNicknameTrade(LegalityAnalysis data, IEncounterTemplate t)
|
||||
@@ -351,21 +351,21 @@ private void VerifyG1NicknameWithinBounds(LegalityAnalysis data, ReadOnlySpan<ch
|
||||
if (StringConverter1.GetIsEnglish(str))
|
||||
{
|
||||
if (str.Length > 10)
|
||||
data.AddLine(GetInvalid(LNickLengthLong));
|
||||
data.AddLine(GetInvalid(NickLengthLong, 10));
|
||||
}
|
||||
else if (StringConverter1.GetIsJapanese(str))
|
||||
{
|
||||
if (str.Length > 5)
|
||||
data.AddLine(GetInvalid(LNickLengthLong));
|
||||
data.AddLine(GetInvalid(NickLengthLong, 5));
|
||||
}
|
||||
else if (pk.Korean && StringConverter2KOR.GetIsKorean(str))
|
||||
{
|
||||
if (str.Length > 5)
|
||||
data.AddLine(GetInvalid(LNickLengthLong));
|
||||
data.AddLine(GetInvalid(NickLengthLong, 5));
|
||||
}
|
||||
else
|
||||
{
|
||||
data.AddLine(GetInvalid(LG1CharNick));
|
||||
data.AddLine(GetInvalid(G1CharNick));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,7 +373,7 @@ private static void VerifyTrade4(LegalityAnalysis data, EncounterTrade4PID t)
|
||||
{
|
||||
var pk = data.Entity;
|
||||
if (t.IsIncorrectEnglish(pk))
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, Japanese, English), CheckIdentifier.Language));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Language, OTLanguageShouldBeLeq_0, (byte)Japanese));
|
||||
var lang = t.DetectOriginalLanguage(pk);
|
||||
VerifyTrade(data, t, lang);
|
||||
}
|
||||
@@ -401,7 +401,7 @@ private static void VerifyTrade8b(LegalityAnalysis data, EncounterTrade8b t)
|
||||
|
||||
lang = t.DetectMeisterMagikarpLanguage(nickname, trainer, lang);
|
||||
if (lang == -1) // err
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, $"{Japanese}/{German}", $"{(LanguageID)pk.Language}"), CheckIdentifier.Language));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Language, OTLanguageShouldBe_0or1, (byte)Japanese, (byte)German));
|
||||
}
|
||||
|
||||
if (t.IsPijako(pk))
|
||||
@@ -418,7 +418,7 @@ private static void VerifyEncounterTrade5(LegalityAnalysis data, EncounterTrade5
|
||||
var pk = data.Entity;
|
||||
var lang = pk.Language;
|
||||
if (pk.Format == 5 && lang == (int)Japanese)
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, 0, Japanese), CheckIdentifier.Language));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Language, OTLanguageShouldBe_0, (byte)0));
|
||||
|
||||
lang = Math.Max(lang, 1);
|
||||
VerifyTrade(data, t, lang);
|
||||
@@ -434,7 +434,7 @@ private static CheckResult CheckTradeOTOnly(LegalityAnalysis data, IFixedTrainer
|
||||
{
|
||||
var pk = data.Entity;
|
||||
if (pk.IsNicknamed && (pk.Format < 8 || pk.FatefulEncounter))
|
||||
return GetInvalid(LEncTradeChangedNickname, CheckIdentifier.Nickname);
|
||||
return GetInvalid(CheckIdentifier.Nickname, EncTradeChangedNickname);
|
||||
int lang = pk.Language;
|
||||
|
||||
Span<char> trainer = stackalloc char[pk.TrashCharCountTrainer];
|
||||
@@ -442,8 +442,8 @@ private static CheckResult CheckTradeOTOnly(LegalityAnalysis data, IFixedTrainer
|
||||
trainer = trainer[..len];
|
||||
|
||||
if (!t.IsTrainerMatch(pk, trainer, lang))
|
||||
return GetInvalid(LEncTradeIndexBad, CheckIdentifier.Trainer);
|
||||
return GetValid(LEncTradeUnchanged, CheckIdentifier.Nickname);
|
||||
return GetInvalid(CheckIdentifier.Trainer, EncTradeIndexBad);
|
||||
return GetValid(CheckIdentifier.Nickname, EncTradeUnchanged);
|
||||
}
|
||||
|
||||
private static void VerifyTrade(LegalityAnalysis data, IEncounterTemplate t, int language)
|
||||
@@ -458,8 +458,8 @@ private static void VerifyNickname(LegalityAnalysis data, IFixedNickname fn, int
|
||||
{
|
||||
var pk = data.Entity;
|
||||
var result = fn.IsNicknameMatch(pk, pk.Nickname, language)
|
||||
? GetValid(LEncTradeUnchanged, CheckIdentifier.Nickname)
|
||||
: Get(LEncTradeChangedNickname, ParseSettings.Settings.Nickname.NicknamedTrade(data.EncounterOriginal.Context), CheckIdentifier.Nickname);
|
||||
? GetValid(CheckIdentifier.Nickname, EncTradeUnchanged)
|
||||
: Get(CheckIdentifier.Nickname, ParseSettings.Settings.Nickname.NicknamedTrade(data.EncounterOriginal.Context), EncTradeChangedNickname);
|
||||
data.AddLine(result);
|
||||
}
|
||||
|
||||
@@ -471,6 +471,6 @@ private static void VerifyTrainerName(LegalityAnalysis data, IFixedTrainer ft, i
|
||||
trainer = trainer[..len];
|
||||
|
||||
if (!ft.IsTrainerMatch(pk, trainer, language))
|
||||
data.AddLine(GetInvalid(LEncTradeChangedOT, CheckIdentifier.Trainer));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Trainer, EncTradeChangedOT));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -23,9 +23,9 @@ public override void Verify(LegalityAnalysis data)
|
||||
VerifyEC100(data, enc.Species);
|
||||
|
||||
if (pk.PID == 0)
|
||||
data.AddLine(Get(LPIDZero, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, PIDZero));
|
||||
if (!pk.Nature.IsFixed()) // out of range
|
||||
data.AddLine(GetInvalid(LPIDNatureMismatch));
|
||||
data.AddLine(GetInvalid(PIDNatureMismatch));
|
||||
if (data.Info.EncounterMatch is IEncounterEgg egg)
|
||||
VerifyEggPID(data, pk, egg);
|
||||
|
||||
@@ -39,7 +39,7 @@ private static void VerifyEggPID(LegalityAnalysis data, PKM pk, IEncounterEgg eg
|
||||
// Gen5 eggs use rand(0xFFFFFFFF), which never yields 0xFFFFFFFF (max 0xFFFFFFFE).
|
||||
// Masuda Method does the same as the original PID roll. PID is never re-rolled a different way.
|
||||
if (pk.EncryptionConstant == uint.MaxValue)
|
||||
data.AddLine(Get(LPIDEncryptZero, Severity.Invalid, CheckIdentifier.EC));
|
||||
data.AddLine(Get(CheckIdentifier.EC, Severity.Invalid, PIDEncryptZero));
|
||||
}
|
||||
else if (egg is EncounterEgg4)
|
||||
{
|
||||
@@ -50,7 +50,7 @@ private static void VerifyEggPID(LegalityAnalysis data, PKM pk, IEncounterEgg eg
|
||||
// None of the un-rolled states share the same shiny-xor as PID=0, you can re-roll into an all-zero PID.
|
||||
// Flag it as fishy, because more often than not, it is hacked rather than a legitimately obtained egg.
|
||||
if (pk.EncryptionConstant == 0)
|
||||
data.AddLine(Get(LPIDEncryptZero, Severity.Fishy, CheckIdentifier.EC));
|
||||
data.AddLine(Get(CheckIdentifier.EC, Severity.Fishy, PIDEncryptZero));
|
||||
|
||||
if (Breeding.IsGenderSpeciesDetermination(egg.Species))
|
||||
VerifyEggGender8000(data, pk);
|
||||
@@ -58,7 +58,7 @@ private static void VerifyEggPID(LegalityAnalysis data, PKM pk, IEncounterEgg eg
|
||||
else if (egg is EncounterEgg3)
|
||||
{
|
||||
if (!Daycare3.IsValidProcPID(pk.EncryptionConstant, egg.Version))
|
||||
data.AddLine(Get(LPIDEncryptZero, Severity.Invalid, CheckIdentifier.EC));
|
||||
data.AddLine(Get(CheckIdentifier.EC, Severity.Invalid, PIDEncryptZero));
|
||||
|
||||
if (Breeding.IsGenderSpeciesDetermination(egg.Species))
|
||||
VerifyEggGender8000(data, pk);
|
||||
@@ -72,7 +72,7 @@ private static void VerifyEggGender8000(LegalityAnalysis data, PKM pk)
|
||||
if (Breeding.IsValidSpeciesBit34(pk.EncryptionConstant, gender))
|
||||
return; // 50/50 chance!
|
||||
if (gender == 1 || IsEggBitRequiredMale34(data.Info.Moves))
|
||||
data.AddLine(GetInvalid(LPIDGenderMismatch, CheckIdentifier.EC));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.EC, PIDGenderMismatch));
|
||||
}
|
||||
|
||||
private void VerifyShiny(LegalityAnalysis data)
|
||||
@@ -81,7 +81,7 @@ private void VerifyShiny(LegalityAnalysis data)
|
||||
var enc = data.EncounterMatch;
|
||||
|
||||
if (!enc.Shiny.IsValid(pk))
|
||||
data.AddLine(GetInvalid(LEncStaticPIDShiny, CheckIdentifier.Shiny));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Shiny, EncStaticPIDShiny));
|
||||
|
||||
switch (enc)
|
||||
{
|
||||
@@ -91,7 +91,7 @@ private void VerifyShiny(LegalityAnalysis data)
|
||||
VerifyG5PID_IDCorrelation(data);
|
||||
break;
|
||||
case EncounterSlot5 {IsHiddenGrotto: true} when pk.IsShiny:
|
||||
data.AddLine(GetInvalid(LG5PIDShinyGrotto, CheckIdentifier.Shiny));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Shiny, G5PIDShinyGrotto));
|
||||
break;
|
||||
case EncounterSlot5 {IsHiddenGrotto: false}:
|
||||
VerifyG5PID_IDCorrelation(data);
|
||||
@@ -99,16 +99,16 @@ private void VerifyShiny(LegalityAnalysis data)
|
||||
|
||||
case PCD d: // fixed PID
|
||||
if (d.IsFixedPID() && pk.EncryptionConstant != d.Gift.PK.PID)
|
||||
data.AddLine(GetInvalid(LEncGiftPIDMismatch, CheckIdentifier.Shiny));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Shiny, EncGiftPIDMismatch));
|
||||
break;
|
||||
|
||||
case WC7 { IsAshGreninja: true } when pk.IsShiny:
|
||||
data.AddLine(GetInvalid(LEncGiftShinyMismatch, CheckIdentifier.Shiny));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Shiny, EncGiftShinyMismatch));
|
||||
break;
|
||||
// Underground Raids are originally anti-shiny on encounter.
|
||||
// When selecting a prize at the end, the game rolls and force-shiny is applied to be XOR=1.
|
||||
case EncounterStatic8U u when !u.IsShinyXorValid(pk.ShinyXor):
|
||||
data.AddLine(GetInvalid(LEncStaticPIDShiny, CheckIdentifier.Shiny));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Shiny, EncStaticPIDShiny));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -118,7 +118,7 @@ private void VerifyG5PID_IDCorrelation(LegalityAnalysis data)
|
||||
var pk = data.Entity;
|
||||
var result = MonochromeRNG.GetBitXor(pk, pk.EncryptionConstant);
|
||||
if (result != 0)
|
||||
data.AddLine(GetInvalid(LPIDTypeMismatch));
|
||||
data.AddLine(GetInvalid(PIDTypeMismatch));
|
||||
}
|
||||
|
||||
private static void VerifyECPIDWurmple(LegalityAnalysis data)
|
||||
@@ -129,14 +129,12 @@ private static void VerifyECPIDWurmple(LegalityAnalysis data)
|
||||
{
|
||||
// Indicate what it will evolve into
|
||||
var evoVal = WurmpleUtil.GetWurmpleEvoVal(pk.EncryptionConstant);
|
||||
var evolvesTo = evoVal == WurmpleEvolution.Silcoon ? (int)Species.Beautifly : (int)Species.Dustox;
|
||||
var species = ParseSettings.SpeciesStrings[evolvesTo];
|
||||
var msg = string.Format(L_XWurmpleEvo_0, species);
|
||||
data.AddLine(GetValid(msg, CheckIdentifier.EC));
|
||||
var evolvesTo = evoVal == WurmpleEvolution.Silcoon ? (ushort)Species.Beautifly : (ushort)Species.Dustox;
|
||||
data.AddLine(GetValid(CheckIdentifier.EC, HintEvolvesToSpecies_0, evolvesTo));
|
||||
}
|
||||
else if (!WurmpleUtil.IsWurmpleEvoValid(pk))
|
||||
{
|
||||
data.AddLine(GetInvalid(LPIDEncryptWurmple, CheckIdentifier.EC));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.EC, PIDEncryptWurmple));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,14 +145,9 @@ private static void VerifyEC100(LegalityAnalysis data, ushort encSpecies)
|
||||
return; // Evolved, don't need to calculate the final evolution for the verbose report.
|
||||
|
||||
// Indicate the evolution for the user.
|
||||
const EntityContext mostRecent = Latest.Context; // latest ec100 form here
|
||||
uint evoVal = pk.EncryptionConstant % 100;
|
||||
bool rare = evoVal == 0;
|
||||
var (species, form) = EvolutionRestrictions.GetEvolvedSpeciesFormEC100(encSpecies, rare);
|
||||
var str = GameInfo.Strings;
|
||||
var forms = FormConverter.GetFormList(species, str.Types, str.forms, GameInfo.GenderSymbolASCII, mostRecent);
|
||||
var msg = string.Format(L_XRareFormEvo_0_1, forms[form], rare);
|
||||
data.AddLine(GetValid(msg, CheckIdentifier.EC));
|
||||
var rare = EvolutionRestrictions.IsEvolvedSpeciesFormRare(pk.EncryptionConstant);
|
||||
var hint = rare ? (byte)1 : (byte)0;
|
||||
data.AddLine(GetValid(CheckIdentifier.EC, HintEvolvesToRareForm_0, hint));
|
||||
}
|
||||
|
||||
private static void VerifyEC(LegalityAnalysis data)
|
||||
@@ -166,7 +159,7 @@ private static void VerifyEC(LegalityAnalysis data)
|
||||
{
|
||||
if (Info.EncounterMatch is WC8 {IsHOMEGift: true})
|
||||
return; // HOME Gifts
|
||||
data.AddLine(Get(LPIDEncryptZero, Severity.Fishy, CheckIdentifier.EC));
|
||||
data.AddLine(Get(CheckIdentifier.EC, Severity.Fishy, PIDEncryptZero));
|
||||
}
|
||||
|
||||
// Gen3-5 => Gen6 have PID==EC with an edge case exception.
|
||||
@@ -186,7 +179,7 @@ private static void VerifyEC(LegalityAnalysis data)
|
||||
if (enc is WB8 {IsEquivalentFixedECPID: true})
|
||||
return;
|
||||
|
||||
data.AddLine(GetInvalid(LPIDEqualsEC, CheckIdentifier.EC)); // better to flag than 1:2^32 odds since RNG is not feasible to yield match
|
||||
data.AddLine(GetInvalid(CheckIdentifier.EC, PIDEqualsEC)); // better to flag than 1:2^32 odds since RNG is not feasible to yield match
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -195,7 +188,7 @@ private static void VerifyEC(LegalityAnalysis data)
|
||||
{
|
||||
var xor = pk.ShinyXor;
|
||||
if (xor >> 3 == 1) // 8 <= x <= 15
|
||||
data.AddLine(Get(LTransferPIDECXor, Severity.Fishy, CheckIdentifier.EC));
|
||||
data.AddLine(Get(CheckIdentifier.EC, Severity.Fishy, TransferEncryptGen6Xor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,8 +231,8 @@ private static void VerifyTransferEC(LegalityAnalysis data)
|
||||
if (pk.PID == expect)
|
||||
return;
|
||||
|
||||
var msg = bitFlipProc ? LTransferPIDECBitFlip : LTransferPIDECEquals;
|
||||
data.AddLine(GetInvalid(msg, CheckIdentifier.EC));
|
||||
var msg = bitFlipProc ? TransferEncryptGen6BitFlip : TransferEncryptGen6Equals;
|
||||
data.AddLine(GetInvalid(CheckIdentifier.EC, msg));
|
||||
}
|
||||
|
||||
private static bool IsEggBitRequiredMale34(ReadOnlySpan<MoveResult> moves)
|
||||
|
||||
@@ -7,15 +7,15 @@ namespace PKHeX.Core;
|
||||
/// <summary>
|
||||
/// String Translation Utility
|
||||
/// </summary>
|
||||
public static class RibbonStrings
|
||||
public class RibbonStrings
|
||||
{
|
||||
private static readonly Dictionary<string, string> RibbonNames = [];
|
||||
private readonly Dictionary<string, string> RibbonNames = [];
|
||||
|
||||
/// <summary>
|
||||
/// Resets the Ribbon Dictionary to use the supplied set of Ribbon (Property) Names.
|
||||
/// </summary>
|
||||
/// <param name="lines">Array of strings that are tab separated with Property Name, \t, and Display Name.</param>
|
||||
public static void ResetDictionary(ReadOnlySpan<string> lines)
|
||||
public RibbonStrings(ReadOnlySpan<string> lines)
|
||||
{
|
||||
RibbonNames.EnsureCapacity(lines.Length);
|
||||
|
||||
@@ -38,11 +38,11 @@ public static void ResetDictionary(ReadOnlySpan<string> lines)
|
||||
/// <param name="propertyName">Ribbon property name</param>
|
||||
/// <param name="result">Ribbon localized name</param>
|
||||
/// <returns>True if exists</returns>
|
||||
public static bool GetNameSafe(string propertyName, [NotNullWhen(true)] out string? result) => RibbonNames.TryGetValue(propertyName, out result);
|
||||
public bool GetNameSafe(string propertyName, [NotNullWhen(true)] out string? result) => RibbonNames.TryGetValue(propertyName, out result);
|
||||
|
||||
/// <returns>Ribbon display name</returns>
|
||||
/// <inheritdoc cref="GetNameSafe"/>
|
||||
public static string GetName(string propertyName)
|
||||
public string GetName(string propertyName)
|
||||
{
|
||||
// Throw an exception with the requested property name as the message, rather than an ambiguous "key not present" message.
|
||||
// We should ALWAYS have the key present as the input arguments are not user-defined, rather, they are from PKM property names.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -23,6 +23,25 @@ public sealed class RibbonVerifier : Verifier
|
||||
public const int MaxRibbonCount = (int)RibbonIndex.MAX_COUNT;
|
||||
|
||||
public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
Span<RibbonResult> result = stackalloc RibbonResult[MaxRibbonCount];
|
||||
var count = Parse(result, data);
|
||||
if (count == 0)
|
||||
data.AddLine(GetValid(RibbonAllValid));
|
||||
else // defer hint string creation unless we request the string. We'll re-do work, but this saves hot path allocation where the string is never needed to be humanized.
|
||||
data.AddLine(GetInvalid(RibbonFInvalid_0, (ushort)count));
|
||||
}
|
||||
|
||||
public static string GetMessage(LegalityAnalysis data, RibbonStrings str, LegalityCheckLocalization localize)
|
||||
{
|
||||
// Calling this method assumes that one or more ribbons are invalid or missing.
|
||||
// The work was already done but forgotten, so we need to parse again.
|
||||
Span<RibbonResult> result = stackalloc RibbonResult[MaxRibbonCount];
|
||||
int count = Parse(result, data);
|
||||
return GetMessage(result[..count], str, localize);
|
||||
}
|
||||
|
||||
private static int Parse(Span<RibbonResult> result, LegalityAnalysis data)
|
||||
{
|
||||
// Flag VC (Gen1/2) ribbons using Gen7 origin rules.
|
||||
var enc = data.EncounterMatch;
|
||||
@@ -30,16 +49,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
|
||||
// Check Unobtainable Ribbons
|
||||
var args = new RibbonVerifierArguments(pk, enc, data.Info.EvoChainsAllGens);
|
||||
Span<RibbonResult> result = stackalloc RibbonResult[MaxRibbonCount];
|
||||
int count = GetRibbonResults(args, result);
|
||||
if (count == 0)
|
||||
{
|
||||
data.AddLine(GetValid(LRibbonAllValid));
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = GetMessage(result[..count]);
|
||||
data.AddLine(GetInvalid(msg));
|
||||
return GetRibbonResults(args, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -86,19 +96,19 @@ private static int GetRibbonResults(in RibbonVerifierArguments args, ref RibbonR
|
||||
return list.Count;
|
||||
}
|
||||
|
||||
private static string GetMessage(ReadOnlySpan<RibbonResult> result)
|
||||
private static string GetMessage(ReadOnlySpan<RibbonResult> result, RibbonStrings str , LegalityCheckLocalization localize)
|
||||
{
|
||||
var total = result.Length;
|
||||
int missing = GetCountMissing(result);
|
||||
int invalid = total - missing;
|
||||
var sb = new StringBuilder(total * 20);
|
||||
if (missing != 0)
|
||||
AppendAll(result, sb, LRibbonFMissing_0, true);
|
||||
AppendAll(result, sb, str, localize.RibbonMissing_0, true);
|
||||
if (invalid != 0)
|
||||
{
|
||||
if (missing != 0) // need to visually separate the message
|
||||
sb.Append(Environment.NewLine);
|
||||
AppendAll(result, sb, LRibbonFInvalid_0, false);
|
||||
AppendAll(result, sb, str, localize.RibbonFInvalid_0, false);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
@@ -116,7 +126,7 @@ private static int GetCountMissing(ReadOnlySpan<RibbonResult> result)
|
||||
|
||||
private const string MessageSplitNextRibbon = ", ";
|
||||
|
||||
private static void AppendAll(ReadOnlySpan<RibbonResult> result, StringBuilder sb, string startText, bool stateMissing)
|
||||
private static void AppendAll(ReadOnlySpan<RibbonResult> result, StringBuilder sb, RibbonStrings str, string startText, bool stateMissing)
|
||||
{
|
||||
int added = 0;
|
||||
sb.Append(startText);
|
||||
@@ -126,7 +136,7 @@ private static void AppendAll(ReadOnlySpan<RibbonResult> result, StringBuilder s
|
||||
continue;
|
||||
if (added++ != 0)
|
||||
sb.Append(MessageSplitNextRibbon);
|
||||
var localized = RibbonStrings.GetName(x.PropertyName);
|
||||
var localized = str.GetName(x.PropertyName);
|
||||
sb.Append(localized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static PKHeX.Core.StorageSlotType;
|
||||
using static PKHeX.Core.Species;
|
||||
|
||||
@@ -18,12 +18,12 @@ public override void Verify(LegalityAnalysis data)
|
||||
if (pk.IsEgg)
|
||||
{
|
||||
if (!IsSourceValidEgg(pk, source))
|
||||
data.AddLine(GetInvalid(LStoredSourceEgg));
|
||||
data.AddLine(GetInvalid(StoredSourceEgg));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsSourceValid(pk, source))
|
||||
data.AddLine(GetInvalid(string.Format(LStoredSourceInvalid_0, source)));
|
||||
data.AddLine(GetInvalid(StoredSlotSourceInvalid_0, (ushort)source));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -23,7 +23,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
var id32 = pk.ID32;
|
||||
if (id32 is 0 or int.MaxValue)
|
||||
{
|
||||
data.AddLine(GetInvalid(LOT_IDInvalid));
|
||||
data.AddLine(GetInvalid(OT_IDInvalid));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -45,29 +45,29 @@ public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
// Only TID is used for Gen 1/2 VC
|
||||
if (pk.SID16 != 0)
|
||||
data.AddLine(GetInvalid(LOT_SID0Invalid));
|
||||
data.AddLine(GetInvalid(OT_SID0Invalid));
|
||||
if (pk.TID16 == 0)
|
||||
data.AddLine(Get(LOT_TID0, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, OT_TID0));
|
||||
return;
|
||||
}
|
||||
else if (pk.Format <= 2)
|
||||
{
|
||||
// Only TID is used for Gen 1/2
|
||||
if (pk.TID16 == 0)
|
||||
data.AddLine(Get(LOT_TID0, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, OT_TID0));
|
||||
return;
|
||||
}
|
||||
|
||||
if (pk is { ID32: 0 })
|
||||
data.AddLine(Get(LOT_IDs0, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, OT_IDs0));
|
||||
else if (pk.TID16 == pk.SID16)
|
||||
data.AddLine(Get(LOT_IDEqual, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, OT_IDEqual));
|
||||
else if (pk.TID16 == 0)
|
||||
data.AddLine(Get(LOT_TID0, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, OT_TID0));
|
||||
else if (pk.SID16 == 0)
|
||||
data.AddLine(Get(LOT_SID0, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, OT_SID0));
|
||||
else if (IsOTIDSuspicious(pk.TID16, pk.SID16))
|
||||
data.AddLine(Get(LOTSuspicious, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, OTSuspicious));
|
||||
}
|
||||
|
||||
public static bool TryGetShinySID(uint pid, ushort tid, GameVersion version, out ushort sid)
|
||||
@@ -97,13 +97,13 @@ public static bool TryGetShinySID(uint pid, ushort tid, GameVersion version, out
|
||||
// japanese will stay Invalid
|
||||
}
|
||||
if (!MethodCXD.TryGetSeedTrainerID(tr.TID16, tr.SID16, out _))
|
||||
data.AddLine(Get(LTrainerIDNoSeed, severity, CheckIdentifier.Trainer));
|
||||
data.AddLine(Get(CheckIdentifier.Trainer, severity, TrainerIDNoSeed));
|
||||
}
|
||||
|
||||
private static void VerifyTrainerID_RS<T>(LegalityAnalysis data, T tr, Severity severity = Severity.Invalid) where T : ITrainerID32ReadOnly
|
||||
{
|
||||
if (!MethodH.TryGetSeedTrainerID(tr.TID16, tr.SID16, out _))
|
||||
data.AddLine(Get(LTrainerIDNoSeed, severity, CheckIdentifier.Trainer));
|
||||
data.AddLine(Get(CheckIdentifier.Trainer, severity, TrainerIDNoSeed));
|
||||
}
|
||||
|
||||
public static bool IsOTIDSuspicious(ushort tid16, ushort sid16) => (tid16, sid16) switch
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -27,19 +27,19 @@ public override void Verify(LegalityAnalysis data)
|
||||
int len = pk.LoadString(pk.OriginalTrainerTrash, trainer);
|
||||
if (len == 0)
|
||||
{
|
||||
data.AddLine(GetInvalid(LOTShort));
|
||||
data.AddLine(GetInvalid(OTShort));
|
||||
return;
|
||||
}
|
||||
trainer = trainer[..len];
|
||||
if (trainer.Contains('\uffff') && pk is { Format: 4 })
|
||||
{
|
||||
data.AddLine(GetInvalid("Trainer Name: Unknown Character"));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Trainer, WordFilterInvalidCharacter_0, 0xFFFF));
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsOTNameSuspicious(trainer))
|
||||
{
|
||||
data.AddLine(Get(LOTSuspicious, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, OTSuspicious));
|
||||
}
|
||||
|
||||
if (pk.VC)
|
||||
@@ -49,20 +49,20 @@ public override void Verify(LegalityAnalysis data)
|
||||
else if (trainer.Length > Legal.GetMaxLengthOT(enc.Generation, (LanguageID)pk.Language))
|
||||
{
|
||||
if (!IsEdgeCaseLength(pk, enc, trainer))
|
||||
data.AddLine(Get(LOTLong, Severity.Invalid));
|
||||
data.AddLine(Get(Severity.Invalid, OTLong));
|
||||
}
|
||||
|
||||
if (ParseSettings.Settings.WordFilter.IsEnabled(pk.Format))
|
||||
{
|
||||
if (WordFilter.IsFiltered(trainer, out var badPattern, pk.Context, enc.Context))
|
||||
data.AddLine(GetInvalid($"Word Filter: {badPattern}"));
|
||||
if (WordFilter.IsFiltered(trainer, pk.Context, enc.Context, out var type, out var badPattern))
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Trainer, WordFilterFlaggedPattern_01, (ushort)type, (ushort)badPattern));
|
||||
if (ContainsTooManyNumbers(trainer, enc.Generation))
|
||||
data.AddLine(GetInvalid("Word Filter: Too many numbers."));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Trainer, WordFilterTooManyNumbers_0, (ushort)GetMaxNumberCount(enc.Generation)));
|
||||
|
||||
Span<char> ht = stackalloc char[pk.TrashCharCountTrainer];
|
||||
int nameLen = pk.LoadString(pk.HandlingTrainerTrash, ht);
|
||||
if (WordFilter.IsFiltered(ht[..nameLen], out badPattern, pk.Context)) // HT context is always the current context
|
||||
data.AddLine(GetInvalid($"Word Filter: {badPattern}"));
|
||||
if (WordFilter.IsFiltered(ht[..nameLen], pk.Context, out type, out badPattern)) // HT context is always the current context
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Handler, WordFilterFlaggedPattern_01, (ushort)type, (ushort)badPattern));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public void VerifyOTGB(LegalityAnalysis data)
|
||||
// Transferring from RBY->Gen7 won't have OT Gender in PK1, nor will PK1 originated encounters.
|
||||
// GSC Trades already checked for OT Gender matching.
|
||||
if (pk is { Format: > 2, VC1: true } || enc is { Generation: 1 } or EncounterGift2 { IsEgg: false })
|
||||
data.AddLine(GetInvalid(LG1OTGender));
|
||||
data.AddLine(GetInvalid(G1OTGender));
|
||||
}
|
||||
|
||||
if (enc is IFixedTrainer { IsFixedTrainer: true })
|
||||
@@ -118,11 +118,11 @@ public void VerifyOTGB(LegalityAnalysis data)
|
||||
{
|
||||
if (pk is SK2 {TID16: 0, IsRental: true})
|
||||
{
|
||||
data.AddLine(Get(LOTShort, Severity.Fishy));
|
||||
data.AddLine(Get(Severity.Fishy, OTShort));
|
||||
}
|
||||
else
|
||||
{
|
||||
data.AddLine(GetInvalid(LOTShort));
|
||||
data.AddLine(GetInvalid(OTShort));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -145,23 +145,23 @@ private void VerifyGBOTWithinBounds(LegalityAnalysis data, ReadOnlySpan<char> st
|
||||
if (pk.Japanese)
|
||||
{
|
||||
if (str.Length > 5)
|
||||
data.AddLine(GetInvalid(LOTLong));
|
||||
data.AddLine(GetInvalid(OTLong, 5));
|
||||
if (!StringConverter1.GetIsJapanese(str))
|
||||
data.AddLine(GetInvalid(LG1CharOT));
|
||||
data.AddLine(GetInvalid(G1CharOT));
|
||||
}
|
||||
else if (pk.Korean)
|
||||
{
|
||||
if (str.Length > 5)
|
||||
data.AddLine(GetInvalid(LOTLong));
|
||||
data.AddLine(GetInvalid(OTLong, 5));
|
||||
if (!StringConverter2KOR.GetIsKorean(str))
|
||||
data.AddLine(GetInvalid(LG1CharOT));
|
||||
data.AddLine(GetInvalid(G1CharOT));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (str.Length > 7)
|
||||
data.AddLine(GetInvalid(LOTLong));
|
||||
data.AddLine(GetInvalid(OTLong, 7));
|
||||
if (!StringConverter1.GetIsEnglish(str))
|
||||
data.AddLine(GetInvalid(LG1CharOT));
|
||||
data.AddLine(GetInvalid(G1CharOT));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,13 +179,15 @@ public static bool ContainsTooManyNumbers(ReadOnlySpan<char> str, byte originalG
|
||||
{
|
||||
if (originalGeneration <= 3)
|
||||
return false; // no limit from these generations
|
||||
int max = originalGeneration < 6 ? 4 : 5;
|
||||
int max = GetMaxNumberCount(originalGeneration);
|
||||
if (str.Length <= max)
|
||||
return false;
|
||||
int count = GetNumberCount(str);
|
||||
return count > max;
|
||||
}
|
||||
|
||||
public static int GetMaxNumberCount(byte originalGeneration) => originalGeneration < 6 ? 4 : 5;
|
||||
|
||||
private static int GetNumberCount(ReadOnlySpan<char> str)
|
||||
{
|
||||
int ctr = 0;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -27,7 +27,7 @@ private void VerifyVCOTGender(LegalityAnalysis data)
|
||||
{
|
||||
var pk = data.Entity;
|
||||
if (pk.OriginalTrainerGender == 1 && pk.Version != GameVersion.C)
|
||||
data.AddLine(GetInvalid(LG2OTGender));
|
||||
data.AddLine(GetInvalid(G2OTGender));
|
||||
}
|
||||
|
||||
private void VerifyVCNatureEXP(LegalityAnalysis data)
|
||||
@@ -39,7 +39,7 @@ private void VerifyVCNatureEXP(LegalityAnalysis data)
|
||||
{
|
||||
var nature = Experience.GetNatureVC(pk.EXP);
|
||||
if (nature != pk.Nature)
|
||||
data.AddLine(GetInvalid(LTransferNature));
|
||||
data.AddLine(GetInvalid(TransferNature, (uint)nature));
|
||||
return;
|
||||
}
|
||||
if (met <= 2) // Not enough EXP to have every nature -- check for exclusions!
|
||||
@@ -49,7 +49,7 @@ private void VerifyVCNatureEXP(LegalityAnalysis data)
|
||||
var nature = pk.Nature;
|
||||
bool valid = VerifyVCNature(growth, nature);
|
||||
if (!valid)
|
||||
data.AddLine(GetInvalid(LTransferNature));
|
||||
data.AddLine(GetInvalid(TransferNature));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ private static void VerifyVCShinyXorIfShiny(LegalityAnalysis data)
|
||||
// (15:65536, ~1:4096) odds on a given shiny transfer!
|
||||
var xor = data.Entity.ShinyXor;
|
||||
if (xor is <= 15 and not 0)
|
||||
data.AddLine(Get(LEncStaticPIDShiny, ParseSettings.Settings.Game.Gen7.Gen7TransferStarPID, CheckIdentifier.PID));
|
||||
data.AddLine(Get(CheckIdentifier.PID, ParseSettings.Settings.Game.Gen7.Gen7TransferStarPID, EncStaticPIDShiny));
|
||||
}
|
||||
|
||||
private static void VerifyVCGeolocation(LegalityAnalysis data)
|
||||
@@ -79,7 +79,7 @@ private static void VerifyVCGeolocation(LegalityAnalysis data)
|
||||
// VC Games were region locked to the Console, meaning not all language games are available.
|
||||
var within = Locale3DS.IsRegionLockedLanguageValidVC(pk7.ConsoleRegion, pk7.Language);
|
||||
if (!within)
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, $"!={(LanguageID)pk7.Language}", ((LanguageID)pk7.Language).ToString()), CheckIdentifier.Language));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Language, OTLanguageCannotTransferToConsoleRegion_0, pk7.ConsoleRegion));
|
||||
}
|
||||
|
||||
public void VerifyTransferLegalityG3(LegalityAnalysis data)
|
||||
@@ -88,12 +88,12 @@ public void VerifyTransferLegalityG3(LegalityAnalysis data)
|
||||
if (pk.Format == 4) // Pal Park (3->4)
|
||||
{
|
||||
if (pk.MetLocation != Locations.Transfer3)
|
||||
data.AddLine(GetInvalid(LEggLocationPalPark));
|
||||
data.AddLine(GetInvalid(EggLocationPalPark, Locations.Transfer3));
|
||||
}
|
||||
else // Transporter (4->5)
|
||||
{
|
||||
if (pk.MetLocation != Locations.Transfer4)
|
||||
data.AddLine(GetInvalid(LTransferEggLocationTransporter));
|
||||
data.AddLine(GetInvalid(TransferEggLocationTransporter, Locations.Transfer4));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,14 +109,14 @@ public void VerifyTransferLegalityG4(LegalityAnalysis data)
|
||||
{
|
||||
case (int)Species.Celebi:
|
||||
if (loc is not (Locations.Transfer4_CelebiUnused or Locations.Transfer4_CelebiUsed))
|
||||
data.AddLine(GetInvalid(LTransferMet));
|
||||
data.AddLine(GetInvalid(TransferMet));
|
||||
break;
|
||||
case (int)Species.Raikou or (int)Species.Entei or (int)Species.Suicune:
|
||||
if (loc is not (Locations.Transfer4_CrownUnused or Locations.Transfer4_CrownUsed))
|
||||
data.AddLine(GetInvalid(LTransferMet));
|
||||
data.AddLine(GetInvalid(TransferMet));
|
||||
break;
|
||||
default:
|
||||
data.AddLine(GetInvalid(LTransferEggLocationTransporter));
|
||||
data.AddLine(GetInvalid(TransferEggLocationTransporter));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -137,7 +137,7 @@ public void VerifyTransferLegalityG8(LegalityAnalysis data)
|
||||
{
|
||||
if (s.IsTotemNoTransfer || pk.Form != s.GetTotemBaseForm())
|
||||
{
|
||||
data.AddLine(GetInvalid(LTransferBad));
|
||||
data.AddLine(GetInvalid(TransferBad));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -152,7 +152,7 @@ public void VerifyTransferLegalityG8(LegalityAnalysis data)
|
||||
_ => PersonalTable.SWSH,
|
||||
};
|
||||
if (!pt.IsPresentInGame(pk.Species, pk.Form))
|
||||
data.AddLine(GetInvalid(LTransferBad));
|
||||
data.AddLine(GetInvalid(TransferBad));
|
||||
}
|
||||
|
||||
private void VerifyHOMETransfer(LegalityAnalysis data, PKM pk)
|
||||
@@ -168,7 +168,7 @@ private void VerifyHOMETransfer(LegalityAnalysis data, PKM pk)
|
||||
if (pk.Context is not (EntityContext.Gen8 or EntityContext.Gen8a or EntityContext.Gen8b))
|
||||
{
|
||||
if (s is { HeightScalar: 0, WeightScalar: 0 } && !data.Info.EvoChainsAllGens.HasVisitedPLA)
|
||||
data.AddLine(GetInvalid(LTransferBad));
|
||||
data.AddLine(GetInvalid(TransferBad));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ private void VerifyHOMETracker(LegalityAnalysis data, PKM pk)
|
||||
// Can't validate the actual values (we aren't the server), so we can only check against zero.
|
||||
if (pk is IHomeTrack { HasTracker: false })
|
||||
{
|
||||
data.AddLine(Get(LTransferTrackerMissing, ParseSettings.Settings.HOMETransfer.HOMETransferTrackerNotPresent));
|
||||
data.AddLine(Get(ParseSettings.Settings.HOMETransfer.HOMETransferTrackerNotPresent, TransferTrackerMissing));
|
||||
// To the reader: It seems like the best course of action for setting a tracker is:
|
||||
// - Transfer a 0-Tracker pk to HOME to get assigned a valid Tracker via the game it originated from.
|
||||
// - Don't make one up.
|
||||
@@ -188,11 +188,11 @@ private void VerifyHOMETracker(LegalityAnalysis data, PKM pk)
|
||||
public void VerifyVCEncounter(PKM pk, IEncounterTemplate original, EncounterTransfer7 transfer, LegalityAnalysis data)
|
||||
{
|
||||
if (pk.MetLocation != transfer.Location)
|
||||
data.AddLine(GetInvalid(LTransferMetLocation));
|
||||
data.AddLine(GetInvalid(TransferMetLocation));
|
||||
|
||||
var expectEgg = pk is PB8 ? Locations.Default8bNone : transfer.EggLocation;
|
||||
if (pk.EggLocation != expectEgg)
|
||||
data.AddLine(GetInvalid(LEggLocationNone));
|
||||
data.AddLine(GetInvalid(EggLocationNone));
|
||||
|
||||
// Flag Moves that cannot be transferred
|
||||
if (original is EncounterStatic2 { DizzyPunchEgg: true}) // Dizzy Punch Gifts
|
||||
@@ -207,12 +207,12 @@ public void VerifyVCEncounter(PKM pk, IEncounterTemplate original, EncounterTran
|
||||
var enc = data.EncounterOriginal;
|
||||
var pi = PersonalTable.USUM[enc.Species];
|
||||
if (pi.Gender == 31 && pk.IsShiny) // impossible gender-shiny
|
||||
data.AddLine(GetInvalid(LEncStaticPIDShiny, CheckIdentifier.PID));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.PID, EncStaticPIDShiny));
|
||||
}
|
||||
else if (pk.Species == (int)Species.Unown)
|
||||
{
|
||||
if (pk.Form is not (8 or 21) && pk.IsShiny) // impossibly form-shiny (not I or V)
|
||||
data.AddLine(GetInvalid(LEncStaticPIDShiny, CheckIdentifier.PID));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.PID, EncStaticPIDShiny));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static PKHeX.Core.StringSource;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
@@ -11,16 +11,6 @@ public sealed class TrashByteVerifier : Verifier
|
||||
{
|
||||
protected override CheckIdentifier Identifier => CheckIdentifier.TrashBytes;
|
||||
|
||||
private static string Format(StringSource s) => s switch
|
||||
{
|
||||
Nickname => L_XNickname,
|
||||
OriginalTrainer => L_XOT,
|
||||
HandlingTrainer => L_XHT,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(s)),
|
||||
};
|
||||
|
||||
private static string Format(StringSource s, string msg) => string.Format(L_F0_1, Format(s), msg);
|
||||
|
||||
public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
var pk = data.Entity;
|
||||
@@ -38,19 +28,19 @@ public override void Verify(LegalityAnalysis data)
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyTrashBytesPalPark(LegalityAnalysis data, PKM pk)
|
||||
private static void VerifyTrashBytesPalPark(LegalityAnalysis data, PKM pk)
|
||||
{
|
||||
if (pk.Japanese)
|
||||
{
|
||||
// Trash bytes should be zero.
|
||||
if (!TrashBytesUTF16.IsTrashEmpty(pk.OriginalTrainerTrash))
|
||||
data.AddLine(GetInvalid(Format(Nickname, LTrashBytesShouldBeEmpty)));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Trainer, TrashBytesShouldBeEmpty));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Should have trash bytes from the transfer process.
|
||||
if (TrashBytesUTF16.IsTrashEmpty(pk.OriginalTrainerTrash))
|
||||
data.AddLine(GetInvalid(Format(Nickname, LTrashBytesExpected)));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Trainer, TrashBytesExpected));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,24 +49,24 @@ private void VerifyTrashBytesPCD(LegalityAnalysis data, PKM pk, PCD pcd)
|
||||
var enc = pcd.Gift.PK;
|
||||
var ot = enc.OriginalTrainerTrash;
|
||||
if (!ot.SequenceEqual(pk.OriginalTrainerTrash))
|
||||
data.AddLine(GetInvalid(Format(OriginalTrainer, LTrashBytesMismatchInitial)));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Trainer, TrashBytesMismatchInitial));
|
||||
|
||||
if (pcd.Species != pk.Species)
|
||||
return; // Evolved, trash bytes are rewritten.
|
||||
|
||||
var nick = enc.NicknameTrash;
|
||||
if (!nick.SequenceEqual(pk.NicknameTrash))
|
||||
data.AddLine(GetInvalid(Format(Nickname, LTrashBytesMismatchInitial)));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Nickname, TrashBytesMismatchInitial));
|
||||
}
|
||||
|
||||
private void VerifyTrashBytesHOME(LegalityAnalysis data, PKM pk)
|
||||
{
|
||||
if (!TrashBytesUTF16.IsFinalTerminatorPresent(pk.NicknameTrash))
|
||||
data.AddLine(GetInvalid(Format(Nickname, LTrashBytesMissingTerminator)));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Nickname, TrashBytesMissingTerminator));
|
||||
if (!TrashBytesUTF16.IsFinalTerminatorPresent(pk.OriginalTrainerTrash))
|
||||
data.AddLine(GetInvalid(Format(OriginalTrainer, LTrashBytesMissingTerminator)));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Trainer, TrashBytesMissingTerminator));
|
||||
if (!TrashBytesUTF16.IsFinalTerminatorPresent(pk.HandlingTrainerTrash))
|
||||
data.AddLine(GetInvalid(Format(HandlingTrainer, LTrashBytesMissingTerminator)));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Handler, TrashBytesMissingTerminator));
|
||||
|
||||
if (pk.IsEgg)
|
||||
{
|
||||
@@ -143,15 +133,14 @@ private void VerifyTrashSingle(LegalityAnalysis data, ReadOnlySpan<byte> span, S
|
||||
{
|
||||
var result = TrashBytesUTF16.IsTrashSingleOrNone(span);
|
||||
if (result.IsInvalid())
|
||||
data.AddLine(GetInvalid(Format(s, LTrashBytesShouldBeEmpty)));
|
||||
data.AddLine(GetInvalid(GetIdentifier(s), TrashBytesShouldBeEmpty));
|
||||
}
|
||||
|
||||
private void VerifyTrashSpecific(LegalityAnalysis data, ReadOnlySpan<byte> span, ReadOnlySpan<char> under, StringSource s,
|
||||
Severity severity = Severity.Invalid)
|
||||
private void VerifyTrashSpecific(LegalityAnalysis data, ReadOnlySpan<byte> span, ReadOnlySpan<char> under, StringSource s, Severity severity = Severity.Invalid)
|
||||
{
|
||||
var result = TrashBytesUTF16.IsTrashSpecific(span, under);
|
||||
if (result.IsInvalid())
|
||||
data.AddLine(Get(Format(s, string.Format(LTrashBytesExpected_0, under.ToString())), severity));
|
||||
data.AddLine(Get(GetIdentifier(s), severity, TrashBytesExpected));
|
||||
}
|
||||
|
||||
private void VerifyTrashNone(LegalityAnalysis data, ReadOnlySpan<byte> span, StringSource s,
|
||||
@@ -159,20 +148,28 @@ private void VerifyTrashSingle(LegalityAnalysis data, ReadOnlySpan<byte> span, S
|
||||
{
|
||||
var result = TrashBytesUTF16.IsTrashNone(span);
|
||||
if (result.IsInvalid())
|
||||
data.AddLine(Get(Format(s, LTrashBytesShouldBeEmpty), severity));
|
||||
data.AddLine(Get(GetIdentifier(s), severity, TrashBytesShouldBeEmpty));
|
||||
}
|
||||
|
||||
private void VerifyTrashNotEmpty(LegalityAnalysis data, ReadOnlySpan<byte> span, StringSource s)
|
||||
{
|
||||
if (!TrashBytesUTF16.IsTrashNotEmpty(span))
|
||||
data.AddLine(GetInvalid(Format(s, LTrashBytesExpected)));
|
||||
data.AddLine(GetInvalid(GetIdentifier(s), TrashBytesExpected));
|
||||
}
|
||||
|
||||
private void VerifyTrashEmpty(LegalityAnalysis data, ReadOnlySpan<byte> span, StringSource s)
|
||||
{
|
||||
if (!TrashBytesUTF16.IsTrashEmpty(span))
|
||||
data.AddLine(GetInvalid(Format(s, LTrashBytesShouldBeEmpty)));
|
||||
data.AddLine(GetInvalid(GetIdentifier(s), TrashBytesShouldBeEmpty));
|
||||
}
|
||||
|
||||
private static CheckIdentifier GetIdentifier(StringSource s) => s switch
|
||||
{
|
||||
Nickname => CheckIdentifier.Nickname,
|
||||
OriginalTrainer => CheckIdentifier.Trainer,
|
||||
HandlingTrainer => CheckIdentifier.Handler,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(s)),
|
||||
};
|
||||
}
|
||||
|
||||
public enum StringSource : byte { Nickname, OriginalTrainer, HandlingTrainer }
|
||||
|
||||
@@ -16,11 +16,19 @@ public abstract class Verifier
|
||||
/// <param name="data">Analysis data to process</param>
|
||||
public abstract void Verify(LegalityAnalysis data);
|
||||
|
||||
protected CheckResult GetInvalid(string msg) => Get(msg, Severity.Invalid);
|
||||
protected CheckResult GetValid(string msg) => Get(msg, Severity.Valid);
|
||||
protected CheckResult Get(string msg, Severity s) => new(s, Identifier, msg);
|
||||
// Standard methods for creating CheckResults
|
||||
protected CheckResult GetValid(LegalityCheckResultCode msg) => Get(Severity.Valid, msg);
|
||||
protected CheckResult GetInvalid(LegalityCheckResultCode msg) => Get(Severity.Invalid, msg);
|
||||
protected CheckResult Get(Severity s, LegalityCheckResultCode msg) => CheckResult.Get(s, Identifier, msg);
|
||||
|
||||
protected static CheckResult GetInvalid(string msg, CheckIdentifier c) => Get(msg, Severity.Invalid, c);
|
||||
protected static CheckResult GetValid(string msg, CheckIdentifier c) => Get(msg, Severity.Valid, c);
|
||||
protected static CheckResult Get(string msg, Severity s, CheckIdentifier c) => new(s, c, msg);
|
||||
protected CheckResult GetValid(LegalityCheckResultCode msg, uint argument) => Get(Severity.Valid, msg, argument);
|
||||
protected CheckResult GetInvalid(LegalityCheckResultCode msg, uint argument) => Get(Severity.Invalid, msg, argument);
|
||||
protected CheckResult GetInvalid(LegalityCheckResultCode msg, ushort arg0, ushort arg1) => GetInvalid(Identifier, msg, arg0, arg1);
|
||||
protected CheckResult Get(Severity s, LegalityCheckResultCode msg, uint argument) => CheckResult.Get(s, Identifier, msg, argument);
|
||||
|
||||
protected static CheckResult GetValid(CheckIdentifier c, LegalityCheckResultCode msg, uint argument = 0) => Get(c, Severity.Valid, msg, argument);
|
||||
protected static CheckResult Get(CheckIdentifier c, Severity s, LegalityCheckResultCode msg, uint argument = 0) => CheckResult.Get(s, c, msg, argument);
|
||||
|
||||
protected static CheckResult GetInvalid(CheckIdentifier c, LegalityCheckResultCode msg, uint value = 0) => CheckResult.Get(Severity.Invalid, c, msg, value);
|
||||
protected static CheckResult GetInvalid(CheckIdentifier c, LegalityCheckResultCode msg, ushort arg0, ushort arg1 = 0) => GetInvalid(c, msg, arg0 | (uint)arg1 << 16);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public PK4 PK
|
||||
{
|
||||
_pk = value;
|
||||
var data = value.Data;
|
||||
bool zero = Array.TrueForAll(data, static z => z == 0); // all zero
|
||||
bool zero = !data.ContainsAnyExcept<byte>(0); // all zero
|
||||
if (!zero)
|
||||
data = PokeCrypto.EncryptArray45(data);
|
||||
data.CopyTo(Data[8..]);
|
||||
|
||||
@@ -33,7 +33,7 @@ public static BK4 ReadUnshuffle(ReadOnlySpan<byte> data)
|
||||
return result;
|
||||
}
|
||||
|
||||
public BK4(byte[] data) : base(data)
|
||||
public BK4(Memory<byte> data) : base(data)
|
||||
{
|
||||
Sanity = 0x4000;
|
||||
ResetPartyStats();
|
||||
@@ -41,24 +41,24 @@ public BK4(byte[] data) : base(data)
|
||||
|
||||
public BK4() : this(new byte[PokeCrypto.SIZE_4STORED]) { }
|
||||
|
||||
public override BK4 Clone() => new((byte[])Data.Clone());
|
||||
public override BK4 Clone() => new(Data.ToArray());
|
||||
|
||||
// Structure
|
||||
public override uint PID { get => ReadUInt32BigEndian(Data.AsSpan(0x00)); set => WriteUInt32BigEndian(Data.AsSpan(0x00), value); }
|
||||
public override ushort Sanity { get => ReadUInt16BigEndian(Data.AsSpan(0x04)); set => WriteUInt16BigEndian(Data.AsSpan(0x04), value); }
|
||||
public override ushort Checksum { get => ReadUInt16BigEndian(Data.AsSpan(0x06)); set => WriteUInt16BigEndian(Data.AsSpan(0x06), value); }
|
||||
public override uint PID { get => ReadUInt32BigEndian(Data); set => WriteUInt32BigEndian(Data, value); }
|
||||
public override ushort Sanity { get => ReadUInt16BigEndian(Data[0x04..]); set => WriteUInt16BigEndian(Data[0x04..], value); }
|
||||
public override ushort Checksum { get => ReadUInt16BigEndian(Data[0x06..]); set => WriteUInt16BigEndian(Data[0x06..], value); }
|
||||
|
||||
#region Block A
|
||||
public override ushort Species { get => ReadUInt16BigEndian(Data.AsSpan(0x08)); set => WriteUInt16BigEndian(Data.AsSpan(0x08), value); }
|
||||
public override int HeldItem { get => ReadUInt16BigEndian(Data.AsSpan(0x0A)); set => WriteUInt16BigEndian(Data.AsSpan(0x0A), (ushort)value); }
|
||||
public override uint ID32 { get => ReadUInt32BigEndian(Data.AsSpan(0x0C)); set => WriteUInt32BigEndian(Data.AsSpan(0x0C), value); }
|
||||
public override ushort SID16 { get => ReadUInt16BigEndian(Data.AsSpan(0x0C)); set => WriteUInt16BigEndian(Data.AsSpan(0x0C), value); }
|
||||
public override ushort TID16 { get => ReadUInt16BigEndian(Data.AsSpan(0x0E)); set => WriteUInt16BigEndian(Data.AsSpan(0x0E), value); }
|
||||
public override ushort Species { get => ReadUInt16BigEndian(Data[0x08..]); set => WriteUInt16BigEndian(Data[0x08..], value); }
|
||||
public override int HeldItem { get => ReadUInt16BigEndian(Data[0x0A..]); set => WriteUInt16BigEndian(Data[0x0A..], (ushort)value); }
|
||||
public override uint ID32 { get => ReadUInt32BigEndian(Data[0x0C..]); set => WriteUInt32BigEndian(Data[0x0C..], value); }
|
||||
public override ushort SID16 { get => ReadUInt16BigEndian(Data[0x0C..]); set => WriteUInt16BigEndian(Data[0x0C..], value); }
|
||||
public override ushort TID16 { get => ReadUInt16BigEndian(Data[0x0E..]); set => WriteUInt16BigEndian(Data[0x0E..], value); }
|
||||
|
||||
public override uint EXP
|
||||
{
|
||||
get => ReadUInt32BigEndian(Data.AsSpan(0x10));
|
||||
set => WriteUInt32BigEndian(Data.AsSpan(0x10), value);
|
||||
get => ReadUInt32BigEndian(Data[0x10..]);
|
||||
set => WriteUInt32BigEndian(Data[0x10..], value);
|
||||
}
|
||||
|
||||
public override byte OriginalTrainerFriendship { get => Data[0x14]; set => Data[0x14] = value; }
|
||||
@@ -115,16 +115,16 @@ public override uint EXP
|
||||
public override bool RIB3_6 { get => (RIB3 & (1 << 6)) == 1 << 6; set => RIB3 = (byte)((RIB3 & ~(1 << 6)) | (value ? 1 << 6 : 0)); } // Unused
|
||||
public override bool RIB3_7 { get => (RIB3 & (1 << 7)) == 1 << 7; set => RIB3 = (byte)((RIB3 & ~(1 << 7)) | (value ? 1 << 7 : 0)); } // Unused
|
||||
|
||||
public override int RibbonCount => BitOperations.PopCount(ReadUInt32LittleEndian(Data.AsSpan(0x24)) & 0b00001111_11111111__11111111_11111111)
|
||||
+ BitOperations.PopCount(ReadUInt32LittleEndian(Data.AsSpan(0x3C)))
|
||||
+ BitOperations.PopCount(ReadUInt32LittleEndian(Data.AsSpan(0x60)) & 0b00000000_00001111__11111111_11111111);
|
||||
public override int RibbonCount => BitOperations.PopCount(ReadUInt32LittleEndian(Data[0x24..]) & 0b00001111_11111111__11111111_11111111)
|
||||
+ BitOperations.PopCount(ReadUInt32LittleEndian(Data[0x3C..]))
|
||||
+ BitOperations.PopCount(ReadUInt32LittleEndian(Data[0x60..]) & 0b00000000_00001111__11111111_11111111);
|
||||
#endregion
|
||||
|
||||
#region Block B
|
||||
public override ushort Move1 { get => ReadUInt16BigEndian(Data.AsSpan(0x28)); set => WriteUInt16BigEndian(Data.AsSpan(0x28), value); }
|
||||
public override ushort Move2 { get => ReadUInt16BigEndian(Data.AsSpan(0x2A)); set => WriteUInt16BigEndian(Data.AsSpan(0x2A), value); }
|
||||
public override ushort Move3 { get => ReadUInt16BigEndian(Data.AsSpan(0x2C)); set => WriteUInt16BigEndian(Data.AsSpan(0x2C), value); }
|
||||
public override ushort Move4 { get => ReadUInt16BigEndian(Data.AsSpan(0x2E)); set => WriteUInt16BigEndian(Data.AsSpan(0x2E), value); }
|
||||
public override ushort Move1 { get => ReadUInt16BigEndian(Data[0x28..]); set => WriteUInt16BigEndian(Data[0x28..], value); }
|
||||
public override ushort Move2 { get => ReadUInt16BigEndian(Data[0x2A..]); set => WriteUInt16BigEndian(Data[0x2A..], value); }
|
||||
public override ushort Move3 { get => ReadUInt16BigEndian(Data[0x2C..]); set => WriteUInt16BigEndian(Data[0x2C..], value); }
|
||||
public override ushort Move4 { get => ReadUInt16BigEndian(Data[0x2E..]); set => WriteUInt16BigEndian(Data[0x2E..], value); }
|
||||
public override int Move1_PP { get => Data[0x30]; set => Data[0x30] = (byte)value; }
|
||||
public override int Move2_PP { get => Data[0x31]; set => Data[0x31] = (byte)value; }
|
||||
public override int Move3_PP { get => Data[0x32]; set => Data[0x32] = (byte)value; }
|
||||
@@ -133,7 +133,7 @@ public override uint EXP
|
||||
public override int Move2_PPUps { get => Data[0x35]; set => Data[0x35] = (byte)value; }
|
||||
public override int Move3_PPUps { get => Data[0x36]; set => Data[0x36] = (byte)value; }
|
||||
public override int Move4_PPUps { get => Data[0x37]; set => Data[0x37] = (byte)value; }
|
||||
public override uint IV32 { get => ReadUInt32BigEndian(Data.AsSpan(0x38)); set => WriteUInt32BigEndian(Data.AsSpan(0x38), value); }
|
||||
public override uint IV32 { get => ReadUInt32BigEndian(Data[0x38..]); set => WriteUInt32BigEndian(Data[0x38..], value); }
|
||||
public override int IV_SPD { get => (int)(IV32 >> 02) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 02)) | ((value > 31 ? 31u : (uint)value) << 02); }
|
||||
public override int IV_SPA { get => (int)(IV32 >> 07) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 07)) | ((value > 31 ? 31u : (uint)value) << 07); }
|
||||
public override int IV_SPE { get => (int)(IV32 >> 12) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 12)) | ((value > 31 ? 31u : (uint)value) << 12); }
|
||||
@@ -188,14 +188,14 @@ public override uint EXP
|
||||
// 0x42-0x43 Unused
|
||||
public override ushort EggLocationExtended
|
||||
{
|
||||
get => ReadUInt16BigEndian(Data.AsSpan(0x44));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0x44), value);
|
||||
get => ReadUInt16BigEndian(Data[0x44..]);
|
||||
set => WriteUInt16BigEndian(Data[0x44..], value);
|
||||
}
|
||||
|
||||
public override ushort MetLocationExtended
|
||||
{
|
||||
get => ReadUInt16BigEndian(Data.AsSpan(0x46));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0x46), value);
|
||||
get => ReadUInt16BigEndian(Data[0x46..]);
|
||||
set => WriteUInt16BigEndian(Data[0x46..], value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -264,13 +264,13 @@ public override string Nickname
|
||||
|
||||
public override ushort EggLocationDP
|
||||
{
|
||||
get => ReadUInt16BigEndian(Data.AsSpan(0x7E));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0x7E), value);
|
||||
get => ReadUInt16BigEndian(Data[0x7E..]);
|
||||
set => WriteUInt16BigEndian(Data[0x7E..], value);
|
||||
}
|
||||
public override ushort MetLocationDP
|
||||
{
|
||||
get => ReadUInt16BigEndian(Data.AsSpan(0x80));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0x80), value);
|
||||
get => ReadUInt16BigEndian(Data[0x80..]);
|
||||
set => WriteUInt16BigEndian(Data[0x80..], value);
|
||||
}
|
||||
|
||||
public override byte PokerusState { get => Data[0x82]; set => Data[0x82] = value; }
|
||||
@@ -298,7 +298,7 @@ public override ushort MetLocationDP
|
||||
public override int Characteristic => EntityCharacteristic.GetCharacteristicInvertFields(PID, IV32);
|
||||
|
||||
// Methods
|
||||
protected override ushort CalculateChecksum() => Checksums.Add16BigEndian(Data.AsSpan()[8..PokeCrypto.SIZE_4STORED]);
|
||||
protected override ushort CalculateChecksum() => Checksums.Add16BigEndian(Data[8..PokeCrypto.SIZE_4STORED]);
|
||||
|
||||
protected override byte[] Encrypt()
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary> Generation 3 <see cref="PKM"/> format, exclusively for Pokémon Colosseum. </summary>
|
||||
public sealed class CK3(byte[] Data) : G3PKM(Data), IShadowCapture, ISeparateIVs, IGCRegion
|
||||
public sealed class CK3(Memory<byte> Raw) : G3PKM(Raw), IShadowCapture, ISeparateIVs, IGCRegion
|
||||
{
|
||||
public CK3() : this(new byte[PokeCrypto.SIZE_3CSTORED]) { }
|
||||
|
||||
@@ -22,31 +22,31 @@ public sealed class CK3(byte[] Data) : G3PKM(Data), IShadowCapture, ISeparateIVs
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_3CSTORED;
|
||||
public override EntityContext Context => EntityContext.Gen3;
|
||||
public override PersonalInfo3 PersonalInfo => PersonalTable.RS[Species];
|
||||
public override CK3 Clone() => new((byte[])Data.Clone());
|
||||
public override CK3 Clone() => new(Data.ToArray());
|
||||
|
||||
// Trash Bytes
|
||||
public override Span<byte> OriginalTrainerTrash => Data.AsSpan(0x18, 22);
|
||||
public Span<byte> NicknameDisplayTrash => Data.AsSpan(0x2E, 22);
|
||||
public override Span<byte> NicknameTrash => Data.AsSpan(0x44, 22);
|
||||
public override Span<byte> OriginalTrainerTrash => Data.Slice(0x18, 22);
|
||||
public Span<byte> NicknameDisplayTrash => Data.Slice(0x2E, 22);
|
||||
public override Span<byte> NicknameTrash => Data.Slice(0x44, 22);
|
||||
public override int TrashCharCountTrainer => 11;
|
||||
public override int TrashCharCountNickname => 11;
|
||||
|
||||
// Future Attributes
|
||||
public override ushort SpeciesInternal { get => ReadUInt16BigEndian(Data.AsSpan(0x00)); set => WriteUInt16BigEndian(Data.AsSpan(0x00), value); } // raw access
|
||||
public override ushort SpeciesInternal { get => ReadUInt16BigEndian(Data); set => WriteUInt16BigEndian(Data, value); } // raw access
|
||||
public override ushort Species { get => SpeciesConverter.GetNational3(SpeciesInternal); set => SpeciesInternal = SpeciesConverter.GetInternal3(value); }
|
||||
// 02-04 unused
|
||||
public override uint PID { get => ReadUInt32BigEndian(Data.AsSpan(0x04)); set => WriteUInt32BigEndian(Data.AsSpan(0x04), value); }
|
||||
public override uint PID { get => ReadUInt32BigEndian(Data[0x04..]); set => WriteUInt32BigEndian(Data[0x04..], value); }
|
||||
public override GameVersion Version { get => GetGBAVersionID((GCVersion)Data[0x08]); set => Data[0x08] = (byte)GetGCVersionID(value); }
|
||||
public GCRegion CurrentRegion { get => (GCRegion)Data[0x09]; set => Data[0x09] = (byte)value; }
|
||||
public GCRegion OriginalRegion { get => (GCRegion)Data[0x0A]; set => Data[0x0A] = (byte)value; }
|
||||
public override int Language { get => Core.Language.GetMainLangIDfromGC(Data[0x0B]); set => Data[0x0B] = Core.Language.GetGCLangIDfromMain((byte)value); }
|
||||
public override ushort MetLocation { get => ReadUInt16BigEndian(Data.AsSpan(0x0C)); set => WriteUInt16BigEndian(Data.AsSpan(0x0C), value); }
|
||||
public override ushort MetLocation { get => ReadUInt16BigEndian(Data[0x0C..]); set => WriteUInt16BigEndian(Data[0x0C..], value); }
|
||||
public override byte MetLevel { get => Data[0x0E]; set => Data[0x0E] = value; }
|
||||
public override byte Ball { get => Data[0x0F]; set => Data[0x0F] = value; }
|
||||
public override byte OriginalTrainerGender { get => Data[0x10]; set => Data[0x10] = value; }
|
||||
public override uint ID32 { get => ReadUInt32BigEndian(Data.AsSpan(0x14)); set => WriteUInt32BigEndian(Data.AsSpan(0x14), value); }
|
||||
public override ushort SID16 { get => ReadUInt16BigEndian(Data.AsSpan(0x14)); set => WriteUInt16BigEndian(Data.AsSpan(0x14), value); }
|
||||
public override ushort TID16 { get => ReadUInt16BigEndian(Data.AsSpan(0x16)); set => WriteUInt16BigEndian(Data.AsSpan(0x16), value); }
|
||||
public override uint ID32 { get => ReadUInt32BigEndian(Data[0x14..]); set => WriteUInt32BigEndian(Data[0x14..], value); }
|
||||
public override ushort SID16 { get => ReadUInt16BigEndian(Data[0x14..]); set => WriteUInt16BigEndian(Data[0x14..], value); }
|
||||
public override ushort TID16 { get => ReadUInt16BigEndian(Data[0x16..]); set => WriteUInt16BigEndian(Data[0x16..], value); }
|
||||
public override string OriginalTrainerName { get => GetString(OriginalTrainerTrash); set => SetString(OriginalTrainerTrash, value, 10, StringConverterOption.None); }
|
||||
public string NicknameDisplay { get => GetString(NicknameDisplayTrash); set => SetString(NicknameDisplayTrash, value, 10, StringConverterOption.None); }
|
||||
public override string Nickname { get => GetString(NicknameTrash); set { SetString(NicknameTrash, value, 10, StringConverterOption.None); ResetNicknameDisplay(); } }
|
||||
@@ -59,7 +59,7 @@ public void ResetNicknameDisplay()
|
||||
current[10..].Clear(); // clamp to 5 chars at most
|
||||
}
|
||||
|
||||
public override uint EXP { get => ReadUInt32BigEndian(Data.AsSpan(0x5C)); set => WriteUInt32BigEndian(Data.AsSpan(0x5C), value); }
|
||||
public override uint EXP { get => ReadUInt32BigEndian(Data[0x5C..]); set => WriteUInt32BigEndian(Data[0x5C..], value); }
|
||||
public override byte Stat_Level { get => Data[0x60]; set => Data[0x60] = value; }
|
||||
|
||||
// 0x64-0x77 are battle/status related
|
||||
@@ -67,55 +67,55 @@ public void ResetNicknameDisplay()
|
||||
// Not that the program cares
|
||||
|
||||
// Moves
|
||||
public override ushort Move1 { get => ReadUInt16BigEndian(Data.AsSpan(0x78)); set => WriteUInt16BigEndian(Data.AsSpan(0x78), value); }
|
||||
public override ushort Move1 { get => ReadUInt16BigEndian(Data[0x78..]); set => WriteUInt16BigEndian(Data[0x78..], value); }
|
||||
public override int Move1_PP { get => Data[0x7A]; set => Data[0x7A] = (byte)value; }
|
||||
public override int Move1_PPUps { get => Data[0x7B]; set => Data[0x7B] = (byte)value; }
|
||||
public override ushort Move2 { get => ReadUInt16BigEndian(Data.AsSpan(0x7C)); set => WriteUInt16BigEndian(Data.AsSpan(0x7C), value); }
|
||||
public override ushort Move2 { get => ReadUInt16BigEndian(Data[0x7C..]); set => WriteUInt16BigEndian(Data[0x7C..], value); }
|
||||
public override int Move2_PP { get => Data[0x7E]; set => Data[0x7E] = (byte)value; }
|
||||
public override int Move2_PPUps { get => Data[0x7F]; set => Data[0x7F] = (byte)value; }
|
||||
public override ushort Move3 { get => ReadUInt16BigEndian(Data.AsSpan(0x80)); set => WriteUInt16BigEndian(Data.AsSpan(0x80), value); }
|
||||
public override ushort Move3 { get => ReadUInt16BigEndian(Data[0x80..]); set => WriteUInt16BigEndian(Data[0x80..], value); }
|
||||
public override int Move3_PP { get => Data[0x82]; set => Data[0x82] = (byte)value; }
|
||||
public override int Move3_PPUps { get => Data[0x83]; set => Data[0x83] = (byte)value; }
|
||||
public override ushort Move4 { get => ReadUInt16BigEndian(Data.AsSpan(0x84)); set => WriteUInt16BigEndian(Data.AsSpan(0x84), value); }
|
||||
public override ushort Move4 { get => ReadUInt16BigEndian(Data[0x84..]); set => WriteUInt16BigEndian(Data[0x84..], value); }
|
||||
public override int Move4_PP { get => Data[0x86]; set => Data[0x86] = (byte)value; }
|
||||
public override int Move4_PPUps { get => Data[0x87]; set => Data[0x87] = (byte)value; }
|
||||
|
||||
public override int SpriteItem => ItemConverter.GetItemFuture3((ushort)HeldItem);
|
||||
public override int HeldItem { get => ReadUInt16BigEndian(Data.AsSpan(0x88)); set => WriteUInt16BigEndian(Data.AsSpan(0x88), (ushort)value); }
|
||||
public override int HeldItem { get => ReadUInt16BigEndian(Data[0x88..]); set => WriteUInt16BigEndian(Data[0x88..], (ushort)value); }
|
||||
|
||||
// More party stats
|
||||
public override int Stat_HPCurrent { get => ReadUInt16BigEndian(Data.AsSpan(0x8A)); set => WriteUInt16BigEndian(Data.AsSpan(0x8A), (ushort)value); }
|
||||
public override int Stat_HPMax { get => ReadUInt16BigEndian(Data.AsSpan(0x8C)); set => WriteUInt16BigEndian(Data.AsSpan(0x8C), (ushort)value); }
|
||||
public override int Stat_ATK { get => ReadUInt16BigEndian(Data.AsSpan(0x8E)); set => WriteUInt16BigEndian(Data.AsSpan(0x8E), (ushort)value); }
|
||||
public override int Stat_DEF { get => ReadUInt16BigEndian(Data.AsSpan(0x90)); set => WriteUInt16BigEndian(Data.AsSpan(0x90), (ushort)value); }
|
||||
public override int Stat_SPA { get => ReadUInt16BigEndian(Data.AsSpan(0x92)); set => WriteUInt16BigEndian(Data.AsSpan(0x92), (ushort)value); }
|
||||
public override int Stat_SPD { get => ReadUInt16BigEndian(Data.AsSpan(0x94)); set => WriteUInt16BigEndian(Data.AsSpan(0x94), (ushort)value); }
|
||||
public override int Stat_SPE { get => ReadUInt16BigEndian(Data.AsSpan(0x96)); set => WriteUInt16BigEndian(Data.AsSpan(0x96), (ushort)value); }
|
||||
public override int Stat_HPCurrent { get => ReadUInt16BigEndian(Data[0x8A..]); set => WriteUInt16BigEndian(Data[0x8A..], (ushort)value); }
|
||||
public override int Stat_HPMax { get => ReadUInt16BigEndian(Data[0x8C..]); set => WriteUInt16BigEndian(Data[0x8C..], (ushort)value); }
|
||||
public override int Stat_ATK { get => ReadUInt16BigEndian(Data[0x8E..]); set => WriteUInt16BigEndian(Data[0x8E..], (ushort)value); }
|
||||
public override int Stat_DEF { get => ReadUInt16BigEndian(Data[0x90..]); set => WriteUInt16BigEndian(Data[0x90..], (ushort)value); }
|
||||
public override int Stat_SPA { get => ReadUInt16BigEndian(Data[0x92..]); set => WriteUInt16BigEndian(Data[0x92..], (ushort)value); }
|
||||
public override int Stat_SPD { get => ReadUInt16BigEndian(Data[0x94..]); set => WriteUInt16BigEndian(Data[0x94..], (ushort)value); }
|
||||
public override int Stat_SPE { get => ReadUInt16BigEndian(Data[0x96..]); set => WriteUInt16BigEndian(Data[0x96..], (ushort)value); }
|
||||
|
||||
// EVs
|
||||
public override int EV_HP {
|
||||
get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0x98)));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0x98), (ushort)(value & 0xFF)); }
|
||||
get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data[0x98..]));
|
||||
set => WriteUInt16BigEndian(Data[0x98..], (ushort)(value & 0xFF)); }
|
||||
|
||||
public override int EV_ATK {
|
||||
get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0x9A)));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0x9A), (ushort)(value & 0xFF)); }
|
||||
get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data[0x9A..]));
|
||||
set => WriteUInt16BigEndian(Data[0x9A..], (ushort)(value & 0xFF)); }
|
||||
|
||||
public override int EV_DEF {
|
||||
get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0x9C)));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0x9C), (ushort)(value & 0xFF)); }
|
||||
get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data[0x9C..]));
|
||||
set => WriteUInt16BigEndian(Data[0x9C..], (ushort)(value & 0xFF)); }
|
||||
|
||||
public override int EV_SPA {
|
||||
get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0x9E)));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0x9E), (ushort)(value & 0xFF)); }
|
||||
get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data[0x9E..]));
|
||||
set => WriteUInt16BigEndian(Data[0x9E..], (ushort)(value & 0xFF)); }
|
||||
|
||||
public override int EV_SPD {
|
||||
get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0xA0)));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0xA0), (ushort)(value & 0xFF)); }
|
||||
get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data[0xA0..]));
|
||||
set => WriteUInt16BigEndian(Data[0xA0..], (ushort)(value & 0xFF)); }
|
||||
|
||||
public override int EV_SPE {
|
||||
get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data.AsSpan(0xA2)));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0xA2), (ushort)(value & 0xFF)); }
|
||||
get => Math.Min(byte.MaxValue, ReadUInt16BigEndian(Data[0xA2..]));
|
||||
set => WriteUInt16BigEndian(Data[0xA2..], (ushort)(value & 0xFF)); }
|
||||
|
||||
// IVs
|
||||
// Big Endian! Shift offset by 1 when interacting with a single byte.
|
||||
@@ -127,32 +127,32 @@ public void ResetNicknameDisplay()
|
||||
byte ISeparateIVs.IV_SPE { get => Data[0xAE + 1]; set => Data[0xAE + 1] = value; }
|
||||
|
||||
public override int IV_HP {
|
||||
get => Math.Min((ushort)31, ReadUInt16BigEndian(Data.AsSpan(0xA4)));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0xA4), (ushort)(value & 0x1F)); }
|
||||
get => Math.Min((ushort)31, ReadUInt16BigEndian(Data[0xA4..]));
|
||||
set => WriteUInt16BigEndian(Data[0xA4..], (ushort)(value & 0x1F)); }
|
||||
|
||||
public override int IV_ATK {
|
||||
get => Math.Min((ushort)31, ReadUInt16BigEndian(Data.AsSpan(0xA6)));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0xA6), (ushort)(value & 0x1F)); }
|
||||
get => Math.Min((ushort)31, ReadUInt16BigEndian(Data[0xA6..]));
|
||||
set => WriteUInt16BigEndian(Data[0xA6..], (ushort)(value & 0x1F)); }
|
||||
|
||||
public override int IV_DEF {
|
||||
get => Math.Min((ushort)31, ReadUInt16BigEndian(Data.AsSpan(0xA8)));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0xA8), (ushort)(value & 0x1F)); }
|
||||
get => Math.Min((ushort)31, ReadUInt16BigEndian(Data[0xA8..]));
|
||||
set => WriteUInt16BigEndian(Data[0xA8..], (ushort)(value & 0x1F)); }
|
||||
|
||||
public override int IV_SPA {
|
||||
get => Math.Min((ushort)31, ReadUInt16BigEndian(Data.AsSpan(0xAA)));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0xAA), (ushort)(value & 0x1F)); }
|
||||
get => Math.Min((ushort)31, ReadUInt16BigEndian(Data[0xAA..]));
|
||||
set => WriteUInt16BigEndian(Data[0xAA..], (ushort)(value & 0x1F)); }
|
||||
|
||||
public override int IV_SPD {
|
||||
get => Math.Min((ushort)31, ReadUInt16BigEndian(Data.AsSpan(0xAC)));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0xAC), (ushort)(value & 0x1F)); }
|
||||
get => Math.Min((ushort)31, ReadUInt16BigEndian(Data[0xAC..]));
|
||||
set => WriteUInt16BigEndian(Data[0xAC..], (ushort)(value & 0x1F)); }
|
||||
|
||||
public override int IV_SPE {
|
||||
get => Math.Min((ushort)31, ReadUInt16BigEndian(Data.AsSpan(0xAE)));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0xAE), (ushort)(value & 0x1F)); }
|
||||
get => Math.Min((ushort)31, ReadUInt16BigEndian(Data[0xAE..]));
|
||||
set => WriteUInt16BigEndian(Data[0xAE..], (ushort)(value & 0x1F)); }
|
||||
|
||||
public override byte OriginalTrainerFriendship {
|
||||
get => (byte)Math.Min((ushort)0xFF, ReadUInt16BigEndian(Data.AsSpan(0xB0)));
|
||||
set => WriteUInt16BigEndian(Data.AsSpan(0xB0), (ushort)(value & 0xFF));
|
||||
get => (byte)Math.Min((ushort)0xFF, ReadUInt16BigEndian(Data[0xB0..]));
|
||||
set => WriteUInt16BigEndian(Data[0xB0..], (ushort)(value & 0xFF));
|
||||
}
|
||||
|
||||
// Contest
|
||||
@@ -186,7 +186,7 @@ public void ResetNicknameDisplay()
|
||||
public override bool Unused3 { get => ((Data[0xC9] >> 2) & 1) == 1; set => Data[0xC9] = (byte)((Data[0xC9] & ~4) | (value ? 4 : 0)); }
|
||||
public override bool Unused4 { get => ((Data[0xC9] >> 3) & 1) == 1; set => Data[0xC9] = (byte)((Data[0xC9] & ~8) | (value ? 8 : 0)); }
|
||||
private bool FatefulEncounterJPN { get => ((Data[0xC9] >> 4) & 1) == 1; set => Data[0xC9] = (byte)((Data[0xC9] &~16) | (value ?16 : 0)); }
|
||||
public override int RibbonCount => Data.AsSpan(0xBD, 12).Count<byte>(1) + RibbonCountG3Cool + RibbonCountG3Beauty + RibbonCountG3Cute + RibbonCountG3Smart + RibbonCountG3Tough;
|
||||
public override int RibbonCount => Data.Slice(0xBD, 12).Count<byte>(1) + RibbonCountG3Cool + RibbonCountG3Beauty + RibbonCountG3Cute + RibbonCountG3Smart + RibbonCountG3Tough;
|
||||
|
||||
public override int PokerusStrain { get => Data[0xCA] & 0xF; set => Data[0xCA] = (byte)(value & 0xF); }
|
||||
public override bool IsEgg { get => Data[0xCB] == 1; set => Data[0xCB] = value ? (byte)1 : (byte)0; }
|
||||
@@ -197,9 +197,9 @@ public void ResetNicknameDisplay()
|
||||
public override int PokerusDays { get => Math.Max((sbyte)Data[0xD0], (sbyte)0); set => Data[0xD0] = (byte)(value == 0 ? 0xFF : value & 0xF); }
|
||||
|
||||
public int PartySlot { get => Data[0xD7]; set => Data[0xD7] = (byte)value; } // or not; only really used while in party?
|
||||
public ushort ShadowID { get => ReadUInt16BigEndian(Data.AsSpan(0xD8)); set => WriteUInt16BigEndian(Data.AsSpan(0xD8), value); }
|
||||
public int Purification { get => ReadInt32BigEndian(Data.AsSpan(0xDC)); set => WriteInt32BigEndian(Data.AsSpan(0xDC), value); }
|
||||
public uint EXP_Shadow { get => ReadUInt32BigEndian(Data.AsSpan(0xC0)); set => WriteUInt32BigEndian(Data.AsSpan(0xC0), value); }
|
||||
public ushort ShadowID { get => ReadUInt16BigEndian(Data[0xD8..]); set => WriteUInt16BigEndian(Data[0xD8..], value); }
|
||||
public int Purification { get => ReadInt32BigEndian(Data[0xDC..]); set => WriteInt32BigEndian(Data[0xDC..], value); }
|
||||
public uint EXP_Shadow { get => ReadUInt32BigEndian(Data[0xC0..]); set => WriteUInt32BigEndian(Data[0xC0..], value); }
|
||||
|
||||
private bool FatefulEncounterINT { get => ((Data[0xFB] >> 0) & 1) == 1; set => Data[0xFB] = (byte)((Data[0xFB] & ~1) | (value ? 1 : 0)); }
|
||||
|
||||
@@ -238,7 +238,7 @@ public bool IsFatefulValid(bool japanese)
|
||||
public const int Purified = -100;
|
||||
public bool IsShadow => ShadowID != 0 && Purification != Purified;
|
||||
|
||||
protected override byte[] Encrypt() => (byte[])Data.Clone();
|
||||
protected override byte[] Encrypt() => Data.ToArray();
|
||||
|
||||
public PK3 ConvertToPK3()
|
||||
{
|
||||
|
||||
@@ -102,9 +102,9 @@ private static void Crypt(ReadOnlySpan<byte> data, Span<byte> result, byte[] key
|
||||
/// Decrypts the input <see cref="data"/> data into a new array if it is encrypted, and updates the reference.
|
||||
/// </summary>
|
||||
/// <remarks>Format encryption check</remarks>
|
||||
public static void DecryptIfEncrypted(ref byte[] data)
|
||||
public static void DecryptIfEncrypted(ref Memory<byte> data)
|
||||
{
|
||||
var span = data.AsSpan();
|
||||
var span = data.Span;
|
||||
var format = ReadUInt16LittleEndian(span);
|
||||
if (IsKnownVersion(format))
|
||||
{
|
||||
|
||||
@@ -17,9 +17,9 @@ public sealed class PKH : PKM, IHandlerLanguage, IFormArgument, IHomeTrack, IBat
|
||||
|
||||
public override EntityContext Context => EntityContext.None;
|
||||
|
||||
public PKH(byte[] data) : base(DecryptHome(data))
|
||||
public PKH(Memory<byte> data) : base(DecryptHome(data))
|
||||
{
|
||||
var mem = Data.AsMemory(HomeCrypto.SIZE_1HEADER + 2);
|
||||
var mem = Raw[(HomeCrypto.SIZE_1HEADER + 2)..];
|
||||
var core = mem[..CoreDataSize];
|
||||
var side = mem.Slice(core.Length + 2, GameDataSize);
|
||||
|
||||
@@ -31,7 +31,7 @@ public PKH() : base(HomeCrypto.SIZE_STORED)
|
||||
{
|
||||
CoreDataSize = HomeCrypto.SIZE_CORE;
|
||||
|
||||
var mem = Data.AsMemory(HomeCrypto.SIZE_1HEADER + 2);
|
||||
var mem = Raw[(HomeCrypto.SIZE_1HEADER + 2)..];
|
||||
var core = mem[..CoreDataSize];
|
||||
Core = new GameDataCore(core) { AffixedRibbon = PKHeX.Core.AffixedRibbon.None };
|
||||
}
|
||||
@@ -62,19 +62,19 @@ private void ReadGameData1(Memory<byte> data)
|
||||
_ => throw new ArgumentException($"Unknown {nameof(HomeGameDataFormat)} {format}"),
|
||||
};
|
||||
|
||||
private static byte[] DecryptHome(byte[] data)
|
||||
private static Memory<byte> DecryptHome(Memory<byte> data)
|
||||
{
|
||||
HomeCrypto.DecryptIfEncrypted(ref data);
|
||||
//Array.Resize(ref data, HomeCrypto.SIZE_1STORED);
|
||||
return data;
|
||||
}
|
||||
|
||||
public ushort DataVersion { get => ReadUInt16LittleEndian(Data.AsSpan(0x00)); set => WriteUInt16LittleEndian(Data.AsSpan(0x00), value); }
|
||||
public ulong EncryptionSeed { get => ReadUInt64LittleEndian(Data.AsSpan(0x02)); set => WriteUInt64LittleEndian(Data.AsSpan(0x02), value); }
|
||||
public uint Checksum { get => ReadUInt32LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt32LittleEndian(Data.AsSpan(0x0A), value); }
|
||||
public ushort EncodedDataSize { get => ReadUInt16LittleEndian(Data.AsSpan(0x0E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0E), value); }
|
||||
public ushort CoreDataSize { get => ReadUInt16LittleEndian(Data.AsSpan(0x10)); set => WriteUInt16LittleEndian(Data.AsSpan(0x10), value); }
|
||||
public ushort GameDataSize { get => ReadUInt16LittleEndian(Data.AsSpan(0x12 + CoreDataSize)); set => WriteUInt16LittleEndian(Data.AsSpan(0x12 + CoreDataSize), value); }
|
||||
public ushort DataVersion { get => ReadUInt16LittleEndian(Data); set => WriteUInt16LittleEndian(Data, value); }
|
||||
public ulong EncryptionSeed { get => ReadUInt64LittleEndian(Data[0x02..]); set => WriteUInt64LittleEndian(Data[0x02..], value); }
|
||||
public uint Checksum { get => ReadUInt32LittleEndian(Data[0x0A..]); set => WriteUInt32LittleEndian(Data[0x0A..], value); }
|
||||
public ushort EncodedDataSize { get => ReadUInt16LittleEndian(Data[0x0E..]); set => WriteUInt16LittleEndian(Data[0x0E..], value); }
|
||||
public ushort CoreDataSize { get => ReadUInt16LittleEndian(Data[0x10..]); set => WriteUInt16LittleEndian(Data[0x10..], value); }
|
||||
public ushort GameDataSize { get => ReadUInt16LittleEndian(Data[(0x12 + CoreDataSize)..]); set => WriteUInt16LittleEndian(Data[(0x12 + CoreDataSize)..], value); }
|
||||
|
||||
private const int GameDataStart = HomeCrypto.SIZE_1HEADER + 2 + HomeCrypto.SIZE_CORE + 2;
|
||||
|
||||
@@ -288,7 +288,7 @@ public byte[] Rebuild()
|
||||
DataVersion = HomeCrypto.VersionLatest;
|
||||
EncodedDataSize = (ushort)(result.Length - HomeCrypto.SIZE_1HEADER);
|
||||
CoreDataSize = (ushort)Core.SerializedSize;
|
||||
Data.AsSpan(0, HomeCrypto.SIZE_1HEADER + 2).CopyTo(span); // Copy updated header & CoreData length.
|
||||
Data[..(HomeCrypto.SIZE_1HEADER + 2)].CopyTo(span); // Copy updated header & CoreData length.
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -307,7 +307,7 @@ private int WriteLength
|
||||
}
|
||||
}
|
||||
|
||||
public override PKH Clone() => new((byte[])Data.Clone())
|
||||
public override PKH Clone() => new(Data.ToArray())
|
||||
{
|
||||
DataPK9 = DataPK9?.Clone(),
|
||||
DataPK8 = DataPK8?.Clone(),
|
||||
|
||||
@@ -34,21 +34,25 @@ public sealed class PA8 : PKM, ISanityChecksum,
|
||||
|
||||
public override EntityContext Context => EntityContext.Gen8a;
|
||||
public PA8() : base(PokeCrypto.SIZE_8APARTY) => AffixedRibbon = Core.AffixedRibbon.None;
|
||||
public PA8(byte[] data) : base(DecryptParty(data)) { }
|
||||
public PA8(Memory<byte> data) : base(DecryptParty(data)) { }
|
||||
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_8APARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_8ASTORED;
|
||||
public override bool ChecksumValid => CalculateChecksum() == Checksum;
|
||||
public override PA8 Clone() => new((byte[])Data.Clone());
|
||||
public override PA8 Clone() => new(Data.ToArray());
|
||||
|
||||
private static byte[] DecryptParty(byte[] data)
|
||||
private static Memory<byte> DecryptParty(Memory<byte> data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted8A(ref data);
|
||||
Array.Resize(ref data, PokeCrypto.SIZE_8APARTY);
|
||||
return data;
|
||||
if (data.Length >= PokeCrypto.SIZE_8APARTY)
|
||||
return data;
|
||||
|
||||
var result = new byte[PokeCrypto.SIZE_8APARTY];
|
||||
data.Span.CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private ushort CalculateChecksum() => Checksums.Add16(Data.AsSpan()[8..PokeCrypto.SIZE_8ASTORED]);
|
||||
private ushort CalculateChecksum() => Checksums.Add16(Data[8..PokeCrypto.SIZE_8ASTORED]);
|
||||
|
||||
// Simple Generated Attributes
|
||||
|
||||
@@ -62,9 +66,9 @@ public override byte CurrentFriendship
|
||||
public override bool Valid { get => Sanity == 0 && ChecksumValid; set { if (!value) return; Sanity = 0; RefreshChecksum(); } }
|
||||
|
||||
// Trash Bytes
|
||||
public override Span<byte> NicknameTrash => Data.AsSpan(0x60, 26);
|
||||
public override Span<byte> HandlingTrainerTrash => Data.AsSpan(0xB8, 26);
|
||||
public override Span<byte> OriginalTrainerTrash => Data.AsSpan(0x110, 26);
|
||||
public override Span<byte> NicknameTrash => Data.Slice(0x60, 26);
|
||||
public override Span<byte> HandlingTrainerTrash => Data.Slice(0xB8, 26);
|
||||
public override Span<byte> OriginalTrainerTrash => Data.Slice(0x110, 26);
|
||||
public override int TrashCharCountTrainer => 13;
|
||||
public override int TrashCharCountNickname => 13;
|
||||
|
||||
@@ -113,29 +117,29 @@ public void FixRelearn()
|
||||
}
|
||||
}
|
||||
|
||||
public override uint EncryptionConstant { get => ReadUInt32LittleEndian(Data.AsSpan(0x00)); set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value); }
|
||||
public ushort Sanity { get => ReadUInt16LittleEndian(Data.AsSpan(0x04)); set => WriteUInt16LittleEndian(Data.AsSpan(0x04), value); }
|
||||
public ushort Checksum { get => ReadUInt16LittleEndian(Data.AsSpan(0x06)); set => WriteUInt16LittleEndian(Data.AsSpan(0x06), value); }
|
||||
public override uint EncryptionConstant { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); }
|
||||
public ushort Sanity { get => ReadUInt16LittleEndian(Data[0x04..]); set => WriteUInt16LittleEndian(Data[0x04..], value); }
|
||||
public ushort Checksum { get => ReadUInt16LittleEndian(Data[0x06..]); set => WriteUInt16LittleEndian(Data[0x06..], value); }
|
||||
|
||||
// Structure
|
||||
#region Block A
|
||||
public override ushort Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x08)); set => WriteUInt16LittleEndian(Data.AsSpan(0x08), value); }
|
||||
public override int HeldItem { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
|
||||
public override uint ID32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x0C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x0C), value); }
|
||||
public override ushort TID16 { get => ReadUInt16LittleEndian(Data.AsSpan(0x0C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0C), value); }
|
||||
public override ushort SID16 { get => ReadUInt16LittleEndian(Data.AsSpan(0x0E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0E), value); }
|
||||
public override uint EXP { get => ReadUInt32LittleEndian(Data.AsSpan(0x10)); set => WriteUInt32LittleEndian(Data.AsSpan(0x10), value); }
|
||||
public override int Ability { get => ReadUInt16LittleEndian(Data.AsSpan(0x14)); set => WriteUInt16LittleEndian(Data.AsSpan(0x14), (ushort)value); }
|
||||
public override ushort Species { get => ReadUInt16LittleEndian(Data[0x08..]); set => WriteUInt16LittleEndian(Data[0x08..], value); }
|
||||
public override int HeldItem { get => ReadUInt16LittleEndian(Data[0x0A..]); set => WriteUInt16LittleEndian(Data[0x0A..], (ushort)value); }
|
||||
public override uint ID32 { get => ReadUInt32LittleEndian(Data[0x0C..]); set => WriteUInt32LittleEndian(Data[0x0C..], value); }
|
||||
public override ushort TID16 { get => ReadUInt16LittleEndian(Data[0x0C..]); set => WriteUInt16LittleEndian(Data[0x0C..], value); }
|
||||
public override ushort SID16 { get => ReadUInt16LittleEndian(Data[0x0E..]); set => WriteUInt16LittleEndian(Data[0x0E..], value); }
|
||||
public override uint EXP { get => ReadUInt32LittleEndian(Data[0x10..]); set => WriteUInt32LittleEndian(Data[0x10..], value); }
|
||||
public override int Ability { get => ReadUInt16LittleEndian(Data[0x14..]); set => WriteUInt16LittleEndian(Data[0x14..], (ushort)value); }
|
||||
public override int AbilityNumber { get => Data[0x16] & 7; set => Data[0x16] = (byte)((Data[0x16] & ~7) | (value & 7)); }
|
||||
public bool IsFavorite { get => (Data[0x16] & 8) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~8) | ((value ? 1 : 0) << 3)); } // unused, was in LGPE but not in SWSH
|
||||
public bool CanGigantamax { get => (Data[0x16] & 16) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~16) | (value ? 16 : 0)); }
|
||||
public bool IsAlpha { get => (Data[0x16] & 32) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~32) | ((value ? 1 : 0) << 5)); }
|
||||
public bool IsNoble { get => (Data[0x16] & 64) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~64) | ((value ? 1 : 0) << 6)); }
|
||||
// 0x17 alignment unused
|
||||
public ushort MarkingValue { get => ReadUInt16LittleEndian(Data.AsSpan(0x18)); set => WriteUInt16LittleEndian(Data.AsSpan(0x18), value); }
|
||||
public ushort MarkingValue { get => ReadUInt16LittleEndian(Data[0x18..]); set => WriteUInt16LittleEndian(Data[0x18..], value); }
|
||||
// 0x1A alignment unused
|
||||
// 0x1B alignment unused
|
||||
public override uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x1C), value); }
|
||||
public override uint PID { get => ReadUInt32LittleEndian(Data[0x1C..]); set => WriteUInt32LittleEndian(Data[0x1C..], value); }
|
||||
public override Nature Nature { get => (Nature)Data[0x20]; set => Data[0x20] = (byte)value; }
|
||||
public override Nature StatNature { get => (Nature)Data[0x21]; set => Data[0x21] = (byte)value; }
|
||||
public override bool FatefulEncounter { get => (Data[0x22] & 1) == 1; set => Data[0x22] = (byte)((Data[0x22] & ~0x01) | (value ? 1 : 0)); }
|
||||
@@ -143,7 +147,7 @@ public void FixRelearn()
|
||||
public override byte Gender { get => (byte)((Data[0x22] >> 2) & 0x3); set => Data[0x22] = (byte)((Data[0x22] & 0xF3) | (value << 2)); }
|
||||
// 0x23 alignment unused
|
||||
|
||||
public override byte Form { get => Data[0x24]; set => WriteUInt16LittleEndian(Data.AsSpan(0x24), value); }
|
||||
public override byte Form { get => Data[0x24]; set => WriteUInt16LittleEndian(Data[0x24..], value); }
|
||||
public override int EV_HP { get => Data[0x26]; set => Data[0x26] = (byte)value; }
|
||||
public override int EV_ATK { get => Data[0x27]; set => Data[0x27] = (byte)value; }
|
||||
public override int EV_DEF { get => Data[0x28]; set => Data[0x28] = (byte)value; }
|
||||
@@ -237,7 +241,7 @@ public void FixRelearn()
|
||||
public byte RibbonCountMemoryContest { get => Data[0x3C]; set => HasContestMemoryRibbon = (Data[0x3C] = value) != 0; }
|
||||
public byte RibbonCountMemoryBattle { get => Data[0x3D]; set => HasBattleMemoryRibbon = (Data[0x3D] = value) != 0; }
|
||||
|
||||
public ushort AlphaMove { get => ReadUInt16LittleEndian(Data.AsSpan(0x3E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x3E), value); }
|
||||
public ushort AlphaMove { get => ReadUInt16LittleEndian(Data[0x3E..]); set => WriteUInt16LittleEndian(Data[0x3E..], value); }
|
||||
|
||||
// 0x40 Ribbon 1
|
||||
public bool RibbonMarkMisty { get => FlagUtil.GetFlag(Data, 0x40, 0); set => FlagUtil.SetFlag(Data, 0x40, 0, value); }
|
||||
@@ -313,18 +317,18 @@ public void FixRelearn()
|
||||
public bool RIB47_6 { get => FlagUtil.GetFlag(Data, 0x47, 6); set => FlagUtil.SetFlag(Data, 0x47, 6, value); }
|
||||
public bool RIB47_7 { get => FlagUtil.GetFlag(Data, 0x47, 7); set => FlagUtil.SetFlag(Data, 0x47, 7, value); }
|
||||
|
||||
public int RibbonCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b00000000_00011111__11111111_11111111__11111111_11111111__11111111_11111111)
|
||||
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000100_00011100__00000000_00000000__00000000_00000000);
|
||||
public int MarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000)
|
||||
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00111011_11100011__11111111_11111111__11111111_11111111);
|
||||
public int RibbonMarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b11111111_11111111__11111111_11111111__11111111_11111111__11111111_11111111)
|
||||
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00111111_11111111__11111111_11111111__11111111_11111111);
|
||||
public int RibbonCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x34..]) & 0b00000000_00011111__11111111_11111111__11111111_11111111__11111111_11111111)
|
||||
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x40..]) & 0b00000000_00000000__00000100_00011100__00000000_00000000__00000000_00000000);
|
||||
public int MarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x34..]) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000)
|
||||
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x40..]) & 0b00000000_00000000__00111011_11100011__11111111_11111111__11111111_11111111);
|
||||
public int RibbonMarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x34..]) & 0b11111111_11111111__11111111_11111111__11111111_11111111__11111111_11111111)
|
||||
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x40..]) & 0b00000000_00000000__00111111_11111111__11111111_11111111__11111111_11111111);
|
||||
|
||||
public bool HasMarkEncounter8 => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000)
|
||||
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000000_00000011__11111111_11111111__11111111_11111111) != 0;
|
||||
public bool HasMarkEncounter8 => BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x34..]) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000)
|
||||
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x40..]) & 0b00000000_00000000__00000000_00000011__11111111_11111111__11111111_11111111) != 0;
|
||||
public bool HasMarkEncounter9 => (Data[0x45] & 0b00111000) != 0;
|
||||
|
||||
public uint Sociability { get => ReadUInt32LittleEndian(Data.AsSpan(0x48)); set => WriteUInt32LittleEndian(Data.AsSpan(0x48), value); }
|
||||
public uint Sociability { get => ReadUInt32LittleEndian(Data[0x48..]); set => WriteUInt32LittleEndian(Data[0x48..], value); }
|
||||
|
||||
// 0x4C-0x4F unused
|
||||
|
||||
@@ -334,10 +338,10 @@ public void FixRelearn()
|
||||
|
||||
// 0x53 unused
|
||||
|
||||
public override ushort Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x54)); set => WriteUInt16LittleEndian(Data.AsSpan(0x54), value); }
|
||||
public override ushort Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x56)); set => WriteUInt16LittleEndian(Data.AsSpan(0x56), value); }
|
||||
public override ushort Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x58)); set => WriteUInt16LittleEndian(Data.AsSpan(0x58), value); }
|
||||
public override ushort Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x5A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5A), value); }
|
||||
public override ushort Move1 { get => ReadUInt16LittleEndian(Data[0x54..]); set => WriteUInt16LittleEndian(Data[0x54..], value); }
|
||||
public override ushort Move2 { get => ReadUInt16LittleEndian(Data[0x56..]); set => WriteUInt16LittleEndian(Data[0x56..], value); }
|
||||
public override ushort Move3 { get => ReadUInt16LittleEndian(Data[0x58..]); set => WriteUInt16LittleEndian(Data[0x58..], value); }
|
||||
public override ushort Move4 { get => ReadUInt16LittleEndian(Data[0x5A..]); set => WriteUInt16LittleEndian(Data[0x5A..], value); }
|
||||
|
||||
public override int Move1_PP { get => Data[0x5C]; set => Data[0x5C] = (byte)value; }
|
||||
public override int Move2_PP { get => Data[0x5D]; set => Data[0x5D] = (byte)value; }
|
||||
@@ -358,13 +362,13 @@ public override string Nickname
|
||||
public override int Move3_PPUps { get => Data[0x88]; set => Data[0x88] = (byte)value; }
|
||||
public override int Move4_PPUps { get => Data[0x89]; set => Data[0x89] = (byte)value; }
|
||||
|
||||
public override ushort RelearnMove1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x8A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x8A), value); }
|
||||
public override ushort RelearnMove2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x8C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x8C), value); }
|
||||
public override ushort RelearnMove3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x8E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x8E), value); }
|
||||
public override ushort RelearnMove4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x90)); set => WriteUInt16LittleEndian(Data.AsSpan(0x90), value); }
|
||||
public override ushort RelearnMove1 { get => ReadUInt16LittleEndian(Data[0x8A..]); set => WriteUInt16LittleEndian(Data[0x8A..], value); }
|
||||
public override ushort RelearnMove2 { get => ReadUInt16LittleEndian(Data[0x8C..]); set => WriteUInt16LittleEndian(Data[0x8C..], value); }
|
||||
public override ushort RelearnMove3 { get => ReadUInt16LittleEndian(Data[0x8E..]); set => WriteUInt16LittleEndian(Data[0x8E..], value); }
|
||||
public override ushort RelearnMove4 { get => ReadUInt16LittleEndian(Data[0x90..]); set => WriteUInt16LittleEndian(Data[0x90..], value); }
|
||||
|
||||
public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data.AsSpan(0x92)); set => WriteUInt16LittleEndian(Data.AsSpan(0x92), (ushort)value); }
|
||||
public uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x94)); set => WriteUInt32LittleEndian(Data.AsSpan(0x94), value); }
|
||||
public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data[0x92..]); set => WriteUInt16LittleEndian(Data[0x92..], (ushort)value); }
|
||||
public uint IV32 { get => ReadUInt32LittleEndian(Data[0x94..]); set => WriteUInt32LittleEndian(Data[0x94..], value); }
|
||||
public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); }
|
||||
public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); }
|
||||
public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); }
|
||||
@@ -376,8 +380,8 @@ public override string Nickname
|
||||
|
||||
public byte DynamaxLevel { get => Data[0x98]; set => Data[0x98] = value; }
|
||||
|
||||
public override int Status_Condition { get => ReadInt32LittleEndian(Data.AsSpan(0x9C)); set => WriteInt32LittleEndian(Data.AsSpan(0x9C), value); }
|
||||
public int UnkA0 { get => ReadInt32LittleEndian(Data.AsSpan(0xA0)); set => WriteInt32LittleEndian(Data.AsSpan(0xA0), value); }
|
||||
public override int Status_Condition { get => ReadInt32LittleEndian(Data[0x9C..]); set => WriteInt32LittleEndian(Data[0x9C..], value); }
|
||||
public int UnkA0 { get => ReadInt32LittleEndian(Data[0xA0..]); set => WriteInt32LittleEndian(Data[0xA0..], value); }
|
||||
public byte GV_HP { get => Data[0xA4]; set => Data[0xA4] = value; }
|
||||
public byte GV_ATK { get => Data[0xA5]; set => Data[0xA5] = value; }
|
||||
public byte GV_DEF { get => Data[0xA6]; set => Data[0xA6] = value; }
|
||||
@@ -387,8 +391,8 @@ public override string Nickname
|
||||
|
||||
// 0xAA-0xAB unused
|
||||
|
||||
public float HeightAbsolute { get => ReadSingleLittleEndian(Data.AsSpan(0xAC)); set => WriteSingleLittleEndian(Data.AsSpan(0xAC), value); }
|
||||
public float WeightAbsolute { get => ReadSingleLittleEndian(Data.AsSpan(0xB0)); set => WriteSingleLittleEndian(Data.AsSpan(0xB0), value); }
|
||||
public float HeightAbsolute { get => ReadSingleLittleEndian(Data[0xAC..]); set => WriteSingleLittleEndian(Data[0xAC..], value); }
|
||||
public float WeightAbsolute { get => ReadSingleLittleEndian(Data[0xB0..]); set => WriteSingleLittleEndian(Data[0xB0..], value); }
|
||||
|
||||
// 0xB4-0xB7 unused
|
||||
|
||||
@@ -404,12 +408,12 @@ public override string HandlingTrainerName
|
||||
public byte HandlingTrainerLanguage { get => Data[0xD3]; set => Data[0xD3] = value; }
|
||||
public override byte CurrentHandler { get => Data[0xD4]; set => Data[0xD4] = value; }
|
||||
// 0xD5 unused (alignment)
|
||||
public ushort HandlingTrainerID { get => ReadUInt16LittleEndian(Data.AsSpan(0xD6)); set => WriteUInt16LittleEndian(Data.AsSpan(0xD6), value); } // unused?
|
||||
public ushort HandlingTrainerID { get => ReadUInt16LittleEndian(Data[0xD6..]); set => WriteUInt16LittleEndian(Data[0xD6..], value); } // unused?
|
||||
public override byte HandlingTrainerFriendship { get => Data[0xD8]; set => Data[0xD8] = value; }
|
||||
public byte HandlingTrainerMemoryIntensity { get => Data[0xD9]; set => Data[0xD9] = value; }
|
||||
public byte HandlingTrainerMemory { get => Data[0xDA]; set => Data[0xDA] = value; }
|
||||
public byte HandlingTrainerMemoryFeeling { get => Data[0xDB]; set => Data[0xDB] = value; }
|
||||
public ushort HandlingTrainerMemoryVariable { get => ReadUInt16LittleEndian(Data.AsSpan(0xDC)); set => WriteUInt16LittleEndian(Data.AsSpan(0xDC), value); }
|
||||
public ushort HandlingTrainerMemoryVariable { get => ReadUInt16LittleEndian(Data[0xDC..]); set => WriteUInt16LittleEndian(Data[0xDC..], value); }
|
||||
|
||||
// 0xDE-0xEB unused
|
||||
|
||||
@@ -421,7 +425,7 @@ public override string HandlingTrainerName
|
||||
// public override byte ConsoleRegion { get => Data[0xF1]; set => Data[0xF1] = (byte)value; }
|
||||
public override int Language { get => Data[0xF2]; set => Data[0xF2] = (byte)value; }
|
||||
public int UnkF3 { get => Data[0xF3]; set => Data[0xF3] = (byte)value; }
|
||||
public uint FormArgument { get => ReadUInt32LittleEndian(Data.AsSpan(0xF4)); set => WriteUInt32LittleEndian(Data.AsSpan(0xF4), value); }
|
||||
public uint FormArgument { get => ReadUInt32LittleEndian(Data[0xF4..]); set => WriteUInt32LittleEndian(Data[0xF4..], value); }
|
||||
public byte FormArgumentRemain { get => (byte)FormArgument; set => FormArgument = (FormArgument & ~0xFFu) | value; }
|
||||
public byte FormArgumentElapsed { get => (byte)(FormArgument >> 8); set => FormArgument = (FormArgument & ~0xFF00u) | (uint)(value << 8); }
|
||||
public byte FormArgumentMaximum { get => (byte)(FormArgument >> 16); set => FormArgument = (FormArgument & ~0xFF0000u) | (uint)(value << 16); }
|
||||
@@ -439,7 +443,7 @@ public override string OriginalTrainerName
|
||||
public byte OriginalTrainerMemoryIntensity { get => Data[0x12B]; set => Data[0x12B] = value; }
|
||||
public byte OriginalTrainerMemory { get => Data[0x12C]; set => Data[0x12C] = value; }
|
||||
// 0x12D unused align
|
||||
public ushort OriginalTrainerMemoryVariable { get => ReadUInt16LittleEndian(Data.AsSpan(0x12E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x12E), value); }
|
||||
public ushort OriginalTrainerMemoryVariable { get => ReadUInt16LittleEndian(Data[0x12E..]); set => WriteUInt16LittleEndian(Data[0x12E..], value); }
|
||||
public byte OriginalTrainerMemoryFeeling { get => Data[0x130]; set => Data[0x130] = value; }
|
||||
public override byte EggYear { get => Data[0x131]; set => Data[0x131] = value; }
|
||||
public override byte EggMonth { get => Data[0x132]; set => Data[0x132] = value; }
|
||||
@@ -448,8 +452,8 @@ public override string OriginalTrainerName
|
||||
public override byte MetMonth { get => Data[0x135]; set => Data[0x135] = value; }
|
||||
public override byte MetDay { get => Data[0x136]; set => Data[0x136] = value; }
|
||||
public override byte Ball { get => Data[0x137]; set => Data[0x137] = value; }
|
||||
public override ushort EggLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0x138)); set => WriteUInt16LittleEndian(Data.AsSpan(0x138), value); }
|
||||
public override ushort MetLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0x13A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x13A), value); }
|
||||
public override ushort EggLocation { get => ReadUInt16LittleEndian(Data[0x138..]); set => WriteUInt16LittleEndian(Data[0x138..], value); }
|
||||
public override ushort MetLocation { get => ReadUInt16LittleEndian(Data[0x13A..]); set => WriteUInt16LittleEndian(Data[0x13A..], value); }
|
||||
// 0x13C unused align
|
||||
public override byte MetLevel { get => (byte)(Data[0x13D] & ~0x80); set => Data[0x13D] = (byte)((Data[0x13D] & 0x80) | value); }
|
||||
public override byte OriginalTrainerGender { get => (byte)(Data[0x13D] >> 7); set => Data[0x13D] = (byte)((Data[0x13D] & ~0x80) | (value << 7)); }
|
||||
@@ -477,23 +481,23 @@ public void SetMoveRecordFlag(int index, bool value = true)
|
||||
FlagUtil.SetFlag(Data, 0x13F + ofs, index & 7, value);
|
||||
}
|
||||
|
||||
public Span<byte> MoveRecordFlags => Data.AsSpan(0x13F, 14);
|
||||
public Span<byte> MoveRecordFlags => Data.Slice(0x13F, 14);
|
||||
public bool GetMoveRecordFlagAny() => MoveRecordFlags.ContainsAnyExcept<byte>(0);
|
||||
public void ClearMoveRecordFlags() => MoveRecordFlags.Clear();
|
||||
|
||||
public ulong Tracker
|
||||
{
|
||||
get => ReadUInt64LittleEndian(Data.AsSpan(0x14D));
|
||||
set => WriteUInt64LittleEndian(Data.AsSpan(0x14D), value);
|
||||
get => ReadUInt64LittleEndian(Data[0x14D..]);
|
||||
set => WriteUInt64LittleEndian(Data[0x14D..], value);
|
||||
}
|
||||
|
||||
public Span<byte> PurchasedRecord => Data.AsSpan(0x155, 8);
|
||||
public Span<byte> PurchasedRecord => Data.Slice(0x155, 8);
|
||||
public bool GetPurchasedRecordFlag(int index) => FlagUtil.GetFlag(PurchasedRecord, index >> 3, index & 7);
|
||||
public void SetPurchasedRecordFlag(int index, bool value) => FlagUtil.SetFlag(PurchasedRecord, index >> 3, index & 7, value);
|
||||
public bool GetPurchasedRecordFlagAny() => PurchasedRecord.ContainsAnyExcept<byte>(0);
|
||||
public int GetPurchasedCount() => BitOperations.PopCount(ReadUInt64LittleEndian(PurchasedRecord));
|
||||
|
||||
public Span<byte> MasteredRecord => Data.AsSpan(0x15D, 8);
|
||||
public Span<byte> MasteredRecord => Data.Slice(0x15D, 8);
|
||||
public bool GetMasteredRecordFlag(int index) => FlagUtil.GetFlag(MasteredRecord, index >> 3, index & 7);
|
||||
public void SetMasteredRecordFlag(int index, bool value) => FlagUtil.SetFlag(MasteredRecord, index >> 3, index & 7, value);
|
||||
public bool GetMasteredRecordFlagAny() => MasteredRecord.ContainsAnyExcept<byte>(0);
|
||||
@@ -502,12 +506,12 @@ public ulong Tracker
|
||||
#region Battle Stats
|
||||
public override byte Stat_Level { get => Data[0x168]; set => Data[0x168] = value; }
|
||||
// 0x149 unused alignment
|
||||
public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data.AsSpan(0x16A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x16A), (ushort)value); }
|
||||
public override int Stat_ATK { get => ReadUInt16LittleEndian(Data.AsSpan(0x16C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x16C), (ushort)value); }
|
||||
public override int Stat_DEF { get => ReadUInt16LittleEndian(Data.AsSpan(0x16E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x16E), (ushort)value); }
|
||||
public override int Stat_SPE { get => ReadUInt16LittleEndian(Data.AsSpan(0x170)); set => WriteUInt16LittleEndian(Data.AsSpan(0x170), (ushort)value); }
|
||||
public override int Stat_SPA { get => ReadUInt16LittleEndian(Data.AsSpan(0x172)); set => WriteUInt16LittleEndian(Data.AsSpan(0x172), (ushort)value); }
|
||||
public override int Stat_SPD { get => ReadUInt16LittleEndian(Data.AsSpan(0x174)); set => WriteUInt16LittleEndian(Data.AsSpan(0x174), (ushort)value); }
|
||||
public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data[0x16A..]); set => WriteUInt16LittleEndian(Data[0x16A..], (ushort)value); }
|
||||
public override int Stat_ATK { get => ReadUInt16LittleEndian(Data[0x16C..]); set => WriteUInt16LittleEndian(Data[0x16C..], (ushort)value); }
|
||||
public override int Stat_DEF { get => ReadUInt16LittleEndian(Data[0x16E..]); set => WriteUInt16LittleEndian(Data[0x16E..], (ushort)value); }
|
||||
public override int Stat_SPE { get => ReadUInt16LittleEndian(Data[0x170..]); set => WriteUInt16LittleEndian(Data[0x170..], (ushort)value); }
|
||||
public override int Stat_SPA { get => ReadUInt16LittleEndian(Data[0x172..]); set => WriteUInt16LittleEndian(Data[0x172..], (ushort)value); }
|
||||
public override int Stat_SPD { get => ReadUInt16LittleEndian(Data[0x174..]); set => WriteUInt16LittleEndian(Data[0x174..], (ushort)value); }
|
||||
#endregion
|
||||
|
||||
public override void LoadStats(IBaseStat p, Span<ushort> stats)
|
||||
|
||||
@@ -30,83 +30,86 @@ public sealed class PB7 : G6PKM, IHyperTrain, IAwakened, IScaledSizeValue, IComb
|
||||
public override PersonalInfo7GG PersonalInfo => PersonalTable.GG.GetFormEntry(Species, Form);
|
||||
|
||||
public PB7() : base(SIZE) { }
|
||||
public PB7(byte[] data) : base(DecryptParty(data)) { }
|
||||
public PB7(Memory<byte> data) : base(DecryptParty(data)) { }
|
||||
|
||||
private static byte[] DecryptParty(byte[] data)
|
||||
private static Memory<byte> DecryptParty(Memory<byte> data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted67(ref data);
|
||||
if (data.Length != SIZE)
|
||||
Array.Resize(ref data, SIZE);
|
||||
return data;
|
||||
if (data.Length >= SIZE)
|
||||
return data;
|
||||
|
||||
var result = new byte[SIZE];
|
||||
data.Span.CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override PB7 Clone() => new((byte[])Data.Clone());
|
||||
public override PB7 Clone() => new(Data.ToArray());
|
||||
|
||||
// Structure
|
||||
#region Block A
|
||||
public override uint EncryptionConstant
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data.AsSpan(0x00));
|
||||
set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value);
|
||||
get => ReadUInt32LittleEndian(Data);
|
||||
set => WriteUInt32LittleEndian(Data, value);
|
||||
}
|
||||
|
||||
public override ushort Sanity
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x04));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x04), value);
|
||||
get => ReadUInt16LittleEndian(Data[0x04..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x04..], value);
|
||||
}
|
||||
|
||||
public override ushort Checksum
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x06));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x06), value);
|
||||
get => ReadUInt16LittleEndian(Data[0x06..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x06..], value);
|
||||
}
|
||||
|
||||
public override ushort Species
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x08));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x08), value);
|
||||
get => ReadUInt16LittleEndian(Data[0x08..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x08..], value);
|
||||
}
|
||||
|
||||
public override int HeldItem
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x0A));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value);
|
||||
get => ReadUInt16LittleEndian(Data[0x0A..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x0A..], (ushort)value);
|
||||
}
|
||||
|
||||
public override uint ID32
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data.AsSpan(0x0C));
|
||||
set => WriteUInt32LittleEndian(Data.AsSpan(0x0C), value);
|
||||
get => ReadUInt32LittleEndian(Data[0x0C..]);
|
||||
set => WriteUInt32LittleEndian(Data[0x0C..], value);
|
||||
}
|
||||
|
||||
public override ushort TID16
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x0C));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x0C), value);
|
||||
get => ReadUInt16LittleEndian(Data[0x0C..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x0C..], value);
|
||||
}
|
||||
|
||||
public override ushort SID16
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x0E));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x0E), value);
|
||||
get => ReadUInt16LittleEndian(Data[0x0E..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x0E..], value);
|
||||
}
|
||||
|
||||
public override uint EXP
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data.AsSpan(0x10));
|
||||
set => WriteUInt32LittleEndian(Data.AsSpan(0x10), value);
|
||||
get => ReadUInt32LittleEndian(Data[0x10..]);
|
||||
set => WriteUInt32LittleEndian(Data[0x10..], value);
|
||||
}
|
||||
|
||||
public override int Ability { get => Data[0x14]; set => Data[0x14] = (byte)value; }
|
||||
public override int AbilityNumber { get => Data[0x15] & 7; set => Data[0x15] = (byte)((Data[0x15] & ~7) | (value & 7)); }
|
||||
public bool IsFavorite { get => (Data[0x15] & 8) != 0; set => Data[0x15] = (byte)((Data[0x15] & ~8) | ((value ? 1 : 0) << 3)); }
|
||||
public ushort MarkingValue { get => ReadUInt16LittleEndian(Data.AsSpan(0x16)); set => WriteUInt16LittleEndian(Data.AsSpan(0x16), value); }
|
||||
public ushort MarkingValue { get => ReadUInt16LittleEndian(Data[0x16..]); set => WriteUInt16LittleEndian(Data[0x16..], value); }
|
||||
|
||||
public override uint PID
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data.AsSpan(0x18));
|
||||
set => WriteUInt32LittleEndian(Data.AsSpan(0x18), value);
|
||||
get => ReadUInt32LittleEndian(Data[0x18..]);
|
||||
set => WriteUInt32LittleEndian(Data[0x18..], value);
|
||||
}
|
||||
|
||||
public override Nature Nature { get => (Nature)Data[0x1C]; set => Data[0x1C] = (byte)value; }
|
||||
@@ -129,7 +132,7 @@ public override uint PID
|
||||
public byte PokerusState { get => Data[0x2B]; set => Data[0x2B] = value; }
|
||||
public override int PokerusDays { get => PokerusState & 0xF; set => PokerusState = (byte)((PokerusState & ~0xF) | value); }
|
||||
public override int PokerusStrain { get => PokerusState >> 4; set => PokerusState = (byte)((PokerusState & 0xF) | (value << 4)); }
|
||||
public float HeightAbsolute { get => ReadSingleLittleEndian(Data.AsSpan(0x2C)); set => WriteSingleLittleEndian(Data.AsSpan(0x2C), value); }
|
||||
public float HeightAbsolute { get => ReadSingleLittleEndian(Data[0x2C..]); set => WriteSingleLittleEndian(Data[0x2C..], value); }
|
||||
private byte RIB0 { get => Data[0x30]; set => Data[0x30] = value; } // Ribbons are read as uints, but let's keep them per byte.
|
||||
private byte RIB1 { get => Data[0x31]; set => Data[0x31] = value; }
|
||||
private byte RIB2 { get => Data[0x32]; set => Data[0x32] = value; }
|
||||
@@ -201,12 +204,12 @@ public override uint PID
|
||||
// 0x39 Unused
|
||||
public byte HeightScalar { get => Data[0x3A]; set => Data[0x3A] = value; }
|
||||
public byte WeightScalar { get => Data[0x3B]; set => Data[0x3B] = value; }
|
||||
public uint FormArgument { get => ReadUInt32LittleEndian(Data.AsSpan(0x3C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x3C), value); }
|
||||
public uint FormArgument { get => ReadUInt32LittleEndian(Data[0x3C..]); set => WriteUInt32LittleEndian(Data[0x3C..], value); }
|
||||
public byte FormArgumentRemain { get => (byte)FormArgument; set => FormArgument = (FormArgument & ~0xFFu) | value; }
|
||||
public byte FormArgumentElapsed { get => (byte)(FormArgument >> 8); set => FormArgument = (FormArgument & ~0xFF00u) | (uint)(value << 8); }
|
||||
public byte FormArgumentMaximum { get => (byte)(FormArgument >> 16); set => FormArgument = (FormArgument & ~0xFF0000u) | (uint)(value << 16); }
|
||||
|
||||
public int RibbonCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x30)) & 0b00000000_00000011__11111111_11111111__11111111_11111111__11111111_11111111);
|
||||
public int RibbonCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data[0x30..]) & 0b00000000_00000011__11111111_11111111__11111111_11111111__11111111_11111111);
|
||||
|
||||
#endregion
|
||||
#region Block B
|
||||
@@ -218,26 +221,26 @@ public override string Nickname
|
||||
|
||||
public override ushort Move1
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x5A));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x5A), value);
|
||||
get => ReadUInt16LittleEndian(Data[0x5A..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x5A..], value);
|
||||
}
|
||||
|
||||
public override ushort Move2
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x5C));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x5C), value);
|
||||
get => ReadUInt16LittleEndian(Data[0x5C..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x5C..], value);
|
||||
}
|
||||
|
||||
public override ushort Move3
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x5E));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x5E), value);
|
||||
get => ReadUInt16LittleEndian(Data[0x5E..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x5E..], value);
|
||||
}
|
||||
|
||||
public override ushort Move4
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x60));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x60), value);
|
||||
get => ReadUInt16LittleEndian(Data[0x60..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x60..], value);
|
||||
}
|
||||
|
||||
public override int Move1_PP { get => Data[0x62]; set => Data[0x62] = (byte)value; }
|
||||
@@ -251,31 +254,31 @@ public override ushort Move4
|
||||
|
||||
public override ushort RelearnMove1
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x6A));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x6A), value);
|
||||
get => ReadUInt16LittleEndian(Data[0x6A..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x6A..], value);
|
||||
}
|
||||
|
||||
public override ushort RelearnMove2
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x6C));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x6C), value);
|
||||
get => ReadUInt16LittleEndian(Data[0x6C..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x6C..], value);
|
||||
}
|
||||
|
||||
public override ushort RelearnMove3
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x6E));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x6E), value);
|
||||
get => ReadUInt16LittleEndian(Data[0x6E..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x6E..], value);
|
||||
}
|
||||
|
||||
public override ushort RelearnMove4
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x70));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x70), value);
|
||||
get => ReadUInt16LittleEndian(Data[0x70..]);
|
||||
set => WriteUInt16LittleEndian(Data[0x70..], value);
|
||||
}
|
||||
|
||||
// 0x72 Unused
|
||||
// 0x73 Unused
|
||||
public override uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x74)); set => WriteUInt32LittleEndian(Data.AsSpan(0x74), value); }
|
||||
public override uint IV32 { get => ReadUInt32LittleEndian(Data[0x74..]); set => WriteUInt32LittleEndian(Data[0x74..], value); }
|
||||
public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); }
|
||||
public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); }
|
||||
public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); }
|
||||
@@ -314,7 +317,7 @@ public override string HandlingTrainerName
|
||||
public byte HT_Memory { get => Data[0xA5]; set => Data[0xA5] = value; }
|
||||
public byte HT_Feeling { get => Data[0xA6]; set => Data[0xA6] = value; }
|
||||
// 0xA7 Unused
|
||||
public ushort HT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0xA8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xA8), value); }
|
||||
public ushort HT_TextVar { get => ReadUInt16LittleEndian(Data[0xA8..]); set => WriteUInt16LittleEndian(Data[0xA8..], value); }
|
||||
// 0xAA Unused
|
||||
// 0xAB Unused
|
||||
public byte FieldEventFatigue1 { get => Data[0xAC]; set => Data[0xAC] = value; }
|
||||
@@ -401,8 +404,8 @@ public override string OriginalTrainerName
|
||||
public override byte MetMonth { get => Data[0xD5]; set => Data[0xD5] = value; }
|
||||
public override byte MetDay { get => Data[0xD6]; set => Data[0xD6] = value; }
|
||||
public int Rank { get => Data[0xD7]; set => Data[0xD7] = (byte)value; } // unused but fetched for stat calcs, and set for trpoke data?
|
||||
public override ushort EggLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0xD8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xD8), value); }
|
||||
public override ushort MetLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0xDA)); set => WriteUInt16LittleEndian(Data.AsSpan(0xDA), value); }
|
||||
public override ushort EggLocation { get => ReadUInt16LittleEndian(Data[0xD8..]); set => WriteUInt16LittleEndian(Data[0xD8..], value); }
|
||||
public override ushort MetLocation { get => ReadUInt16LittleEndian(Data[0xDA..]); set => WriteUInt16LittleEndian(Data[0xDA..], value); }
|
||||
public override byte Ball { get => Data[0xDC]; set => Data[0xDC] = value; }
|
||||
public override byte MetLevel { get => (byte)(Data[0xDD] & ~0x80); set => Data[0xDD] = (byte)((Data[0xDD] & 0x80) | value); }
|
||||
public override byte OriginalTrainerGender { get => (byte)(Data[0xDD] >> 7); set => Data[0xDD] = (byte)((Data[0xDD] & ~0x80) | (value << 7)); }
|
||||
@@ -418,22 +421,22 @@ public override string OriginalTrainerName
|
||||
// 0xE1 Unused
|
||||
// 0xE2 Unused
|
||||
public override int Language { get => Data[0xE3]; set => Data[0xE3] = (byte)value; }
|
||||
public float WeightAbsolute { get => ReadSingleLittleEndian(Data.AsSpan(0xE4)); set => WriteSingleLittleEndian(Data.AsSpan(0xE4), value); }
|
||||
public float WeightAbsolute { get => ReadSingleLittleEndian(Data[0xE4..]); set => WriteSingleLittleEndian(Data[0xE4..], value); }
|
||||
#endregion
|
||||
#region Battle Stats
|
||||
public override int Status_Condition { get => ReadInt32LittleEndian(Data.AsSpan(0xE8)); set => WriteInt32LittleEndian(Data.AsSpan(0xE8), value); }
|
||||
public override int Status_Condition { get => ReadInt32LittleEndian(Data[0xE8..]); set => WriteInt32LittleEndian(Data[0xE8..], value); }
|
||||
public override byte Stat_Level { get => Data[0xEC]; set => Data[0xEC] = value; }
|
||||
public byte DirtType { get => Data[0xED]; set => Data[0xED] = value; }
|
||||
public byte DirtLocation { get => Data[0xEE]; set => Data[0xEE] = value; }
|
||||
// 0xEF unused
|
||||
public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data.AsSpan(0xF0)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF0), (ushort)value); }
|
||||
public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data.AsSpan(0xF2)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF2), (ushort)value); }
|
||||
public override int Stat_ATK { get => ReadUInt16LittleEndian(Data.AsSpan(0xF4)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF4), (ushort)value); }
|
||||
public override int Stat_DEF { get => ReadUInt16LittleEndian(Data.AsSpan(0xF6)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF6), (ushort)value); }
|
||||
public override int Stat_SPE { get => ReadUInt16LittleEndian(Data.AsSpan(0xF8)); set => WriteUInt16LittleEndian(Data.AsSpan(0xF8), (ushort)value); }
|
||||
public override int Stat_SPA { get => ReadUInt16LittleEndian(Data.AsSpan(0xFA)); set => WriteUInt16LittleEndian(Data.AsSpan(0xFA), (ushort)value); }
|
||||
public override int Stat_SPD { get => ReadUInt16LittleEndian(Data.AsSpan(0xFC)); set => WriteUInt16LittleEndian(Data.AsSpan(0xFC), (ushort)value); }
|
||||
public int Stat_CP { get => ReadUInt16LittleEndian(Data.AsSpan(0xFE)); set => WriteUInt16LittleEndian(Data.AsSpan(0xFE), (ushort)value); }
|
||||
public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data[0xF0..]); set => WriteUInt16LittleEndian(Data[0xF0..], (ushort)value); }
|
||||
public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data[0xF2..]); set => WriteUInt16LittleEndian(Data[0xF2..], (ushort)value); }
|
||||
public override int Stat_ATK { get => ReadUInt16LittleEndian(Data[0xF4..]); set => WriteUInt16LittleEndian(Data[0xF4..], (ushort)value); }
|
||||
public override int Stat_DEF { get => ReadUInt16LittleEndian(Data[0xF6..]); set => WriteUInt16LittleEndian(Data[0xF6..], (ushort)value); }
|
||||
public override int Stat_SPE { get => ReadUInt16LittleEndian(Data[0xF8..]); set => WriteUInt16LittleEndian(Data[0xF8..], (ushort)value); }
|
||||
public override int Stat_SPA { get => ReadUInt16LittleEndian(Data[0xFA..]); set => WriteUInt16LittleEndian(Data[0xFA..], (ushort)value); }
|
||||
public override int Stat_SPD { get => ReadUInt16LittleEndian(Data[0xFC..]); set => WriteUInt16LittleEndian(Data[0xFC..], (ushort)value); }
|
||||
public int Stat_CP { get => ReadUInt16LittleEndian(Data[0xFE..]); set => WriteUInt16LittleEndian(Data[0xFE..], (ushort)value); }
|
||||
public bool Stat_Mega { get => Data[0x100] != 0; set => Data[0x100] = value ? (byte)1 : (byte)0; }
|
||||
public int Stat_MegaForm { get => Data[0x101]; set => Data[0x101] = (byte)value; }
|
||||
// 102/103 unused
|
||||
|
||||
@@ -35,8 +35,8 @@ public PB8()
|
||||
AffixedRibbon = Core.AffixedRibbon.None;
|
||||
}
|
||||
|
||||
public PB8(byte[] data) : base(data) { }
|
||||
public override PB8 Clone() => new((byte[])Data.Clone());
|
||||
public PB8(Memory<byte> data) : base(data) { }
|
||||
public override PB8 Clone() => new(Data.ToArray());
|
||||
|
||||
public bool IsDprIllegal
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@ private static byte[] EnsurePartySize(byte[] data)
|
||||
|
||||
public override PK1 Clone()
|
||||
{
|
||||
PK1 clone = new((byte[])Data.Clone(), Japanese);
|
||||
PK1 clone = new(Data.ToArray(), Japanese);
|
||||
OriginalTrainerTrash.CopyTo(clone.OriginalTrainerTrash);
|
||||
NicknameTrash.CopyTo(clone.NicknameTrash);
|
||||
return clone;
|
||||
@@ -47,7 +47,7 @@ public override PK1 Clone()
|
||||
#region Stored Attributes
|
||||
public byte SpeciesInternal { get => Data[0]; set => Data[0] = value; } // raw access
|
||||
public override ushort Species { get => SpeciesConverter.GetNational1(SpeciesInternal); set => SetSpeciesValues(value); }
|
||||
public override int Stat_HPCurrent { get => ReadUInt16BigEndian(Data.AsSpan(0x1)); set => WriteUInt16BigEndian(Data.AsSpan(0x1), (ushort)value); }
|
||||
public override int Stat_HPCurrent { get => ReadUInt16BigEndian(Data[0x1..]); set => WriteUInt16BigEndian(Data[0x1..], (ushort)value); }
|
||||
public int Stat_LevelBox { get => Data[3]; set => Data[3] = (byte)value; }
|
||||
public override int Status_Condition { get => Data[4]; set => Data[4] = (byte)value; }
|
||||
public byte Type1 { get => Data[5]; set => Data[5] = value; }
|
||||
@@ -57,14 +57,14 @@ public override PK1 Clone()
|
||||
public override ushort Move2 { get => Data[9]; set => Data[9] = (byte)value; }
|
||||
public override ushort Move3 { get => Data[10]; set => Data[10] = (byte)value; }
|
||||
public override ushort Move4 { get => Data[11]; set => Data[11] = (byte)value; }
|
||||
public override ushort TID16 { get => ReadUInt16BigEndian(Data.AsSpan(0xC)); set => WriteUInt16BigEndian(Data.AsSpan(0xC), value); }
|
||||
public override uint EXP { get => ReadUInt32BigEndian(Data.AsSpan(0xE)) >> 8; set => WriteUInt32BigEndian(Data.AsSpan(0xE), (value << 8) | Data[0x11]); }
|
||||
public override int EV_HP { get => ReadUInt16BigEndian(Data.AsSpan(0x11)); set => WriteUInt16BigEndian(Data.AsSpan(0x11), (ushort)value); }
|
||||
public override int EV_ATK { get => ReadUInt16BigEndian(Data.AsSpan(0x13)); set => WriteUInt16BigEndian(Data.AsSpan(0x13), (ushort)value); }
|
||||
public override int EV_DEF { get => ReadUInt16BigEndian(Data.AsSpan(0x15)); set => WriteUInt16BigEndian(Data.AsSpan(0x15), (ushort)value); }
|
||||
public override int EV_SPE { get => ReadUInt16BigEndian(Data.AsSpan(0x17)); set => WriteUInt16BigEndian(Data.AsSpan(0x17), (ushort)value); }
|
||||
public override int EV_SPC { get => ReadUInt16BigEndian(Data.AsSpan(0x19)); set => WriteUInt16BigEndian(Data.AsSpan(0x19), (ushort)value); }
|
||||
public override ushort DV16 { get => ReadUInt16BigEndian(Data.AsSpan(0x1B)); set => WriteUInt16BigEndian(Data.AsSpan(0x1B), value); }
|
||||
public override ushort TID16 { get => ReadUInt16BigEndian(Data[0xC..]); set => WriteUInt16BigEndian(Data[0xC..], value); }
|
||||
public override uint EXP { get => ReadUInt32BigEndian(Data[0xE..]) >> 8; set => WriteUInt32BigEndian(Data[0xE..], (value << 8) | Data[0x11]); }
|
||||
public override int EV_HP { get => ReadUInt16BigEndian(Data[0x11..]); set => WriteUInt16BigEndian(Data[0x11..], (ushort)value); }
|
||||
public override int EV_ATK { get => ReadUInt16BigEndian(Data[0x13..]); set => WriteUInt16BigEndian(Data[0x13..], (ushort)value); }
|
||||
public override int EV_DEF { get => ReadUInt16BigEndian(Data[0x15..]); set => WriteUInt16BigEndian(Data[0x15..], (ushort)value); }
|
||||
public override int EV_SPE { get => ReadUInt16BigEndian(Data[0x17..]); set => WriteUInt16BigEndian(Data[0x17..], (ushort)value); }
|
||||
public override int EV_SPC { get => ReadUInt16BigEndian(Data[0x19..]); set => WriteUInt16BigEndian(Data[0x19..], (ushort)value); }
|
||||
public override ushort DV16 { get => ReadUInt16BigEndian(Data[0x1B..]); set => WriteUInt16BigEndian(Data[0x1B..], value); }
|
||||
public override int Move1_PP { get => Data[0x1D] & 0x3F; set => Data[0x1D] = (byte)((Data[0x1D] & 0xC0) | Math.Min(63, value)); }
|
||||
public override int Move2_PP { get => Data[0x1E] & 0x3F; set => Data[0x1E] = (byte)((Data[0x1E] & 0xC0) | Math.Min(63, value)); }
|
||||
public override int Move3_PP { get => Data[0x1F] & 0x3F; set => Data[0x1F] = (byte)((Data[0x1F] & 0xC0) | Math.Min(63, value)); }
|
||||
@@ -77,11 +77,11 @@ public override PK1 Clone()
|
||||
|
||||
#region Party Attributes
|
||||
public override byte Stat_Level { get => Data[0x21]; set => Stat_LevelBox = Data[0x21] = value; }
|
||||
public override int Stat_HPMax { get => ReadUInt16BigEndian(Data.AsSpan(0x22)); set => WriteUInt16BigEndian(Data.AsSpan(0x22), (ushort)value); }
|
||||
public override int Stat_ATK { get => ReadUInt16BigEndian(Data.AsSpan(0x24)); set => WriteUInt16BigEndian(Data.AsSpan(0x24), (ushort)value); }
|
||||
public override int Stat_DEF { get => ReadUInt16BigEndian(Data.AsSpan(0x26)); set => WriteUInt16BigEndian(Data.AsSpan(0x26), (ushort)value); }
|
||||
public override int Stat_SPE { get => ReadUInt16BigEndian(Data.AsSpan(0x28)); set => WriteUInt16BigEndian(Data.AsSpan(0x28), (ushort)value); }
|
||||
public int Stat_SPC { get => ReadUInt16BigEndian(Data.AsSpan(0x2A)); set => WriteUInt16BigEndian(Data.AsSpan(0x2A), (ushort)value); }
|
||||
public override int Stat_HPMax { get => ReadUInt16BigEndian(Data[0x22..]); set => WriteUInt16BigEndian(Data[0x22..], (ushort)value); }
|
||||
public override int Stat_ATK { get => ReadUInt16BigEndian(Data[0x24..]); set => WriteUInt16BigEndian(Data[0x24..], (ushort)value); }
|
||||
public override int Stat_DEF { get => ReadUInt16BigEndian(Data[0x26..]); set => WriteUInt16BigEndian(Data[0x26..], (ushort)value); }
|
||||
public override int Stat_SPE { get => ReadUInt16BigEndian(Data[0x28..]); set => WriteUInt16BigEndian(Data[0x28..], (ushort)value); }
|
||||
public int Stat_SPC { get => ReadUInt16BigEndian(Data[0x2A..]); set => WriteUInt16BigEndian(Data[0x2A..], (ushort)value); }
|
||||
// Leave SPA and SPD as alias for SPC
|
||||
public override int Stat_SPA { get => Stat_SPC; set => Stat_SPC = value; }
|
||||
public override int Stat_SPD { get => Stat_SPC; set { } }
|
||||
@@ -108,8 +108,7 @@ private void SetSpeciesValues(ushort species)
|
||||
SpeciesInternal = internalID;
|
||||
|
||||
var pi = PersonalTable.RB[species];
|
||||
Type1 = pi.Type1;
|
||||
Type2 = pi.Type2;
|
||||
SetTypes(pi);
|
||||
|
||||
// Before updating catch rate, check if non-standard
|
||||
if (IsValidCatchRateAnyPreEvo((byte)species, CatchRate))
|
||||
@@ -119,6 +118,12 @@ private void SetSpeciesValues(ushort species)
|
||||
CatchRate = pi.CatchRate;
|
||||
}
|
||||
|
||||
public void SetTypes<T>(T pi) where T : IPersonalType
|
||||
{
|
||||
Type1 = pi.Type1;
|
||||
Type2 = pi.Type2;
|
||||
}
|
||||
|
||||
private static bool IsValidCatchRateAnyPreEvo(byte species, byte rate)
|
||||
{
|
||||
if (IsCatchRateHeldItem(rate))
|
||||
@@ -158,7 +163,7 @@ private static bool IsValidCatchRateAnyPreEvo(byte species, byte rate)
|
||||
public PK2 ConvertToPK2()
|
||||
{
|
||||
PK2 pk2 = new(Japanese) {Species = Species};
|
||||
Data.AsSpan(7, 0x1A).CopyTo(pk2.Data.AsSpan(1));
|
||||
Data.Slice(7, 0x1A).CopyTo(pk2.Data[1..]);
|
||||
OriginalTrainerTrash.CopyTo(pk2.OriginalTrainerTrash);
|
||||
NicknameTrash.CopyTo(pk2.NicknameTrash);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ private static byte[] EnsurePartySize(byte[] data)
|
||||
|
||||
public override PK2 Clone()
|
||||
{
|
||||
PK2 clone = new((byte[])Data.Clone(), Japanese) { IsEgg = IsEgg };
|
||||
PK2 clone = new(Data.ToArray(), Japanese) { IsEgg = IsEgg };
|
||||
OriginalTrainerTrash.CopyTo(clone.OriginalTrainerTrash);
|
||||
NicknameTrash.CopyTo(clone.NicknameTrash);
|
||||
return clone;
|
||||
@@ -48,19 +48,19 @@ public override PK2 Clone()
|
||||
public override ushort Species { get => Data[0]; set => Data[0] = (byte)value; }
|
||||
public byte SpeciesInternal { get => Data[0]; set => Data[0] = value; } // Alias with a different type.
|
||||
public override int SpriteItem => ItemConverter.GetItemFuture2((byte)HeldItem);
|
||||
public override int HeldItem { get => Data[0x1]; set => Data[0x1] = (byte)value; }
|
||||
public override int HeldItem { get => Data[1]; set => Data[1] = (byte)value; }
|
||||
public override ushort Move1 { get => Data[2]; set => Data[2] = (byte)value; }
|
||||
public override ushort Move2 { get => Data[3]; set => Data[3] = (byte)value; }
|
||||
public override ushort Move3 { get => Data[4]; set => Data[4] = (byte)value; }
|
||||
public override ushort Move4 { get => Data[5]; set => Data[5] = (byte)value; }
|
||||
public override ushort TID16 { get => ReadUInt16BigEndian(Data.AsSpan(6)); set => WriteUInt16BigEndian(Data.AsSpan(6), value); }
|
||||
public override uint EXP { get => ReadUInt32BigEndian(Data.AsSpan(0x08)) >> 8; set => WriteUInt32BigEndian(Data.AsSpan(8), (value << 8) | Data[0xB]); }
|
||||
public override int EV_HP { get => ReadUInt16BigEndian(Data.AsSpan(0x0B)); set => WriteUInt16BigEndian(Data.AsSpan(0xB), (ushort)value); }
|
||||
public override int EV_ATK { get => ReadUInt16BigEndian(Data.AsSpan(0x0D)); set => WriteUInt16BigEndian(Data.AsSpan(0xD), (ushort)value); }
|
||||
public override int EV_DEF { get => ReadUInt16BigEndian(Data.AsSpan(0x0F)); set => WriteUInt16BigEndian(Data.AsSpan(0xF), (ushort)value); }
|
||||
public override int EV_SPE { get => ReadUInt16BigEndian(Data.AsSpan(0x11)); set => WriteUInt16BigEndian(Data.AsSpan(0x11), (ushort)value); }
|
||||
public override int EV_SPC { get => ReadUInt16BigEndian(Data.AsSpan(0x13)); set => WriteUInt16BigEndian(Data.AsSpan(0x13), (ushort)value); }
|
||||
public override ushort DV16 { get => ReadUInt16BigEndian(Data.AsSpan(0x15)); set => WriteUInt16BigEndian(Data.AsSpan(0x15), value); }
|
||||
public override ushort TID16 { get => ReadUInt16BigEndian(Data[6..]); set => WriteUInt16BigEndian(Data[6..], value); }
|
||||
public override uint EXP { get => ReadUInt32BigEndian(Data[0x08..]) >> 8; set => WriteUInt32BigEndian(Data[8..], (value << 8) | Data[0xB]); }
|
||||
public override int EV_HP { get => ReadUInt16BigEndian(Data[0x0B..]); set => WriteUInt16BigEndian(Data[0xB..], (ushort)value); }
|
||||
public override int EV_ATK { get => ReadUInt16BigEndian(Data[0x0D..]); set => WriteUInt16BigEndian(Data[0xD..], (ushort)value); }
|
||||
public override int EV_DEF { get => ReadUInt16BigEndian(Data[0x0F..]); set => WriteUInt16BigEndian(Data[0xF..], (ushort)value); }
|
||||
public override int EV_SPE { get => ReadUInt16BigEndian(Data[0x11..]); set => WriteUInt16BigEndian(Data[0x11..], (ushort)value); }
|
||||
public override int EV_SPC { get => ReadUInt16BigEndian(Data[0x13..]); set => WriteUInt16BigEndian(Data[0x13..], (ushort)value); }
|
||||
public override ushort DV16 { get => ReadUInt16BigEndian(Data[0x15..]); set => WriteUInt16BigEndian(Data[0x15..], value); }
|
||||
public override int Move1_PP { get => Data[0x17] & 0x3F; set => Data[0x17] = (byte)((Data[0x17] & 0xC0) | Math.Min(63, value)); }
|
||||
public override int Move2_PP { get => Data[0x18] & 0x3F; set => Data[0x18] = (byte)((Data[0x18] & 0xC0) | Math.Min(63, value)); }
|
||||
public override int Move3_PP { get => Data[0x19] & 0x3F; set => Data[0x19] = (byte)((Data[0x19] & 0xC0) | Math.Min(63, value)); }
|
||||
@@ -74,7 +74,7 @@ public override PK2 Clone()
|
||||
public override int PokerusDays { get => PokerusState & 0xF; set => PokerusState = (byte)((PokerusState & ~0xF) | value); }
|
||||
public override int PokerusStrain { get => PokerusState >> 4; set => PokerusState = (byte)((PokerusState & 0xF) | (value << 4)); }
|
||||
// Crystal only Caught Data
|
||||
public ushort CaughtData { get => ReadUInt16BigEndian(Data.AsSpan(0x1D)); set => WriteUInt16BigEndian(Data.AsSpan(0x1D), value); }
|
||||
public ushort CaughtData { get => ReadUInt16BigEndian(Data[0x1D..]); set => WriteUInt16BigEndian(Data[0x1D..], value); }
|
||||
public int MetTimeOfDay { get => (CaughtData >> 14) & 0x3; set => CaughtData = (ushort)((CaughtData & 0x3FFF) | ((value & 0x3) << 14)); }
|
||||
public override byte MetLevel { get => (byte)((CaughtData >> 8) & 0x3F); set => CaughtData = (ushort)((CaughtData & 0xC0FF) | ((value & 0x3F) << 8)); }
|
||||
public override byte OriginalTrainerGender { get => (byte)((CaughtData >> 7) & 1); set => CaughtData = (ushort)((CaughtData & 0xFF7F) | ((value & 1) << 7)); }
|
||||
@@ -91,13 +91,13 @@ public override byte Stat_Level
|
||||
#region Party Attributes
|
||||
public override int Status_Condition { get => Data[0x20]; set => Data[0x20] = (byte)value; }
|
||||
|
||||
public override int Stat_HPCurrent { get => ReadUInt16BigEndian(Data.AsSpan(0x22)); set => WriteUInt16BigEndian(Data.AsSpan(0x22), (ushort)value); }
|
||||
public override int Stat_HPMax { get => ReadUInt16BigEndian(Data.AsSpan(0x24)); set => WriteUInt16BigEndian(Data.AsSpan(0x24), (ushort)value); }
|
||||
public override int Stat_ATK { get => ReadUInt16BigEndian(Data.AsSpan(0x26)); set => WriteUInt16BigEndian(Data.AsSpan(0x26), (ushort)value); }
|
||||
public override int Stat_DEF { get => ReadUInt16BigEndian(Data.AsSpan(0x28)); set => WriteUInt16BigEndian(Data.AsSpan(0x28), (ushort)value); }
|
||||
public override int Stat_SPE { get => ReadUInt16BigEndian(Data.AsSpan(0x2A)); set => WriteUInt16BigEndian(Data.AsSpan(0x2A), (ushort)value); }
|
||||
public override int Stat_SPA { get => ReadUInt16BigEndian(Data.AsSpan(0x2C)); set => WriteUInt16BigEndian(Data.AsSpan(0x2C), (ushort)value); }
|
||||
public override int Stat_SPD { get => ReadUInt16BigEndian(Data.AsSpan(0x2E)); set => WriteUInt16BigEndian(Data.AsSpan(0x2E), (ushort)value); }
|
||||
public override int Stat_HPCurrent { get => ReadUInt16BigEndian(Data[0x22..]); set => WriteUInt16BigEndian(Data[0x22..], (ushort)value); }
|
||||
public override int Stat_HPMax { get => ReadUInt16BigEndian(Data[0x24..]); set => WriteUInt16BigEndian(Data[0x24..], (ushort)value); }
|
||||
public override int Stat_ATK { get => ReadUInt16BigEndian(Data[0x26..]); set => WriteUInt16BigEndian(Data[0x26..], (ushort)value); }
|
||||
public override int Stat_DEF { get => ReadUInt16BigEndian(Data[0x28..]); set => WriteUInt16BigEndian(Data[0x28..], (ushort)value); }
|
||||
public override int Stat_SPE { get => ReadUInt16BigEndian(Data[0x2A..]); set => WriteUInt16BigEndian(Data[0x2A..], (ushort)value); }
|
||||
public override int Stat_SPA { get => ReadUInt16BigEndian(Data[0x2C..]); set => WriteUInt16BigEndian(Data[0x2C..], (ushort)value); }
|
||||
public override int Stat_SPD { get => ReadUInt16BigEndian(Data[0x2E..]); set => WriteUInt16BigEndian(Data[0x2E..], (ushort)value); }
|
||||
#endregion
|
||||
|
||||
public override bool IsEgg { get; set; }
|
||||
@@ -114,7 +114,8 @@ public override byte Stat_Level
|
||||
public PK1 ConvertToPK1()
|
||||
{
|
||||
PK1 pk1 = new(Japanese);
|
||||
Array.Copy(Data, 0x1, pk1.Data, 0x7, 0x1A);
|
||||
var dest = pk1.Data.Slice(0x7, 0x1A);
|
||||
Data[1..].CopyTo(dest);
|
||||
pk1.Species = Species; // This will take care of Typing :)
|
||||
|
||||
var lvl = Stat_Level;
|
||||
|
||||
@@ -15,13 +15,17 @@ public sealed class PK3 : G3PKM, ISanityChecksum
|
||||
public override PersonalInfo3 PersonalInfo => PersonalTable.RS[Species];
|
||||
|
||||
public PK3() : base(PokeCrypto.SIZE_3PARTY) { }
|
||||
public PK3(byte[] data) : base(DecryptParty(data)) { }
|
||||
public PK3(Memory<byte> data) : base(DecryptParty(data)) { }
|
||||
|
||||
private static byte[] DecryptParty(byte[] data)
|
||||
private static Memory<byte> DecryptParty(Memory<byte> data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted3(ref data);
|
||||
Array.Resize(ref data, PokeCrypto.SIZE_3PARTY);
|
||||
return data;
|
||||
if (data.Length >= PokeCrypto.SIZE_3PARTY)
|
||||
return data;
|
||||
|
||||
var result = new byte[PokeCrypto.SIZE_3PARTY];
|
||||
data.Span.CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override PK3 Clone()
|
||||
@@ -29,25 +33,25 @@ public override PK3 Clone()
|
||||
// Don't use the byte[] constructor, the DecryptIfEncrypted call is based on checksum.
|
||||
// An invalid checksum will shuffle the data; we already know it's un-shuffled. Set up manually.
|
||||
PK3 pk = new();
|
||||
Data.CopyTo(pk.Data, 0);
|
||||
Data.CopyTo(pk.Data);
|
||||
return pk;
|
||||
}
|
||||
|
||||
private const string EggNameJapanese = "タマゴ";
|
||||
|
||||
// Trash Bytes
|
||||
public override Span<byte> NicknameTrash => Data.AsSpan(0x08, 10); // no inaccessible terminator
|
||||
public override Span<byte> OriginalTrainerTrash => Data.AsSpan(0x14, 7); // no inaccessible terminator
|
||||
public override Span<byte> NicknameTrash => Data.Slice(0x08, 10); // no inaccessible terminator
|
||||
public override Span<byte> OriginalTrainerTrash => Data.Slice(0x14, 7); // no inaccessible terminator
|
||||
public override int TrashCharCountTrainer => 7;
|
||||
public override int TrashCharCountNickname => 10;
|
||||
|
||||
// At top for System.Reflection execution order hack
|
||||
|
||||
// 0x20 Intro
|
||||
public override uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0x00)); set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value); }
|
||||
public override uint ID32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x04)); set => WriteUInt32LittleEndian(Data.AsSpan(0x04), value); }
|
||||
public override ushort TID16 { get => ReadUInt16LittleEndian(Data.AsSpan(0x04)); set => WriteUInt16LittleEndian(Data.AsSpan(0x04), value); }
|
||||
public override ushort SID16 { get => ReadUInt16LittleEndian(Data.AsSpan(0x06)); set => WriteUInt16LittleEndian(Data.AsSpan(0x06), value); }
|
||||
public override uint PID { get => ReadUInt32LittleEndian(Data); set => WriteUInt32LittleEndian(Data, value); }
|
||||
public override uint ID32 { get => ReadUInt32LittleEndian(Data[0x04..]); set => WriteUInt32LittleEndian(Data[0x04..], value); }
|
||||
public override ushort TID16 { get => ReadUInt16LittleEndian(Data[0x04..]); set => WriteUInt16LittleEndian(Data[0x04..], value); }
|
||||
public override ushort SID16 { get => ReadUInt16LittleEndian(Data[0x06..]); set => WriteUInt16LittleEndian(Data[0x06..], value); }
|
||||
public override string Nickname
|
||||
{
|
||||
get => StringConverter3.GetString(NicknameTrash, Language);
|
||||
@@ -63,11 +67,11 @@ public override string OriginalTrainerName
|
||||
set => StringConverter3.SetString(OriginalTrainerTrash, value, 7, Language, StringConverterOption.None);
|
||||
}
|
||||
public override byte MarkingValue { get => (byte)SwapBits(Data[0x1B], 1, 2); set => Data[0x1B] = (byte)SwapBits(value, 1, 2); }
|
||||
public ushort Checksum { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), value); }
|
||||
public ushort Sanity { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), value); }
|
||||
public ushort Checksum { get => ReadUInt16LittleEndian(Data[0x1C..]); set => WriteUInt16LittleEndian(Data[0x1C..], value); }
|
||||
public ushort Sanity { get => ReadUInt16LittleEndian(Data[0x1E..]); set => WriteUInt16LittleEndian(Data[0x1E..], value); }
|
||||
|
||||
#region Block A
|
||||
public override ushort SpeciesInternal { get => ReadUInt16LittleEndian(Data.AsSpan(0x20)); set => WriteUInt16LittleEndian(Data.AsSpan(0x20), value); } // raw access
|
||||
public override ushort SpeciesInternal { get => ReadUInt16LittleEndian(Data[0x20..]); set => WriteUInt16LittleEndian(Data[0x20..], value); } // raw access
|
||||
|
||||
public override ushort Species
|
||||
{
|
||||
@@ -80,9 +84,9 @@ public override ushort Species
|
||||
}
|
||||
|
||||
public override int SpriteItem => ItemConverter.GetItemFuture3((ushort)HeldItem);
|
||||
public override int HeldItem { get => ReadUInt16LittleEndian(Data.AsSpan(0x22)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22), (ushort)value); }
|
||||
public override int HeldItem { get => ReadUInt16LittleEndian(Data[0x22..]); set => WriteUInt16LittleEndian(Data[0x22..], (ushort)value); }
|
||||
|
||||
public override uint EXP { get => ReadUInt32LittleEndian(Data.AsSpan(0x24)); set => WriteUInt32LittleEndian(Data.AsSpan(0x24), value); }
|
||||
public override uint EXP { get => ReadUInt32LittleEndian(Data[0x24..]); set => WriteUInt32LittleEndian(Data[0x24..], value); }
|
||||
private byte PPUps { get => Data[0x28]; set => Data[0x28] = value; }
|
||||
public override int Move1_PPUps { get => (PPUps >> 0) & 3; set => PPUps = (byte)((PPUps & ~(3 << 0)) | (value << 0)); }
|
||||
public override int Move2_PPUps { get => (PPUps >> 2) & 3; set => PPUps = (byte)((PPUps & ~(3 << 2)) | (value << 2)); }
|
||||
@@ -93,10 +97,10 @@ public override ushort Species
|
||||
#endregion
|
||||
|
||||
#region Block B
|
||||
public override ushort Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x2C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2C), value); }
|
||||
public override ushort Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x2E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x2E), value); }
|
||||
public override ushort Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x30)); set => WriteUInt16LittleEndian(Data.AsSpan(0x30), value); }
|
||||
public override ushort Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x32)); set => WriteUInt16LittleEndian(Data.AsSpan(0x32), value); }
|
||||
public override ushort Move1 { get => ReadUInt16LittleEndian(Data[0x2C..]); set => WriteUInt16LittleEndian(Data[0x2C..], value); }
|
||||
public override ushort Move2 { get => ReadUInt16LittleEndian(Data[0x2E..]); set => WriteUInt16LittleEndian(Data[0x2E..], value); }
|
||||
public override ushort Move3 { get => ReadUInt16LittleEndian(Data[0x30..]); set => WriteUInt16LittleEndian(Data[0x30..], value); }
|
||||
public override ushort Move4 { get => ReadUInt16LittleEndian(Data[0x32..]); set => WriteUInt16LittleEndian(Data[0x32..], value); }
|
||||
public override int Move1_PP { get => Data[0x34]; set => Data[0x34] = (byte)value; }
|
||||
public override int Move2_PP { get => Data[0x35]; set => Data[0x35] = (byte)value; }
|
||||
public override int Move3_PP { get => Data[0x36]; set => Data[0x36] = (byte)value; }
|
||||
@@ -124,13 +128,13 @@ public override ushort Species
|
||||
public override int PokerusStrain { get => PokerusState >> 4; set => PokerusState = (byte)((PokerusState & 0xF) | (value << 4)); }
|
||||
public override ushort MetLocation { get => Data[0x45]; set => Data[0x45] = (byte)value; }
|
||||
// Origins
|
||||
private ushort Origins { get => ReadUInt16LittleEndian(Data.AsSpan(0x46)); set => WriteUInt16LittleEndian(Data.AsSpan(0x46), value); }
|
||||
private ushort Origins { get => ReadUInt16LittleEndian(Data[0x46..]); set => WriteUInt16LittleEndian(Data[0x46..], value); }
|
||||
public override byte MetLevel { get => (byte)(Origins & 0x7F); set => Origins = (ushort)((Origins & ~0x7F) | value); }
|
||||
public override GameVersion Version { get => (GameVersion)((Origins >> 7) & 0xF); set => Origins = (ushort)((Origins & ~0x780) | (((byte)value & 0xF) << 7)); }
|
||||
public override byte Ball { get => (byte)((Origins >> 11) & 0xF); set => Origins = (ushort)((Origins & ~0x7800) | ((value & 0xF) << 11)); }
|
||||
public override byte OriginalTrainerGender { get => (byte)((Origins >> 15) & 1); set => Origins = (ushort)((Origins & ~(1 << 15)) | ((value & 1) << 15)); }
|
||||
|
||||
public uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x48)); set => WriteUInt32LittleEndian(Data.AsSpan(0x48), value); }
|
||||
public uint IV32 { get => ReadUInt32LittleEndian(Data[0x48..]); set => WriteUInt32LittleEndian(Data[0x48..], value); }
|
||||
public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); }
|
||||
public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); }
|
||||
public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); }
|
||||
@@ -155,7 +159,7 @@ public override bool IsEgg
|
||||
|
||||
public override bool AbilityBit { get => IV32 >> 31 == 1; set => IV32 = (IV32 & 0x7FFFFFFF) | (value ? 1u << 31 : 0u); }
|
||||
|
||||
private uint RIB0 { get => ReadUInt32LittleEndian(Data.AsSpan(0x4C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x4C), value); }
|
||||
private uint RIB0 { get => ReadUInt32LittleEndian(Data[0x4C..]); set => WriteUInt32LittleEndian(Data[0x4C..], value); }
|
||||
public override byte RibbonCountG3Cool { get => (byte)((RIB0 >> 00) & 7); set => RIB0 = ((RIB0 & ~(7u << 00)) | ((uint)(value & 7) << 00)); }
|
||||
public override byte RibbonCountG3Beauty { get => (byte)((RIB0 >> 03) & 7); set => RIB0 = ((RIB0 & ~(7u << 03)) | ((uint)(value & 7) << 03)); }
|
||||
public override byte RibbonCountG3Cute { get => (byte)((RIB0 >> 06) & 7); set => RIB0 = ((RIB0 & ~(7u << 06)) | ((uint)(value & 7) << 06)); }
|
||||
@@ -183,16 +187,16 @@ public override bool IsEgg
|
||||
#endregion
|
||||
|
||||
#region Battle Stats
|
||||
public override int Status_Condition { get => ReadInt32LittleEndian(Data.AsSpan(0x50)); set => WriteInt32LittleEndian(Data.AsSpan(0x50), value); }
|
||||
public override int Status_Condition { get => ReadInt32LittleEndian(Data[0x50..]); set => WriteInt32LittleEndian(Data[0x50..], value); }
|
||||
public override byte Stat_Level { get => Data[0x54]; set => Data[0x54] = value; }
|
||||
public sbyte HeldMailID { get => (sbyte)Data[0x55]; set => Data[0x55] = (byte)value; }
|
||||
public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data.AsSpan(0x56)); set => WriteUInt16LittleEndian(Data.AsSpan(0x56), (ushort)value); }
|
||||
public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data.AsSpan(0x58)); set => WriteUInt16LittleEndian(Data.AsSpan(0x58), (ushort)value); }
|
||||
public override int Stat_ATK { get => ReadUInt16LittleEndian(Data.AsSpan(0x5A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5A), (ushort)value); }
|
||||
public override int Stat_DEF { get => ReadUInt16LittleEndian(Data.AsSpan(0x5C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5C), (ushort)value); }
|
||||
public override int Stat_SPE { get => ReadUInt16LittleEndian(Data.AsSpan(0x5E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5E), (ushort)value); }
|
||||
public override int Stat_SPA { get => ReadUInt16LittleEndian(Data.AsSpan(0x60)); set => WriteUInt16LittleEndian(Data.AsSpan(0x60), (ushort)value); }
|
||||
public override int Stat_SPD { get => ReadUInt16LittleEndian(Data.AsSpan(0x62)); set => WriteUInt16LittleEndian(Data.AsSpan(0x62), (ushort)value); }
|
||||
public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data[0x56..]); set => WriteUInt16LittleEndian(Data[0x56..], (ushort)value); }
|
||||
public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data[0x58..]); set => WriteUInt16LittleEndian(Data[0x58..], (ushort)value); }
|
||||
public override int Stat_ATK { get => ReadUInt16LittleEndian(Data[0x5A..]); set => WriteUInt16LittleEndian(Data[0x5A..], (ushort)value); }
|
||||
public override int Stat_DEF { get => ReadUInt16LittleEndian(Data[0x5C..]); set => WriteUInt16LittleEndian(Data[0x5C..], (ushort)value); }
|
||||
public override int Stat_SPE { get => ReadUInt16LittleEndian(Data[0x5E..]); set => WriteUInt16LittleEndian(Data[0x5E..], (ushort)value); }
|
||||
public override int Stat_SPA { get => ReadUInt16LittleEndian(Data[0x60..]); set => WriteUInt16LittleEndian(Data[0x60..], (ushort)value); }
|
||||
public override int Stat_SPD { get => ReadUInt16LittleEndian(Data[0x62..]); set => WriteUInt16LittleEndian(Data[0x62..], (ushort)value); }
|
||||
#endregion
|
||||
|
||||
protected override byte[] Encrypt()
|
||||
@@ -201,7 +205,7 @@ protected override byte[] Encrypt()
|
||||
return PokeCrypto.EncryptArray3(Data);
|
||||
}
|
||||
|
||||
private ushort CalculateChecksum() => Checksums.Add16(Data.AsSpan()[0x20..PokeCrypto.SIZE_3STORED]);
|
||||
private ushort CalculateChecksum() => Checksums.Add16(Data[0x20..PokeCrypto.SIZE_3STORED]);
|
||||
|
||||
public override void RefreshChecksum()
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user