diff --git a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts index aa71ee35c08..ec3cc64dd3a 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -26,6 +26,7 @@ import { trainerNamePools } from "#app/data/trainer-names"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { addPokemonDataToDexAndValidateAchievements } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import type { PokeballType } from "#enums/pokeball"; +import { doShinySparkleAnim } from "#app/field/anims"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/globalTradeSystem"; @@ -846,8 +847,7 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke onComplete: () => { if (receivedPokemon.shiny) { scene.time.delayedCall(500, () => { - pokemonShinySparkle.play(`sparkle${receivedPokemon.variant ? `_${receivedPokemon.variant + 1}` : ""}`); - scene.playSound("se/sparkle"); + doShinySparkleAnim(scene, pokemonShinySparkle, receivedPokemon.variant); }); } receivedPokeballSprite.destroy(); diff --git a/src/field/anims.ts b/src/field/anims.ts index dddf38e4a7e..10198c29005 100644 --- a/src/field/anims.ts +++ b/src/field/anims.ts @@ -1,6 +1,7 @@ -import BattleScene from "../battle-scene"; +import BattleScene from "#app/battle-scene"; import { PokeballType } from "#enums/pokeball"; -import * as Utils from "../utils"; +import { Variant } from "#app/data/variant"; +import { getFrameMs, randGauss } from "#app/utils"; export function addPokeballOpenParticles(scene: BattleScene, x: number, y: number, pokeballType: PokeballType): void { switch (pokeballType) { @@ -127,7 +128,7 @@ function doFanOutParticle(scene: BattleScene, trigIndex: integer, x: integer, y: const particleTimer = scene.tweens.addCounter({ repeat: -1, - duration: Utils.getFrameMs(1), + duration: getFrameMs(1), onRepeat: () => { updateParticle(); } @@ -159,7 +160,7 @@ export function addPokeballCaptureStars(scene: BattleScene, pokeball: Phaser.Gam } }); - const dist = Utils.randGauss(25); + const dist = randGauss(25); scene.tweens.add({ targets: particle, x: pokeball.x + dist, @@ -185,3 +186,31 @@ export function sin(index: integer, amplitude: integer): number { export function cos(index: integer, amplitude: integer): number { return amplitude * Math.cos(index * (Math.PI / 128)); } + +/** + * Play the shiny sparkle animation and sound effect for the given sprite + * First ensures that the animation has been properly initialized + * @param sparkleSprite the Sprite to play the animation on + * @param variant which shiny {@linkcode variant} to play the animation for + */ +export function doShinySparkleAnim(scene: BattleScene, sparkleSprite: Phaser.GameObjects.Sprite, variant: Variant) { + const keySuffix = variant ? `_${variant + 1}` : ""; + const spriteKey = `shiny${keySuffix}`; + const animationKey = `sparkle${keySuffix}`; + + // Make sure the animation exists, and create it if not + if (!scene.anims.exists(animationKey)) { + const frameNames = scene.anims.generateFrameNames(spriteKey, { suffix: ".png", end: 34 }); + scene.anims.create({ + key: `sparkle${keySuffix}`, + frames: frameNames, + frameRate: 32, + showOnStart: true, + hideOnComplete: true, + }); + } + + // Play the animation + sparkleSprite.play(animationKey); + scene.playSound("se/sparkle"); +} diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index 952d95a8ca8..4c68a296265 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -1,11 +1,12 @@ import { GameObjects } from "phaser"; -import BattleScene from "../battle-scene"; -import MysteryEncounter from "../data/mystery-encounters/mystery-encounter"; +import BattleScene from "#app/battle-scene"; +import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; 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"; +import { doShinySparkleAnim } from "#app/field/anims"; type KnownFileRoot = | "arenas" @@ -294,12 +295,18 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con this.getSprites().map((sprite, i) => { if (!this.spriteConfigs[i].isItem) { - sprite.setTexture(this.spriteConfigs[i].spriteKey).setFrame(0); + sprite.setTexture(this.spriteConfigs[i].spriteKey); + // Show the first frame for a smooth transition when the animation starts. + const firstFrame = sprite.texture.frames["0001.png"]; + sprite.setFrame(firstFrame ?? 0); } }); this.getTintSprites().map((tintSprite, i) => { if (!this.spriteConfigs[i].isItem) { - tintSprite.setTexture(this.spriteConfigs[i].spriteKey).setFrame(0); + tintSprite.setTexture(this.spriteConfigs[i].spriteKey); + // Show the first frame for a smooth transition when the animation starts. + const firstFrame = tintSprite.texture.frames["0001.png"]; + tintSprite.setFrame(firstFrame ?? 0); } }); @@ -349,8 +356,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con 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"); + doShinySparkleAnim(this.scene, sparkleConfig.sprite, sparkleConfig.variant); }); } } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index c9d7033e02c..d1f98395c5b 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -69,6 +69,7 @@ import { SpeciesFormKey } from "#enums/species-form-key"; import { BASE_HIDDEN_ABILITY_CHANCE, BASE_SHINY_CHANCE, SHINY_EPIC_CHANCE, SHINY_VARIANT_CHANCE } from "#app/data/balance/rates"; import { Nature } from "#enums/nature"; import { StatusEffect } from "#enums/status-effect"; +import { doShinySparkleAnim } from "#app/field/anims"; export enum FieldPosition { CENTER, @@ -668,22 +669,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } initShinySparkle(): void { - // TODO - const keySuffix = this.variant ? `_${this.variant + 1}` : ""; - const key = `shiny${keySuffix}`; - const shinySparkle = this.scene.addFieldSprite(0, 0, key); + const shinySparkle = this.scene.addFieldSprite(0, 0, "shiny"); shinySparkle.setVisible(false); shinySparkle.setOrigin(0.5, 1); - const frameNames = this.scene.anims.generateFrameNames(key, { suffix: ".png", end: 34 }); - if (!(this.scene.anims.exists(`sparkle${keySuffix}`))) { - this.scene.anims.create({ - key: `sparkle${keySuffix}`, - frames: frameNames, - frameRate: 32, - showOnStart: true, - hideOnComplete: true, - }); - } this.add(shinySparkle); this.shinySparkle = shinySparkle; @@ -3746,8 +3734,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { sparkle(): void { if (this.shinySparkle) { - this.shinySparkle.play(`sparkle${this.variant ? `_${this.variant + 1}` : ""}`); - this.scene.playSound("se/sparkle"); + doShinySparkleAnim(this.scene, this.shinySparkle, this.variant); } } diff --git a/src/phases/egg-hatch-phase.ts b/src/phases/egg-hatch-phase.ts index 734744ef23a..d45c580228c 100644 --- a/src/phases/egg-hatch-phase.ts +++ b/src/phases/egg-hatch-phase.ts @@ -14,6 +14,7 @@ import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import * as Utils from "#app/utils"; import { EggLapsePhase } from "./egg-lapse-phase"; import { EggHatchData } from "#app/data/egg-hatch-data"; +import { doShinySparkleAnim } from "#app/field/anims"; /** @@ -341,9 +342,7 @@ export class EggHatchPhase extends Phase { this.pokemon.cry(); if (isShiny) { this.scene.time.delayedCall(Utils.fixedInt(500), () => { - // TODO - this.pokemonShinySparkle.play(`sparkle${this.pokemon.variant ? `_${this.pokemon.variant + 1}` : ""}`); - this.scene.playSound("se/sparkle"); + doShinySparkleAnim(this.scene, this.pokemonShinySparkle, this.pokemon.variant); }); } this.scene.time.delayedCall(Utils.fixedInt(!this.skipped ? !isShiny ? 1250 : 1750 : !isShiny ? 250 : 750), () => {