pokerogue/src/data/mystery-encounters/utils/encounter-transformation-sequence.ts
Sirz Benjie 408b66f913
[Misc][Refactor][GitHub] Ditch eslint for biome, and add a formatter (#5495)
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2025-03-09 14:13:25 -07:00

471 lines
14 KiB
TypeScript

import type { PlayerPokemon } from "#app/field/pokemon";
import { getFrameMs } from "#app/utils";
import { cos, sin } from "#app/field/anims";
import { getTypeRgb } from "#app/data/type";
import { globalScene } from "#app/global-scene";
export enum TransformationScreenPosition {
CENTER,
LEFT,
RIGHT,
}
/**
* Initiates an "evolution-like" animation to transform a previousPokemon (presumably from the player's party) into a new one, not necessarily an evolution species.
* @param scene
* @param previousPokemon
* @param transformPokemon
* @param screenPosition
*/
export function doPokemonTransformationSequence(
previousPokemon: PlayerPokemon,
transformPokemon: PlayerPokemon,
screenPosition: TransformationScreenPosition,
) {
return new Promise<void>(resolve => {
const transformationContainer = globalScene.fieldUI.getByName("Dream Background") as Phaser.GameObjects.Container;
const transformationBaseBg = globalScene.add.image(0, 0, "default_bg");
transformationBaseBg.setOrigin(0, 0);
transformationBaseBg.setVisible(false);
transformationContainer.add(transformationBaseBg);
let pokemonSprite: Phaser.GameObjects.Sprite;
let pokemonTintSprite: Phaser.GameObjects.Sprite;
let pokemonEvoSprite: Phaser.GameObjects.Sprite;
let pokemonEvoTintSprite: Phaser.GameObjects.Sprite;
const xOffset =
screenPosition === TransformationScreenPosition.CENTER
? 0
: screenPosition === TransformationScreenPosition.RIGHT
? 100
: -100;
// Centered transformations occur at a lower y Position
const yOffset = screenPosition !== TransformationScreenPosition.CENTER ? -15 : 0;
const getPokemonSprite = () => {
const ret = globalScene.addPokemonSprite(
previousPokemon,
transformationBaseBg.displayWidth / 2 + xOffset,
transformationBaseBg.displayHeight / 2 + yOffset,
"pkmn__sub",
);
ret.setPipeline(globalScene.spritePipeline, {
tone: [0.0, 0.0, 0.0, 0.0],
ignoreTimeTint: true,
});
return ret;
};
transformationContainer.add((pokemonSprite = getPokemonSprite()));
transformationContainer.add((pokemonTintSprite = getPokemonSprite()));
transformationContainer.add((pokemonEvoSprite = getPokemonSprite()));
transformationContainer.add((pokemonEvoTintSprite = getPokemonSprite()));
pokemonSprite.setAlpha(0);
pokemonTintSprite.setAlpha(0);
pokemonTintSprite.setTintFill(0xffffff);
pokemonEvoSprite.setVisible(false);
pokemonEvoTintSprite.setVisible(false);
pokemonEvoTintSprite.setTintFill(0xffffff);
[pokemonSprite, pokemonTintSprite, pokemonEvoSprite, pokemonEvoTintSprite].map(sprite => {
const spriteKey = previousPokemon.getSpriteKey(true);
try {
sprite.play(spriteKey);
} catch (err: unknown) {
console.error(`Failed to play animation for ${spriteKey}`, err);
}
sprite.setPipeline(globalScene.spritePipeline, {
tone: [0.0, 0.0, 0.0, 0.0],
hasShadow: false,
teraColor: getTypeRgb(previousPokemon.getTeraType()),
isTerastallized: previousPokemon.isTerastallized,
});
sprite.setPipelineData("ignoreTimeTint", true);
sprite.setPipelineData("spriteKey", previousPokemon.getSpriteKey());
sprite.setPipelineData("shiny", previousPokemon.shiny);
sprite.setPipelineData("variant", previousPokemon.variant);
["spriteColors", "fusionSpriteColors"].map(k => {
if (previousPokemon.summonData?.speciesForm) {
k += "Base";
}
sprite.pipelineData[k] = previousPokemon.getSprite().pipelineData[k];
});
});
[pokemonEvoSprite, pokemonEvoTintSprite].map(sprite => {
const spriteKey = transformPokemon.getSpriteKey(true);
try {
sprite.play(spriteKey);
} catch (err: unknown) {
console.error(`Failed to play animation for ${spriteKey}`, err);
}
sprite.setPipelineData("ignoreTimeTint", true);
sprite.setPipelineData("spriteKey", transformPokemon.getSpriteKey());
sprite.setPipelineData("shiny", transformPokemon.shiny);
sprite.setPipelineData("variant", transformPokemon.variant);
["spriteColors", "fusionSpriteColors"].map(k => {
if (transformPokemon.summonData?.speciesForm) {
k += "Base";
}
sprite.pipelineData[k] = transformPokemon.getSprite().pipelineData[k];
});
});
globalScene.tweens.add({
targets: pokemonSprite,
alpha: 1,
ease: "Cubic.easeInOut",
duration: 2000,
onComplete: () => {
doSpiralUpward(transformationBaseBg, transformationContainer, xOffset, yOffset);
globalScene.tweens.addCounter({
from: 0,
to: 1,
duration: 1000,
onUpdate: t => {
pokemonTintSprite.setAlpha(t.getValue());
},
onComplete: () => {
pokemonSprite.setVisible(false);
globalScene.time.delayedCall(700, () => {
doArcDownward(transformationBaseBg, transformationContainer, xOffset, yOffset);
globalScene.time.delayedCall(1000, () => {
pokemonEvoTintSprite.setScale(0.25);
pokemonEvoTintSprite.setVisible(true);
doCycle(1.5, 6, pokemonTintSprite, pokemonEvoTintSprite).then(() => {
pokemonEvoSprite.setVisible(true);
doCircleInward(transformationBaseBg, transformationContainer, xOffset, yOffset);
globalScene.time.delayedCall(900, () => {
globalScene.tweens.add({
targets: pokemonEvoTintSprite,
alpha: 0,
duration: 1500,
delay: 150,
easing: "Sine.easeIn",
onComplete: () => {
globalScene.time.delayedCall(3000, () => {
resolve();
globalScene.tweens.add({
targets: pokemonEvoSprite,
alpha: 0,
duration: 2000,
delay: 150,
easing: "Sine.easeIn",
onComplete: () => {
previousPokemon.destroy();
transformPokemon.setVisible(false);
transformPokemon.setAlpha(1);
},
});
});
},
});
});
});
});
});
},
});
},
});
});
}
/**
* Animates particles that "spiral" upwards at start of transform animation
* @param scene
* @param transformationBaseBg
* @param transformationContainer
* @param xOffset
* @param yOffset
*/
function doSpiralUpward(
transformationBaseBg: Phaser.GameObjects.Image,
transformationContainer: Phaser.GameObjects.Container,
xOffset: number,
yOffset: number,
) {
let f = 0;
globalScene.tweens.addCounter({
repeat: 64,
duration: getFrameMs(1),
onRepeat: () => {
if (f < 64) {
if (!(f & 7)) {
for (let i = 0; i < 4; i++) {
doSpiralUpwardParticle(
(f & 120) * 2 + i * 64,
transformationBaseBg,
transformationContainer,
xOffset,
yOffset,
);
}
}
f++;
}
},
});
}
/**
* Animates particles that arc downwards after the upwards spiral
* @param scene
* @param transformationBaseBg
* @param transformationContainer
* @param xOffset
* @param yOffset
*/
function doArcDownward(
transformationBaseBg: Phaser.GameObjects.Image,
transformationContainer: Phaser.GameObjects.Container,
xOffset: number,
yOffset: number,
) {
let f = 0;
globalScene.tweens.addCounter({
repeat: 96,
duration: getFrameMs(1),
onRepeat: () => {
if (f < 96) {
if (f < 6) {
for (let i = 0; i < 9; i++) {
doArcDownParticle(i * 16, transformationBaseBg, transformationContainer, xOffset, yOffset);
}
}
f++;
}
},
});
}
/**
* Animates the transformation between the old pokemon form and new pokemon form
* @param scene
* @param l
* @param lastCycle
* @param pokemonTintSprite
* @param pokemonEvoTintSprite
*/
function doCycle(
l: number,
lastCycle: number,
pokemonTintSprite: Phaser.GameObjects.Sprite,
pokemonEvoTintSprite: Phaser.GameObjects.Sprite,
): Promise<boolean> {
return new Promise(resolve => {
const isLastCycle = l === lastCycle;
globalScene.tweens.add({
targets: pokemonTintSprite,
scale: 0.25,
ease: "Cubic.easeInOut",
duration: 500 / l,
yoyo: !isLastCycle,
});
globalScene.tweens.add({
targets: pokemonEvoTintSprite,
scale: 1,
ease: "Cubic.easeInOut",
duration: 500 / l,
yoyo: !isLastCycle,
onComplete: () => {
if (l < lastCycle) {
doCycle(l + 0.5, lastCycle, pokemonTintSprite, pokemonEvoTintSprite).then(success => resolve(success));
} else {
pokemonTintSprite.setVisible(false);
resolve(true);
}
},
});
});
}
/**
* Animates particles in a circle pattern
* @param scene
* @param transformationBaseBg
* @param transformationContainer
* @param xOffset
* @param yOffset
*/
function doCircleInward(
transformationBaseBg: Phaser.GameObjects.Image,
transformationContainer: Phaser.GameObjects.Container,
xOffset: number,
yOffset: number,
) {
let f = 0;
globalScene.tweens.addCounter({
repeat: 48,
duration: getFrameMs(1),
onRepeat: () => {
if (!f) {
for (let i = 0; i < 16; i++) {
doCircleInwardParticle(i * 16, 4, transformationBaseBg, transformationContainer, xOffset, yOffset);
}
} else if (f === 32) {
for (let i = 0; i < 16; i++) {
doCircleInwardParticle(i * 16, 8, transformationBaseBg, transformationContainer, xOffset, yOffset);
}
}
f++;
},
});
}
/**
* Helper function for {@linkcode doSpiralUpward}, handles a single particle
* @param scene
* @param trigIndex
* @param transformationBaseBg
* @param transformationContainer
* @param xOffset
* @param yOffset
*/
function doSpiralUpwardParticle(
trigIndex: number,
transformationBaseBg: Phaser.GameObjects.Image,
transformationContainer: Phaser.GameObjects.Container,
xOffset: number,
yOffset: number,
) {
const initialX = transformationBaseBg.displayWidth / 2 + xOffset;
const particle = globalScene.add.image(initialX, 0, "evo_sparkle");
transformationContainer.add(particle);
let f = 0;
let amp = 48;
const particleTimer = globalScene.tweens.addCounter({
repeat: -1,
duration: getFrameMs(1),
onRepeat: () => {
updateParticle();
},
});
const updateParticle = () => {
if (!f || particle.y > 8) {
particle.setPosition(initialX, 88 - (f * f) / 80 + yOffset);
particle.y += sin(trigIndex, amp) / 4;
particle.x += cos(trigIndex, amp);
particle.setScale(1 - f / 80);
trigIndex += 4;
if (f & 1) {
amp--;
}
f++;
} else {
particle.destroy();
particleTimer.remove();
}
};
updateParticle();
}
/**
* Helper function for {@linkcode doArcDownward}, handles a single particle
* @param scene
* @param trigIndex
* @param transformationBaseBg
* @param transformationContainer
* @param xOffset
* @param yOffset
*/
function doArcDownParticle(
trigIndex: number,
transformationBaseBg: Phaser.GameObjects.Image,
transformationContainer: Phaser.GameObjects.Container,
xOffset: number,
yOffset: number,
) {
const initialX = transformationBaseBg.displayWidth / 2 + xOffset;
const particle = globalScene.add.image(initialX, 0, "evo_sparkle");
particle.setScale(0.5);
transformationContainer.add(particle);
let f = 0;
let amp = 8;
const particleTimer = globalScene.tweens.addCounter({
repeat: -1,
duration: getFrameMs(1),
onRepeat: () => {
updateParticle();
},
});
const updateParticle = () => {
if (!f || particle.y < 88) {
particle.setPosition(initialX, 8 + (f * f) / 5 + yOffset);
particle.y += sin(trigIndex, amp) / 4;
particle.x += cos(trigIndex, amp);
amp = 8 + sin(f * 4, 40);
f++;
} else {
particle.destroy();
particleTimer.remove();
}
};
updateParticle();
}
/**
* Helper function for @{link doCircleInward}, handles a single particle
* @param scene
* @param trigIndex
* @param speed
* @param transformationBaseBg
* @param transformationContainer
* @param xOffset
* @param yOffset
*/
function doCircleInwardParticle(
trigIndex: number,
speed: number,
transformationBaseBg: Phaser.GameObjects.Image,
transformationContainer: Phaser.GameObjects.Container,
xOffset: number,
yOffset: number,
) {
const initialX = transformationBaseBg.displayWidth / 2 + xOffset;
const initialY = transformationBaseBg.displayHeight / 2 + yOffset;
const particle = globalScene.add.image(initialX, initialY, "evo_sparkle");
transformationContainer.add(particle);
let amp = 120;
const particleTimer = globalScene.tweens.addCounter({
repeat: -1,
duration: getFrameMs(1),
onRepeat: () => {
updateParticle();
},
});
const updateParticle = () => {
if (amp > 8) {
particle.setPosition(initialX, initialY);
particle.y += sin(trigIndex, amp);
particle.x += cos(trigIndex, amp);
amp -= speed;
trigIndex += 4;
} else {
particle.destroy();
particleTimer.remove();
}
};
updateParticle();
}