PKHeX/PKHeX.Core/Legality/Localization/LegalityLocalizationContext.cs
Kurt 3ef46268e9 Indicate invalid PP(ups) count on localize
Viewing the invalid mon will have the UI fix it, so at least it gives some clarity as to what is actually being flagged.
VC->Bank is the big offender here.
2026-02-05 10:04:32 -06:00

157 lines
7.9 KiB
C#

using System;
using static PKHeX.Core.LegalityCheckResultCode;
namespace PKHeX.Core;
/// <summary>
/// Value object that contains the context for localization of legality checks.
/// </summary>
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(LegalityCheckResultCode code) => RibbonVerifier.GetMessage(Analysis, Strings.Ribbons, code);
public string GetStatName(int displayIndex) => GetSafe(Settings.General.StatNames, displayIndex);
public string GetMoveName(ushort move) => GetSafe(Strings.movelist, move);
public string GetItemName(ushort item) => GetSafe(Strings.itemlist, item);
public string GetSpeciesName(ushort species) => GetSafe(Strings.specieslist, species);
public string GetConsoleRegion3DS(int index) => GetSafe(Strings.console3ds, index);
public string GetRibbonName(RibbonIndex index) => Strings.Ribbons.GetNameSafe($"Ribbon{index}", out var result) ? result : index.ToString();
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, Settings.Description(chk.Judgement), str);
}
private string GetInternalString(CheckResult chk)
{
var code = chk.Result;
if (code is External)
return ExternalLegalityCheck.Localize(chk, Settings, Analysis);
var template = code.GetTemplate(Settings.Lines);
if (code < FirstWithArgument)
return template;
if (code.IsArgument)
return string.Format(template, chk.Argument);
if (code.IsArgument2)
return string.Format(template, chk.Argument, chk.Argument2);
if (code.IsMove)
return string.Format(template, GetMoveName(chk.Argument));
if (code.IsItem)
return string.Format(template, GetItemName(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 < FirstMemoryWithValue)
return string.Format(template, GetTrainer(chk.Argument));
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?
RibbonsInvalid_0 => string.Format(format, GetRibbonMessage(code)),
RibbonsMissing_0 => string.Format(format, GetRibbonMessage(code)),
WordFilterFlaggedPattern_01 => string.Format(format, WordFilter.GetPattern((WordFilterType)chk.Argument, chk.Argument2), (WordFilterType)chk.Argument),
WordFilterInvalidCharacter_0 => string.Format(format, chk.Argument, 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((RibbonIndex)chk.Argument)),
RibbonMarkingMissing_0 => string.Format(format, GetRibbonName((RibbonIndex)chk.Argument)),
RibbonMarkingAffixed_0 => string.Format(format, GetRibbonName((RibbonIndex)chk.Argument)),
StoredSlotSourceInvalid_0 => string.Format(format, (StorageSlotSource)chk.Argument),
HintEvolvesToRareForm_0 => string.Format(format, chk.Argument == 1),
FormInvalidRangeLEQ_0F => string.Format(format, chk.Argument, Analysis.Entity.Form),
OTLanguageShouldBe_0or1 => string.Format(format, GetLanguageName(chk.Argument), GetLanguageName(chk.Argument2), GetLanguageName(Analysis.Entity.Language)),
G6SuperTrainBagHitsInvalid_012 => string.Format(format, (Analysis.Entity as PK6)?.TrainingBagHits ?? 0, chk.Argument, chk.Argument2),
>= 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, Settings.Description(move.Judgement), index, move.Summary(this));
}