From 8ed40dd0c8748cfeaf5cf6f7cb4896f8a402cb30 Mon Sep 17 00:00:00 2001 From: Moka Date: Sat, 9 Nov 2024 00:11:01 +0100 Subject: [PATCH] [ME] Show shiny sparkles for Pokemon in ME intro --- .../encounters/berries-abound-encounter.ts | 4 +- .../encounters/fight-or-flight-encounter.ts | 4 +- .../the-pokemon-salesman-encounter.ts | 4 +- .../encounters/uncommon-breed-encounter.ts | 10 ++- src/field/mystery-encounter-intro.ts | 79 +++++++++++++++++-- src/phases/encounter-phase.ts | 4 + 6 files changed, 94 insertions(+), 11 deletions(-) diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index 5524511c67b..786ca3e8fc0 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -98,7 +98,9 @@ export const BerriesAboundEncounter: MysteryEncounter = tint: 0.25, x: -5, repeat: true, - isPokemon: true + isPokemon: true, + isShiny: bossPokemon.shiny, + variant: bossPokemon.variant } ]; diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index 3f9030dc3b2..3533e10df29 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -114,7 +114,9 @@ export const FightOrFlightEncounter: MysteryEncounter = tint: 0.25, x: -5, repeat: true, - isPokemon: true + isPokemon: true, + isShiny: bossPokemon.shiny, + variant: bossPokemon.variant }, ]; diff --git a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts index 02bf12e9c97..579861ffbc5 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -85,7 +85,9 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = fileRoot: fileRoot, hasShadow: true, repeat: true, - isPokemon: true + isPokemon: true, + isShiny: pokemon.shiny, + variant: pokemon.variant }); const starterTier = speciesStarterCosts[species.speciesId]; diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index a2c32c6af40..3a0dd68b02e 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -100,7 +100,9 @@ export const UncommonBreedEncounter: MysteryEncounter = hasShadow: true, x: -5, repeat: true, - isPokemon: true + isPokemon: true, + isShiny: pokemon.shiny, + variant: pokemon.variant }, ]; @@ -113,13 +115,17 @@ export const UncommonBreedEncounter: MysteryEncounter = const encounter = scene.currentBattle.mysteryEncounter!; const pokemonSprite = encounter.introVisuals!.getSprites(); - scene.tweens.add({ // Bounce at the end + // Bounce at the end, then shiny sparkle if the Pokemon is shiny + scene.tweens.add({ targets: pokemonSprite, duration: 300, ease: "Cubic.easeOut", yoyo: true, y: "-=20", loop: 1, + onComplete: () => { + encounter.introVisuals?.playShinySparkles(); + } }); scene.time.delayedCall(500, () => scene.playSound("battle_anims/PRSFX- Spotlight2")); diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index 12bcace500c..952d95a8ca8 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -5,6 +5,7 @@ import { Species } from "#enums/species"; import { isNullOrUndefined } from "#app/utils"; import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import PlayAnimationConfig = Phaser.Types.Animations.PlayAnimationConfig; +import { Variant, variantColorCache, variantData, VariantSet } from "#app/data/variant"; type KnownFileRoot = | "arenas" @@ -59,6 +60,9 @@ export class MysteryEncounterSpriteConfig { scale?: number; /** If you are using a Pokemon sprite, set to `true`. This will ensure variant, form, gender, shiny sprites are loaded properly */ isPokemon?: boolean; + //TODO + isShiny?: boolean; + variant?: Variant; /** If you are using an item sprite, set to `true` */ isItem?: boolean; /** The sprites alpha. `0` - `1` The lower the number, the more transparent */ @@ -74,9 +78,12 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con public encounter: MysteryEncounter; public spriteConfigs: MysteryEncounterSpriteConfig[]; public enterFromRight: boolean; + private shinySparkleSprites: { sprite: Phaser.GameObjects.Sprite, variant: Variant }[]; constructor(scene: BattleScene, encounter: MysteryEncounter) { super(scene, -72, 76); + this.shinySparkleSprites = []; + this.encounter = encounter; this.enterFromRight = encounter.enterIntroVisualsFromRight ?? false; // Shallow copy configs to allow visual config updates at runtime without dirtying master copy of Encounter @@ -86,7 +93,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con }; if (!isNullOrUndefined(result.species)) { - const keys = getSpriteKeysFromSpecies(result.species); + const keys = getSpriteKeysFromSpecies(result.species, undefined, undefined, result.isShiny, result.variant); result.spriteKey = keys.spriteKey; result.fileRoot = keys.fileRoot; result.isPokemon = true; @@ -120,18 +127,35 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con // Sprites with custom X or Y defined will not count for normal spacing requirements const spacingValue = Math.round((maxX - minX) / Math.max(this.spriteConfigs.filter(s => !s.x && !s.y).length, 1)); + const shinySparkleSprites = scene.add.container(0, 0); this.spriteConfigs?.forEach((config) => { - const { spriteKey, isItem, hasShadow, scale, x, y, yShadow, alpha } = config; + const { spriteKey, isItem, hasShadow, scale, x, y, yShadow, alpha, isPokemon, isShiny, variant } = config; let sprite: GameObjects.Sprite; let tintSprite: GameObjects.Sprite; + let pokemonShinySparkle: Phaser.GameObjects.Sprite | undefined; - if (!isItem) { - sprite = getSprite(spriteKey, hasShadow, yShadow); - tintSprite = getSprite(spriteKey); - } else { + if (isItem) { sprite = getItemSprite(spriteKey, hasShadow, yShadow); tintSprite = getItemSprite(spriteKey); + } else { + sprite = getSprite(spriteKey, hasShadow, yShadow); + tintSprite = getSprite(spriteKey); + if (isPokemon && isShiny) { + // Set Pipeline for shiny variant + sprite.setPipelineData("spriteKey", spriteKey); + tintSprite.setPipelineData("spriteKey", spriteKey); + sprite.setPipelineData("shiny", true); + sprite.setPipelineData("variant", variant); + tintSprite.setPipelineData("shiny", true); + tintSprite.setPipelineData("variant", variant); + // Create Sprite for shiny Sparkle + pokemonShinySparkle = scene.add.sprite(sprite.x, sprite.y, "shiny"); + pokemonShinySparkle.setOrigin(0.5, 1); + pokemonShinySparkle.setVisible(false); + this.shinySparkleSprites.push({ sprite: pokemonShinySparkle, variant: variant ?? 0 }); + shinySparkleSprites.add(pokemonShinySparkle); + } } sprite.setVisible(!config.hidden); @@ -165,6 +189,11 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con } } + if (!isNullOrUndefined(pokemonShinySparkle)) { + // Offset the sparkle to match the Pokemon's position + pokemonShinySparkle.setPosition(sprite.x, sprite.y); + } + if (!isNullOrUndefined(alpha)) { sprite.setAlpha(alpha); tintSprite.setAlpha(alpha); @@ -173,6 +202,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con this.add(sprite); this.add(tintSprite); }); + this.add(shinySparkleSprites); } /** @@ -187,6 +217,31 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con this.spriteConfigs.forEach((config) => { if (config.isPokemon) { this.scene.loadPokemonAtlas(config.spriteKey, config.fileRoot); + if (config.isShiny) { + // Load variant assets + const useExpSprite = this.scene.experimentalSprites && this.scene.hasExpSprite(config.spriteKey); + if (useExpSprite) { + config.fileRoot = `exp/${config.fileRoot}`; + } + let variantConfig = variantData; + config.fileRoot.split("/").map(p => variantConfig ? variantConfig = variantConfig[p] : null); + const variantSet = variantConfig as VariantSet; + if (variantSet && (config.variant !== undefined && variantSet[config.variant] === 1)) { + const populateVariantColors = (key: string): Promise => { + return new Promise(resolve => { + if (variantColorCache.hasOwnProperty(key)) { + return resolve(); + } + this.scene.cachedFetch(`./images/pokemon/variant/${config.fileRoot}.json`).then(res => res.json()).then(c => { + variantColorCache[key] = c; + resolve(); + }); + }); + }; + populateVariantColors(config.spriteKey); + } + // TODO load shiny sparkle? + } } else if (config.isItem) { this.scene.loadAtlas("items", ""); } else { @@ -288,6 +343,18 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con return true; } + /** + * Play shiny sparkle animations if there are shiny Pokemon + */ + playShinySparkles() { + for (const sparkleConfig of this.shinySparkleSprites) { + this.scene.time.delayedCall(500, () => { + sparkleConfig.sprite.play(`sparkle${sparkleConfig.variant ? `_${sparkleConfig.variant + 1}` : ""}`); + this.scene.playSound("se/sparkle"); + }); + } + } + /** * For sprites with animation and that do not have animation disabled, will begin frame animation */ diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index 123f9ded9fc..64a9641316a 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -204,6 +204,7 @@ export class EncounterPhase extends BattlePhase { battle.seenEnemyPartyMemberIds.add(enemyPokemon.id); const playerPokemon = this.scene.getPlayerPokemon(); if (playerPokemon?.visible) { + //TODO HERE this.scene.field.moveBelow(enemyPokemon as Pokemon, playerPokemon); } enemyPokemon.tint(0, 0.5); @@ -383,6 +384,9 @@ export class EncounterPhase extends BattlePhase { if (encounter.onVisualsStart) { encounter.onVisualsStart(this.scene); + } else if (encounter.spriteConfigs && introVisuals) { + // If the encounter doesn't have any special visual intro, show sparkle for shiny Pokemon + introVisuals.playShinySparkles(); } const doEncounter = () => {