Extract loadPokemonVariantAssets out of BattleScene

This commit is contained in:
Sirz Benjie 2025-04-01 11:44:00 -05:00
parent 0717a37ee3
commit 7b98d1b78c
No known key found for this signature in database
GPG Key ID: 4A524B4D196C759E
6 changed files with 165 additions and 58 deletions

View File

@ -106,8 +106,8 @@ import PokemonInfoContainer from "#app/ui/pokemon-info-container";
import { biomeDepths, getBiomeName } from "#app/data/balance/biomes"; import { biomeDepths, getBiomeName } from "#app/data/balance/biomes";
import { SceneBase } from "#app/scene-base"; import { SceneBase } from "#app/scene-base";
import CandyBar from "#app/ui/candy-bar"; import CandyBar from "#app/ui/candy-bar";
import type { Variant, VariantSet } from "#app/data/variant"; import type { Variant } from "#app/data/variant";
import { variantColorCache, variantData, clearVariantData } from "#app/data/variant"; import { variantData, clearVariantData } from "#app/data/variant";
import type { Localizable } from "#app/interfaces/locales"; import type { Localizable } from "#app/interfaces/locales";
import Overrides from "#app/overrides"; import Overrides from "#app/overrides";
import { InputsController } from "#app/inputs-controller"; import { InputsController } from "#app/inputs-controller";
@ -421,35 +421,6 @@ export default class BattleScene extends SceneBase {
); );
} }
/**
* Load the variant assets for the given sprite and stores them in {@linkcode variantColorCache}
*/
public async loadPokemonVariantAssets(spriteKey: string, fileRoot: string, variant?: Variant): Promise<void> {
const useExpSprite = this.experimentalSprites && hasExpSprite(spriteKey);
if (useExpSprite) {
fileRoot = `exp/${fileRoot}`;
}
let variantConfig = variantData;
fileRoot.split("/").map(p => (variantConfig ? (variantConfig = variantConfig[p]) : null));
const variantSet = variantConfig as VariantSet;
return new Promise<void>(resolve => {
if (variantSet && variant !== undefined && variantSet[variant] === 1) {
if (variantColorCache.hasOwnProperty(spriteKey)) {
return resolve();
}
this.cachedFetch(`./images/pokemon/variant/${fileRoot}.json`)
.then(res => res.json())
.then(c => {
variantColorCache[spriteKey] = c;
resolve();
});
} else {
resolve();
}
});
}
async preload() { async preload() {
if (DEBUG_RNG) { if (DEBUG_RNG) {
const originalRealInRange = Phaser.Math.RND.realInRange; const originalRealInRange = Phaser.Math.RND.realInRange;
@ -824,23 +795,15 @@ export default class BattleScene extends SceneBase {
return fetch(url, init); return fetch(url, init);
} }
initStarterColors(): Promise<void> { async initStarterColors() {
return new Promise(resolve => {
if (starterColors) { if (starterColors) {
return resolve(); return;
} }
const sc = await this.cachedFetch("./starter-colors.json").then(res => res.json());
this.cachedFetch("./starter-colors.json")
.then(res => res.json())
.then(sc => {
starterColors = {}; starterColors = {};
for (const key of Object.keys(sc)) { for (const key of Object.keys(sc)) {
starterColors[key] = sc[key]; starterColors[key] = sc[key];
} }
resolve();
});
});
} }
public getPlayerParty(): PlayerPokemon[] { public getPlayerParty(): PlayerPokemon[] {

View File

@ -31,6 +31,7 @@ import { variantData } from "#app/data/variant";
import { speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters"; import { speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters";
import { SpeciesFormKey } from "#enums/species-form-key"; import { SpeciesFormKey } from "#enums/species-form-key";
import { starterPassiveAbilities } from "#app/data/balance/passives"; import { starterPassiveAbilities } from "#app/data/balance/passives";
import { loadPokemonVariantAssets } from "#app/sprites/pokemon-sprite";
export enum Region { export enum Region {
NORMAL, NORMAL,
@ -387,6 +388,7 @@ export abstract class PokemonSpeciesForm {
return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`;
} }
/** Compute the sprite ID of the pokemon form. */
getSpriteId(female: boolean, formIndex?: number, shiny?: boolean, variant = 0, back?: boolean): string { getSpriteId(female: boolean, formIndex?: number, shiny?: boolean, variant = 0, back?: boolean): string {
if (formIndex === undefined || this instanceof PokemonForm) { if (formIndex === undefined || this instanceof PokemonForm) {
formIndex = this.formIndex; formIndex = this.formIndex;
@ -394,7 +396,9 @@ export abstract class PokemonSpeciesForm {
const formSpriteKey = this.getFormSpriteKey(formIndex); const formSpriteKey = this.getFormSpriteKey(formIndex);
const showGenderDiffs = const showGenderDiffs =
this.genderDiffs && female && ![SpeciesFormKey.MEGA, SpeciesFormKey.GIGANTAMAX].find(k => formSpriteKey === k); this.genderDiffs &&
female &&
![SpeciesFormKey.MEGA, SpeciesFormKey.GIGANTAMAX].includes(formSpriteKey as SpeciesFormKey);
const baseSpriteKey = `${showGenderDiffs ? "female__" : ""}${this.speciesId}${formSpriteKey ? `-${formSpriteKey}` : ""}`; const baseSpriteKey = `${showGenderDiffs ? "female__" : ""}${this.speciesId}${formSpriteKey ? `-${formSpriteKey}` : ""}`;
@ -621,7 +625,9 @@ export abstract class PokemonSpeciesForm {
const spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant, back) const spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant, back)
.replace("variant/", "") .replace("variant/", "")
.replace(/_[1-3]$/, ""); .replace(/_[1-3]$/, "");
globalScene.loadPokemonVariantAssets(spriteKey, spritePath, variant).then(() => resolve()); if (!Utils.isNullOrUndefined(variant)) {
loadPokemonVariantAssets(spriteKey, spritePath, variant).then(() => resolve());
}
}); });
if (startLoad) { if (startLoad) {
if (!globalScene.load.isLoading()) { if (!globalScene.load.isLoading()) {

View File

@ -2236,12 +2236,7 @@ export const trainerConfigs: TrainerConfigs = {
Species.PHANTUMP, Species.PHANTUMP,
Species.PUMPKABOO, Species.PUMPKABOO,
], ],
[TrainerPoolTier.RARE]: [ [TrainerPoolTier.RARE]: [Species.SNEASEL, Species.LITWICK, Species.PAWNIARD, Species.NOIBAT],
Species.SNEASEL,
Species.LITWICK,
Species.PAWNIARD,
Species.NOIBAT,
],
[TrainerPoolTier.SUPER_RARE]: [Species.SLIGGOO, Species.HISUI_SLIGGOO, Species.HISUI_AVALUGG], [TrainerPoolTier.SUPER_RARE]: [Species.SLIGGOO, Species.HISUI_SLIGGOO, Species.HISUI_AVALUGG],
}), }),
[TrainerType.BRYONY]: new TrainerConfig(++t) [TrainerType.BRYONY]: new TrainerConfig(++t)

View File

@ -7,6 +7,7 @@ import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/enc
import type { Variant } from "#app/data/variant"; import type { Variant } from "#app/data/variant";
import { doShinySparkleAnim } from "#app/field/anims"; import { doShinySparkleAnim } from "#app/field/anims";
import PlayAnimationConfig = Phaser.Types.Animations.PlayAnimationConfig; import PlayAnimationConfig = Phaser.Types.Animations.PlayAnimationConfig;
import { loadPokemonVariantAssets } from "#app/sprites/pokemon-sprite";
type KnownFileRoot = type KnownFileRoot =
| "arenas" | "arenas"
@ -233,8 +234,8 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
this.spriteConfigs.forEach(config => { this.spriteConfigs.forEach(config => {
if (config.isPokemon) { if (config.isPokemon) {
globalScene.loadPokemonAtlas(config.spriteKey, config.fileRoot); globalScene.loadPokemonAtlas(config.spriteKey, config.fileRoot);
if (config.isShiny) { if (config.isShiny && !isNullOrUndefined(config.variant)) {
shinyPromises.push(globalScene.loadPokemonVariantAssets(config.spriteKey, config.fileRoot, config.variant)); shinyPromises.push(loadPokemonVariantAssets(config.spriteKey, config.fileRoot, config.variant));
} }
} else if (config.isItem) { } else if (config.isItem) {
globalScene.loadAtlas("items", ""); globalScene.loadAtlas("items", "");

View File

@ -0,0 +1,11 @@
import type { Moves } from "#enums/moves";
import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
/**
* Asynchronously load the animations and assets for the provided moves.
* @param moveIds An array of move IDs to load assets for.
*/
export async function loadMoveAnimations(moveIds: Moves[]) {
await Promise.allSettled(moveIds.map(m => initMoveAnim(m)));
await loadMoveAnimAssets(moveIds);
}

View File

@ -0,0 +1,131 @@
import { globalScene } from "#app/global-scene";
import { isNullOrUndefined } from "#app/utils";
import { variantColorCache, variantData } from "#app/data/variant";
import { Gender } from "#app/data/gender";
import { hasExpSprite } from "./sprite-utilts";
import type { Variant, VariantSet } from "#app/data/variant";
import type Pokemon from "#app/field/pokemon";
import type BattleScene from "#app/battle-scene";
// Regex patterns
/** Regex matching double underscores */
const DUNDER_REGEX = /\_{2}/g;
/**
* Gracefully handle errors loading a variant sprite. Log if it fails and attempt to fall back on
* non-experimental sprites before giving up.
*
* @param cacheKey the cache key for the variant color sprite
* @param attemptedSpritePath the sprite path that failed to load
* @param useExpSprite was the attempted sprite experimental
* @param battleSpritePath the filename of the sprite
* @param optionalParams any additional params to log
*/
async function fallbackVariantColor(
cacheKey: string,
attemptedSpritePath: string,
useExpSprite: boolean,
battleSpritePath: string,
...optionalParams: any[]
) {
console.warn(`Could not load ${attemptedSpritePath}!`, ...optionalParams);
if (useExpSprite) {
await populateVariantColorCache(cacheKey, false, battleSpritePath);
}
}
/**
* Attempt to process variant sprite.
*
* @param cacheKey the cache key for the variant color sprite
* @param useExpSprite should the experimental sprite be used
* @param battleSpritePath the filename of the sprite
*/
export async function populateVariantColorCache(cacheKey: string, useExpSprite: boolean, battleSpritePath: string) {
const spritePath = `./images/pokemon/variant/${useExpSprite ? "exp/" : ""}${battleSpritePath}.json`;
return globalScene
.cachedFetch(spritePath)
.then(res => {
// Prevent the JSON from processing if it failed to load
if (!res.ok) {
return fallbackVariantColor(cacheKey, res.url, useExpSprite, battleSpritePath, res.status, res.statusText);
}
return res.json();
})
.catch(error => {
return fallbackVariantColor(cacheKey, spritePath, useExpSprite, battleSpritePath, error);
})
.then(c => {
if (!isNullOrUndefined(c)) {
variantColorCache[cacheKey] = c;
}
});
}
/**
* Calculate the sprite ID from a pokemon form.
*/
export function getSpriteId(pokemon: Pokemon, ignoreOverride?: boolean): string {
return pokemon
.getSpeciesForm(ignoreOverride)
.getSpriteId(
pokemon.getGender(ignoreOverride) === Gender.FEMALE,
pokemon.formIndex,
pokemon.shiny,
pokemon.variant,
);
}
export function getBattleSpriteId(pokemon: Pokemon, back?: boolean, ignoreOverride = false): string {
if (back === undefined) {
back = pokemon.isPlayer();
}
return pokemon
.getSpeciesForm(ignoreOverride)
.getSpriteId(
pokemon.getGender(ignoreOverride) === Gender.FEMALE,
pokemon.formIndex,
pokemon.shiny,
pokemon.variant,
back,
);
}
/** Compute the path to the sprite atlas by converting double underscores to path components (/)
*/
export function getSpriteAtlasPath(pokemon: Pokemon, ignoreOverride = false): string {
const spriteId = getSpriteId(pokemon, ignoreOverride).replace(DUNDER_REGEX, "/");
return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`;
}
/**
* Load the variant assets for the given sprite and stores them in {@linkcode variantColorCache}.
* @param spriteKey the key of the sprite to load
* @param fileRoot the root path of the sprite file
* @param variant the variant to load
* @param scene the scene to load the assets in (defaults to the global scene)
*/
export async function loadPokemonVariantAssets(
spriteKey: string,
fileRoot: string,
variant: Variant,
scene: BattleScene = globalScene,
) {
if (variantColorCache.hasOwnProperty(spriteKey)) {
return;
}
const useExpSprite = scene.experimentalSprites && hasExpSprite(spriteKey);
if (useExpSprite) {
fileRoot = `exp/${fileRoot}`;
}
let variantConfig = variantData;
fileRoot.split("/").map(p => (variantConfig ? (variantConfig = variantConfig[p]) : null));
const variantSet = variantConfig as VariantSet;
if (!variantConfig || variantSet[variant] !== 1) {
return;
}
variantColorCache[spriteKey] = await scene
.cachedFetch(`./images/pokemon/variant/${fileRoot}.json`)
.then(res => res.json());
}