Fixed main repo code to not expect snake cased locale strings

This commit is contained in:
Bertie690 2025-08-18 21:00:03 -04:00
parent d2b0a3147b
commit 6b98afa34f
3 changed files with 66 additions and 47 deletions

View File

@ -44,7 +44,10 @@ import { PokemonData } from "#system/pokemon-data";
import { MusicPreference } from "#system/settings";
import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
import { isNullOrUndefined, NumberHolder, randInt, randSeedInt, randSeedItem, randSeedShuffle } from "#utils/common";
import { getEnumKeys } from "#utils/enums";
import { getRandomLocaleKey } from "#utils/i18n";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import { toCamelCase } from "#utils/strings";
import i18next from "i18next";
/** the i18n namespace for the encounter */
@ -984,14 +987,17 @@ function doTradeReceivedSequence(
}
function generateRandomTraderName() {
const length = TrainerType.YOUNGSTER - TrainerType.ACE_TRAINER + 1;
// +1 avoids TrainerType.UNKNOWN
const classKey = `trainersCommon:${TrainerType[randInt(length) + 1]}`;
// Some trainers have 2 gendered pools, some do not
const genderKey = i18next.exists(`${classKey}.MALE`) ? (randInt(2) === 0 ? ".MALE" : ".FEMALE") : "";
const trainerNameKey = randSeedItem(Object.keys(i18next.t(`${classKey}${genderKey}`, { returnObjects: true })));
const trainerNameString = i18next.t(`${classKey}${genderKey}.${trainerNameKey}`);
// Some names have an '&' symbol and need to be trimmed to a single name instead of a double name
const trainerNames = trainerNameString.split(" & ");
return trainerNames[randInt(trainerNames.length)];
const allTrainerNames = getEnumKeys(TrainerType);
// Exclude TrainerType.UNKNOWN and everything after Ace Trainers (grunts and unique trainers)
const eligibleNames = allTrainerNames.slice(
1,
allTrainerNames.indexOf(TrainerType[TrainerType.YOUNGSTER] as keyof typeof TrainerType),
);
const randomTrainer = toCamelCase(randSeedItem(eligibleNames));
const classKey = `trainersCommon:${randomTrainer}`;
// Pick a random gender for ones with gendered pools, or access the raw object for ones without.
const genderKey = i18next.exists(`${classKey}.male`) ? randSeedItem([".male", ".female"]) : "";
const trainerNameString = getRandomLocaleKey(`${classKey}${genderKey}`)[1];
// Split the string by &s (for duo trainers)
return randSeedItem(trainerNameString.split(" & "));
}

View File

@ -16,14 +16,11 @@ import type { PersistentModifier } from "#modifiers/modifier";
import { getIsInitialized, initI18n } from "#plugins/i18n";
import type { TrainerConfig } from "#trainers/trainer-config";
import { trainerConfigs } from "#trainers/trainer-config";
import {
TrainerPartyCompoundTemplate,
type TrainerPartyTemplate,
trainerPartyTemplates,
} from "#trainers/trainer-party-template";
import { TrainerPartyCompoundTemplate, type TrainerPartyTemplate } from "#trainers/trainer-party-template";
import { randSeedInt, randSeedItem, randSeedWeightedItem } from "#utils/common";
import { getRandomLocaleKey } from "#utils/i18n";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import { toSnakeCase } from "#utils/strings";
import { toCamelCase, toSnakeCase } from "#utils/strings";
import i18next from "i18next";
export class Trainer extends Phaser.GameObjects.Container {
@ -35,6 +32,18 @@ export class Trainer extends Phaser.GameObjects.Container {
public partnerNameKey: string | undefined;
public originalIndexes: { [key: number]: number } = {};
/**
* Create a new Trainer.
* @param trainerType - The {@linkcode TrainerType} for this trainer, used to determine
* name, sprite, party contents and other details.
* @param variant - The {@linkcode TrainerVariant} for this trainer (if any are available)
* @param partyTemplateIndex - If provided, will override the trainer's party template with the given
* version.
* @param nameKey - If provided, will override the name key of the trainer
* @param partnerNameKey - If provided, will override the
* @param trainerConfigOverride - If provided, will override the trainer config for the given trainer type
* @todo Review how many of these parameters we actually need
*/
constructor(
trainerType: TrainerType,
variant: TrainerVariant,
@ -44,13 +53,11 @@ export class Trainer extends Phaser.GameObjects.Container {
trainerConfigOverride?: TrainerConfig,
) {
super(globalScene, -72, 80);
this.config = trainerConfigs.hasOwnProperty(trainerType)
? trainerConfigs[trainerType]
: trainerConfigs[TrainerType.ACE_TRAINER];
if (trainerConfigOverride) {
this.config = trainerConfigOverride;
}
this.config =
trainerConfigOverride ??
(trainerConfigs.hasOwnProperty(trainerType)
? trainerConfigs[trainerType]
: trainerConfigs[TrainerType.ACE_TRAINER]);
this.variant = variant;
this.partyTemplateIndex = Math.min(
@ -59,20 +66,21 @@ export class Trainer extends Phaser.GameObjects.Container {
: randSeedWeightedItem(this.config.partyTemplates.map((_, i) => i)),
this.config.partyTemplates.length - 1,
);
const classKey = `trainersCommon:${TrainerType[trainerType]}`;
// TODO: Rework this and add actual error handling for missing names
const classKey = `trainersCommon:${toCamelCase(TrainerType[trainerType])}`;
if (i18next.exists(classKey, { returnObjects: true })) {
if (nameKey) {
this.nameKey = nameKey;
this.name = i18next.t(nameKey);
} else {
const genderKey = i18next.exists(`${classKey}.MALE`)
const genderKey = i18next.exists(`${classKey}.male`)
? variant === TrainerVariant.FEMALE
? ".FEMALE"
: ".MALE"
? ".female"
: ".male"
: "";
const trainerKey = randSeedItem(Object.keys(i18next.t(`${classKey}${genderKey}`, { returnObjects: true })));
this.nameKey = `${classKey}${genderKey}.${trainerKey}`;
[this.nameKey, this.name] = getRandomLocaleKey(`${classKey}${genderKey}`);
}
this.name = i18next.t(this.nameKey);
if (variant === TrainerVariant.DOUBLE) {
if (this.config.doubleOnly) {
if (partnerNameKey) {
@ -82,16 +90,8 @@ export class Trainer extends Phaser.GameObjects.Container {
[this.name, this.partnerName] = this.name.split(" & ");
}
} else {
const partnerGenderKey = i18next.exists(`${classKey}.FEMALE`) ? ".FEMALE" : "";
const partnerTrainerKey = randSeedItem(
Object.keys(
i18next.t(`${classKey}${partnerGenderKey}`, {
returnObjects: true,
}),
),
);
this.partnerNameKey = `${classKey}${partnerGenderKey}.${partnerTrainerKey}`;
this.partnerName = i18next.t(this.partnerNameKey);
const partnerGenderKey = i18next.exists(`${classKey}.fenale`) ? ".fenale" : "";
[this.partnerNameKey, this.partnerName] = getRandomLocaleKey(`${classKey}${partnerGenderKey}`);
}
}
}
@ -109,10 +109,6 @@ export class Trainer extends Phaser.GameObjects.Container {
break;
}
console.log(
Object.keys(trainerPartyTemplates)[Object.values(trainerPartyTemplates).indexOf(this.getPartyTemplate())],
);
const getSprite = (hasShadow?: boolean, forceFemale?: boolean) => {
const ret = globalScene.addFieldSprite(
0,
@ -157,9 +153,9 @@ export class Trainer extends Phaser.GameObjects.Container {
/**
* Returns the name of the trainer based on the provided trainer slot and the option to include a title.
* @param {TrainerSlot} trainerSlot - The slot to determine which name to use. Defaults to TrainerSlot.NONE.
* @param {boolean} includeTitle - Whether to include the title in the returned name. Defaults to false.
* @returns {string} - The formatted name of the trainer.
* @param rainerSlot - The slot to determine which name to use; default `TrainerSlot.NONE`
* @param includeTitle - Whether to include the title in the returned name; default `false`
* @returns - The formatted name of the trainer
*/
getName(trainerSlot: TrainerSlot = TrainerSlot.NONE, includeTitle = false): string {
// Get the base title based on the trainer slot and variant.

17
src/utils/i18n.ts Normal file
View File

@ -0,0 +1,17 @@
import { randSeedItem } from "#utils/common";
import i18next from "i18next";
/**
* Select a random i18n key from all nested keys in the given object.
* @param key - The i18n key to retrieve a random value of.
* The key's value should be an object containing numerical keys (starting from 1).
* @returns A typle containing the key and value pair.
* @privateRemarks
* The reason such "array-like" keys are not stored as actual arrays is due to the
* translation software used by the Translation Team (Mozilla Pontoon)
* not supporting arrays in any capacity.
*/
export function getRandomLocaleKey(key: string): [key: string, value: string] {
const keyName = `${key}.${randSeedItem(Object.keys(i18next.t("key", { returnObjects: true })))}`;
return [keyName, i18next.t(keyName)];
}