PKHeX/PKHeX.Core/Legality/Localization/LegalityLocalizationContext.cs
Kurt 9792455f34 Refactor to use Context over Generation
Generation was always more weak; am I paranoid about potential VC3? maybe
Better indicates the move source for LGPE exclusive moves, etc.
2026-02-15 02:15:50 -06:00

158 lines
8.1 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);
public string GetFormName(ushort species, byte form, EntityContext context) => FormConverter.GetStringFromForm(species, form, Strings, context);
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, EntityContext current)
{
var result = Format(move, index, Settings.Moves.FormatMove);
var original = move.Context;
if (current != original && original != 0)
result += $" [{original}]";
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));
}