clean up battle animation logic

This commit is contained in:
ImperialSympathizer 2024-07-17 22:58:34 -04:00
parent 09a3167bac
commit 06684e06a2
8 changed files with 151 additions and 800 deletions

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ import {Constructor, isNullOrUndefined} from "#app/utils";
import * as Utils from "./utils"; import * as Utils from "./utils";
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier"; import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier";
import { PokeballType } from "./data/pokeball"; import { PokeballType } from "./data/pokeball";
import { initCommonAnims, initEncounterAnims, initMoveAnim, loadCommonAnimAssets, loadEncounterAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims"; import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims";
import { Phase } from "./phase"; import { Phase } from "./phase";
import { initGameSpeed } from "./system/game-speed"; import { initGameSpeed } from "./system/game-speed";
import { Arena, ArenaBase } from "./field/arena"; import { Arena, ArenaBase } from "./field/arena";
@ -555,7 +555,6 @@ export default class BattleScene extends SceneBase {
Promise.all([ Promise.all([
Promise.all(loadPokemonAssets), Promise.all(loadPokemonAssets),
initCommonAnims(this).then(() => loadCommonAnimAssets(this, true)), initCommonAnims(this).then(() => loadCommonAnimAssets(this, true)),
initEncounterAnims(this).then(() => loadEncounterAnimAssets(this, true)),
Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(this, m))).then(() => loadMoveAnimAssets(this, defaultMoves, true)), Promise.all([ Moves.TACKLE, Moves.TAIL_WHIP, Moves.FOCUS_ENERGY, Moves.STRUGGLE ].map(m => initMoveAnim(this, m))).then(() => loadMoveAnimAssets(this, defaultMoves, true)),
this.initStarterColors() this.initStarterColors()
]).then(() => { ]).then(() => {

View File

@ -6,6 +6,7 @@ import * as Utils from "../utils";
import { BattlerIndex } from "../battle"; import { BattlerIndex } from "../battle";
import { Element } from "json-stable-stringify"; import { Element } from "json-stable-stringify";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { isNullOrUndefined } from "../utils";
//import fs from 'vite-plugin-fs/browser'; //import fs from 'vite-plugin-fs/browser';
export enum AnimFrameTarget { export enum AnimFrameTarget {
@ -307,7 +308,7 @@ abstract class AnimTimedEvent {
this.resourceName = resourceName; this.resourceName = resourceName;
} }
abstract execute(scene: BattleScene, battleAnim: BattleAnim): integer; abstract execute(scene: BattleScene, battleAnim: BattleAnim, priority?: number): integer;
abstract getEventType(): string; abstract getEventType(): string;
} }
@ -325,7 +326,7 @@ class AnimTimedSoundEvent extends AnimTimedEvent {
} }
} }
execute(scene: BattleScene, battleAnim: BattleAnim): integer { execute(scene: BattleScene, battleAnim: BattleAnim, priority?: number): integer {
const soundConfig = { rate: (this.pitch * 0.01), volume: (this.volume * 0.01) }; const soundConfig = { rate: (this.pitch * 0.01), volume: (this.volume * 0.01) };
if (this.resourceName) { if (this.resourceName) {
try { try {
@ -387,7 +388,7 @@ class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
super(frameIndex, resourceName, source); super(frameIndex, resourceName, source);
} }
execute(scene: BattleScene, moveAnim: MoveAnim): integer { execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer {
const tweenProps = {}; const tweenProps = {};
if (this.bgX !== undefined) { if (this.bgX !== undefined) {
tweenProps["x"] = (this.bgX * 0.5) - 320; tweenProps["x"] = (this.bgX * 0.5) - 320;
@ -417,7 +418,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
super(frameIndex, resourceName, source); super(frameIndex, resourceName, source);
} }
execute(scene: BattleScene, moveAnim: MoveAnim): integer { execute(scene: BattleScene, moveAnim: MoveAnim, priority?: number): integer {
if (moveAnim.bgSprite) { if (moveAnim.bgSprite) {
moveAnim.bgSprite.destroy(); moveAnim.bgSprite.destroy();
} }
@ -429,7 +430,9 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
moveAnim.bgSprite.setAlpha(this.opacity / 255); moveAnim.bgSprite.setAlpha(this.opacity / 255);
scene.field.add(moveAnim.bgSprite); scene.field.add(moveAnim.bgSprite);
const fieldPokemon = scene.getEnemyPokemon() || scene.getPlayerPokemon(); const fieldPokemon = scene.getEnemyPokemon() || scene.getPlayerPokemon();
if (fieldPokemon?.isOnField()) { if (!isNullOrUndefined(priority)) {
scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority);
} else if (fieldPokemon?.isOnField()) {
scene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon); scene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon);
} }
@ -517,14 +520,18 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
}); });
} }
export function initEncounterAnims(scene: BattleScene): Promise<void> { export function initEncounterAnims(scene: BattleScene, anims: EncounterAnim | EncounterAnim[]): Promise<void> {
anims = anims instanceof Array ? anims : [anims];
return new Promise(resolve => { return new Promise(resolve => {
const encounterAnimNames = Utils.getEnumKeys(EncounterAnim); const encounterAnimNames = Utils.getEnumKeys(EncounterAnim);
const encounterAnimIds = Utils.getEnumValues(EncounterAnim); const encounterAnimIds = Utils.getEnumValues(EncounterAnim);
const encounterAnimFetches = []; const encounterAnimFetches = [];
for (let ea = 0; ea < encounterAnimIds.length; ea++) { for (const anim of anims) {
const encounterAnimId = encounterAnimIds[ea]; if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) {
encounterAnimFetches.push(scene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[ea].toLowerCase().replace(/\_/g, "-")}.json`) continue;
}
const encounterAnimId = encounterAnimIds[anim];
encounterAnimFetches.push(scene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`)
.then(response => response.json()) .then(response => response.json())
.then(cas => encounterAnims.set(encounterAnimId, new AnimConfig(cas)))); .then(cas => encounterAnims.set(encounterAnimId, new AnimConfig(cas))));
} }
@ -1005,18 +1012,13 @@ export abstract class BattleAnim {
}); });
} }
private getGraphicFrameDataWithoutTarget(scene: BattleScene, frames: AnimFrame[], targetInitialX: number, targetInitialY: number): Map<integer, Map<AnimFrameTarget, GraphicFrameData>> { private getGraphicFrameDataWithoutTarget(frames: AnimFrame[], targetInitialX: number, targetInitialY: number): Map<integer, Map<AnimFrameTarget, GraphicFrameData>> {
const ret: Map<integer, Map<AnimFrameTarget, GraphicFrameData>> = new Map([ const ret: Map<integer, Map<AnimFrameTarget, GraphicFrameData>> = new Map([
[AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ], [AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ],
[AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ], [AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ],
[AnimFrameTarget.TARGET, new Map<AnimFrameTarget, GraphicFrameData>() ] [AnimFrameTarget.TARGET, new Map<AnimFrameTarget, GraphicFrameData>() ]
]); ]);
const userInitialX = 0;
const userInitialY = 0;
const userHalfHeight = 30;
const targetHalfHeight = 30;
let g = 0; let g = 0;
let u = 0; let u = 0;
let t = 0; let t = 0;
@ -1024,27 +1026,10 @@ export abstract class BattleAnim {
for (const frame of frames) { for (const frame of frames) {
let x = frame.x; let x = frame.x;
let y = frame.y; let y = frame.y;
let scaleX = (frame.zoomX / 100) * (!frame.mirror ? 1 : -1); const scaleX = (frame.zoomX / 100) * (!frame.mirror ? 1 : -1);
const scaleY = (frame.zoomY / 100); const scaleY = (frame.zoomY / 100);
switch (frame.focus) { x += targetInitialX;
case AnimFocus.TARGET: y += targetInitialY;
x += targetInitialX - targetFocusX;
y += (targetInitialY - targetHalfHeight) - targetFocusY;
break;
case AnimFocus.USER:
x += userInitialX - userFocusX;
y += (userInitialY - userHalfHeight) - userFocusY;
break;
case AnimFocus.USER_TARGET:
const point = transformPoint(this.srcLine[0], this.srcLine[1], this.srcLine[2], this.srcLine[3],
this.dstLine[0], this.dstLine[1] - userHalfHeight, this.dstLine[2], this.dstLine[3] - targetHalfHeight, x, y);
x = point[0];
y = point[1];
if (frame.target === AnimFrameTarget.GRAPHIC && isReversed(this.srcLine[0], this.srcLine[2], this.dstLine[0], this.dstLine[2])) {
scaleX = scaleX * -1;
}
break;
}
const angle = -frame.angle; const angle = -frame.angle;
const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++; const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++;
ret.get(frame.target).set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle }); ret.get(frame.target).set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle });
@ -1053,7 +1038,20 @@ export abstract class BattleAnim {
return ret; return ret;
} }
playWithoutTargets(scene: BattleScene, targetInitialX: number, targetInitialY: number, frameTimeMult: number, callback?: Function) { /**
*
* @param scene
* @param targetInitialX
* @param targetInitialY
* @param frameTimeMult
* @param frameTimedEventPriority
* - 0 is behind all other sprites (except BG)
* - 1 on top of player field
* - 3 is on top of both fields
* - 5 is on top of player sprite
* @param callback
*/
playWithoutTargets(scene: BattleScene, targetInitialX: number, targetInitialY: number, frameTimeMult: number, frameTimedEventPriority?: 0 | 1 | 3 | 5, callback?: Function) {
const spriteCache: SpriteCache = { const spriteCache: SpriteCache = {
[AnimFrameTarget.GRAPHIC]: [], [AnimFrameTarget.GRAPHIC]: [],
[AnimFrameTarget.USER]: [], [AnimFrameTarget.USER]: [],
@ -1087,12 +1085,17 @@ export abstract class BattleAnim {
let r = anim.frames.length; let r = anim.frames.length;
let f = 0; let f = 0;
const fieldSprites = scene.field.getAll();
const playerFieldSprite = fieldSprites[1];
const enemyFieldSprite = fieldSprites[3];
const trainerSprite = fieldSprites[5];
scene.tweens.addCounter({ scene.tweens.addCounter({
duration: Utils.getFrameMs(3) * frameTimeMult, duration: Utils.getFrameMs(3) * frameTimeMult,
repeat: anim.frames.length, repeat: anim.frames.length,
onRepeat: () => { onRepeat: () => {
const spriteFrames = anim.frames[f]; const spriteFrames = anim.frames[f];
const frameData = this.getGraphicFrameDataWithoutTarget(scene, anim.frames[f], targetInitialX, targetInitialY); const frameData = this.getGraphicFrameDataWithoutTarget(anim.frames[f], targetInitialX, targetInitialY);
const u = 0; const u = 0;
const t = 0; const t = 0;
let g = 0; let g = 0;
@ -1118,11 +1121,27 @@ export abstract class BattleAnim {
if (priority < 0) { if (priority < 0) {
// Move to top of scene // Move to top of scene
scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); scene.field.moveTo(moveSprite, scene.field.getAll().length - 1);
} else if (priority < scene.field.getAll().length) { } else if (priority === 1) {
// Indexes of field: // Move above player field
// 0 is scene background if (playerFieldSprite) {
// 1 is enemy field scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, playerFieldSprite);
scene.field.moveTo(moveSprite, priority); } else {
setSpritePriority(-1);
}
} else if (priority === 3) {
// Move above player enemy field
if (enemyFieldSprite) {
scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, enemyFieldSprite);
} else {
setSpritePriority(-1);
}
} else if (priority === 5) {
// Move above player trainer sprite
if (trainerSprite) {
scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, trainerSprite);
} else {
setSpritePriority(-1);
}
} else { } else {
setSpritePriority(-1); setSpritePriority(-1);
} }
@ -1143,7 +1162,7 @@ export abstract class BattleAnim {
} }
if (anim.frameTimedEvents.has(f)) { if (anim.frameTimedEvents.has(f)) {
for (const event of anim.frameTimedEvents.get(f)) { for (const event of anim.frameTimedEvents.get(f)) {
r = Math.max((anim.frames.length - f) + event.execute(scene, this), r); r = Math.max((anim.frames.length - f) + event.execute(scene, this, frameTimedEventPriority), r);
} }
} }
const targets = Utils.getEnumValues(AnimFrameTarget); const targets = Utils.getEnumValues(AnimFrameTarget);

View File

@ -1,5 +1,5 @@
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, initCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { modifierTypes, } from "#app/modifier/modifier-type"; import { modifierTypes, } from "#app/modifier/modifier-type";
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "../../../battle-scene"; import BattleScene from "../../../battle-scene";
@ -12,7 +12,7 @@ import { Type } from "#app/data/type";
import { BattlerIndex } from "#app/battle"; import { BattlerIndex } from "#app/battle";
import { PokemonMove } from "#app/field/pokemon"; import { PokemonMove } from "#app/field/pokemon";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { EncounterAnim, EncounterBattleAnim, initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; import { EncounterAnim, EncounterBattleAnim } from "#app/data/battle-anims";
import { WeatherType } from "#app/data/weather"; import { WeatherType } from "#app/data/weather";
import { randSeedInt } from "#app/utils"; import { randSeedInt } from "#app/utils";
@ -27,6 +27,7 @@ export const FieryFalloutEncounter: IMysteryEncounter =
.withSceneWaveRangeRequirement(40, 180) // waves 10 to 180 .withSceneWaveRangeRequirement(40, 180) // waves 10 to 180
.withCatchAllowed(true) .withCatchAllowed(true)
.withIntroSpriteConfigs([]) // Set in onInit() .withIntroSpriteConfigs([]) // Set in onInit()
.withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT)
.withIntroDialogue([ .withIntroDialogue([
{ {
text: `${namespace}_intro_message`, text: `${namespace}_intro_message`,
@ -60,21 +61,20 @@ export const FieryFalloutEncounter: IMysteryEncounter =
scene.arena.trySetWeather(WeatherType.SUNNY, true); scene.arena.trySetWeather(WeatherType.SUNNY, true);
// Load animations/sfx for Volcarona moves // Load animations/sfx for Volcarona moves
Promise.all([initMoveAnim(scene, Moves.QUIVER_DANCE), initMoveAnim(scene, Moves.FIRE_SPIN)]) initCustomMovesForEncounter(scene, [Moves.FIRE_SPIN, Moves.QUIVER_DANCE]);
.then(() => loadMoveAnimAssets(scene, [Moves.QUIVER_DANCE, Moves.FIRE_SPIN]));
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart((scene: BattleScene) => {
// Play animations // Play animations
const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, scene.getPlayerPokemon(), scene.getPlayerPokemon()); const background = new EncounterBattleAnim(EncounterAnim.MAGMA_BG, scene.getPlayerPokemon(), scene.getPlayerPokemon());
background.playWithoutTargets(scene, 200, 70, 2); background.playWithoutTargets(scene, 200, 70, 2, 3);
const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, scene.getPlayerPokemon(), scene.getPlayerPokemon()); const animation = new EncounterBattleAnim(EncounterAnim.MAGMA_SPOUT, scene.getPlayerPokemon(), scene.getPlayerPokemon());
animation.playWithoutTargets(scene, 200, 70, 2); animation.playWithoutTargets(scene, 100, 100, 2);
const increment = 600; const increment = 600;
for (let i = 3; i < 6; i++) { for (let i = 3; i < 6; i++) {
scene.time.delayedCall((increment) * (i - 2), () => { scene.time.delayedCall((increment) * (i - 2), () => {
animation.playWithoutTargets(scene, 100 + randSeedInt(12) * 20, 110 - randSeedInt(10) * 15, 2); animation.playWithoutTargets(scene, randSeedInt(12) * 15, 150 - randSeedInt(10) * 15, 2);
}); });
} }

View File

@ -19,6 +19,7 @@ import {
WaveRangeRequirement WaveRangeRequirement
} from "./mystery-encounter-requirements"; } from "./mystery-encounter-requirements";
import { BattlerIndex } from "#app/battle"; import { BattlerIndex } from "#app/battle";
import { EncounterAnim } from "#app/data/battle-anims";
export enum MysteryEncounterVariant { export enum MysteryEncounterVariant {
DEFAULT, DEFAULT,
@ -57,6 +58,7 @@ export default interface IMysteryEncounter {
* Optional params * Optional params
*/ */
encounterTier?: MysteryEncounterTier; encounterTier?: MysteryEncounterTier;
encounterAnimations?: EncounterAnim[];
hideBattleIntroMessage?: boolean; hideBattleIntroMessage?: boolean;
hideIntroVisuals?: boolean; hideIntroVisuals?: boolean;
catchAllowed?: boolean; catchAllowed?: boolean;
@ -193,13 +195,6 @@ export default class IMysteryEncounter implements IMysteryEncounter {
const secReqs = this.meetsSecondaryRequirementAndSecondaryPokemonSelected(scene); // secondary is checked first to handle cases of primary overlapping with secondary const secReqs = this.meetsSecondaryRequirementAndSecondaryPokemonSelected(scene); // secondary is checked first to handle cases of primary overlapping with secondary
const priReqs = this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene); const priReqs = this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene);
// console.log("-------" + MysteryEncounterType[this.encounterType] + " Encounter Check -------");
// console.log(this);
// console.log( "sceneCheck: " + sceneReq);
// console.log( "primaryCheck: " + priReqs);
// console.log( "secondaryCheck: " + secReqs);
// console.log(MysteryEncounterTier[this.encounterTier]);
return sceneReq && secReqs && priReqs; return sceneReq && secReqs && priReqs;
} }
@ -377,6 +372,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
dialogue?: MysteryEncounterDialogue; dialogue?: MysteryEncounterDialogue;
encounterTier?: MysteryEncounterTier; encounterTier?: MysteryEncounterTier;
encounterAnimations?: EncounterAnim[];
requirements?: EncounterSceneRequirement[] = []; requirements?: EncounterSceneRequirement[] = [];
primaryPokemonRequirements?: EncounterPokemonRequirement[] = []; primaryPokemonRequirements?: EncounterPokemonRequirement[] = [];
secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = []; secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = [];
@ -471,6 +467,16 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
return Object.assign(this, { encounterTier: encounterTier }); return Object.assign(this, { encounterTier: encounterTier });
} }
/**
* Defines any EncounterAnim animations that are intended to be used during the encounter
* @param encounterAnimations
* @returns
*/
withAnimations(...encounterAnimations: EncounterAnim[]): this & Required<Pick<IMysteryEncounter, "encounterAnimations">> {
encounterAnimations = encounterAnimations instanceof Array ? encounterAnimations : [encounterAnimations];
return Object.assign(this, { encounterAnimations: encounterAnimations });
}
/** /**
* Sets the maximum number of times that an encounter can spawn in a given Classic run * Sets the maximum number of times that an encounter can spawn in a given Classic run
* @param maxAllowedEncounters * @param maxAllowedEncounters

View File

@ -26,6 +26,8 @@ import * as Overrides from "#app/overrides";
import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { Gender } from "#app/data/gender"; import { Gender } from "#app/data/gender";
import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
import { Moves } from "#enums/moves";
export class EnemyPokemonConfig { export class EnemyPokemonConfig {
species: PokemonSpecies; species: PokemonSpecies;
@ -230,6 +232,20 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
} }
} }
/**
* Load special move animations/sfx for hard-coded encounter-specific moves that a pokemon uses at the start of an encounter
* See: [startOfBattleEffects](IMysteryEncounter.startOfBattleEffects) for more details
*
* This promise does not need to be awaited on if called in an encounter onInit (will just load lazily)
* @param scene
* @param moves
*/
export function initCustomMovesForEncounter(scene: BattleScene, moves: Moves | Moves[]) {
moves = moves instanceof Array ? moves : [moves];
return Promise.all(moves.map(move => initMoveAnim(scene, move)))
.then(() => loadMoveAnimAssets(scene, moves));
}
/** /**
* Will update player money, and animate change (sound optional) * Will update player money, and animate change (sound optional)
* @param scene - Battle Scene * @param scene - Battle Scene

View File

@ -9,7 +9,7 @@ import { Stat } from "./data/pokemon-stat";
import { BerryModifier, BypassSpeedChanceModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, IvScannerModifier, LapsingPersistentModifier, LapsingPokemonHeldItemModifier, MapModifier, Modifier, MoneyInterestModifier, MoneyMultiplierModifier, MultipleParticipantExpBonusModifier, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier, TurnStatusEffectModifier } from "./modifier/modifier"; import { BerryModifier, BypassSpeedChanceModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, IvScannerModifier, LapsingPersistentModifier, LapsingPokemonHeldItemModifier, MapModifier, Modifier, MoneyInterestModifier, MoneyMultiplierModifier, MultipleParticipantExpBonusModifier, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier, TurnStatusEffectModifier } from "./modifier/modifier";
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler"; import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball"; import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball";
import { CommonAnim, CommonBattleAnim, initMoveAnim, loadMoveAnimAssets, MoveAnim } from "./data/battle-anims"; import { CommonAnim, CommonBattleAnim, initEncounterAnims, initMoveAnim, loadEncounterAnimAssets, loadMoveAnimAssets, MoveAnim } from "./data/battle-anims";
import { getStatusEffectActivationText, getStatusEffectCatchRateMultiplier, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText, StatusEffect } from "./data/status-effect"; import { getStatusEffectActivationText, getStatusEffectCatchRateMultiplier, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText, StatusEffect } from "./data/status-effect";
import { SummaryUiMode } from "./ui/summary-ui-handler"; import { SummaryUiMode } from "./ui/summary-ui-handler";
import EvolutionSceneHandler from "./ui/evolution-scene-handler"; import EvolutionSceneHandler from "./ui/evolution-scene-handler";
@ -832,6 +832,11 @@ export class EncounterPhase extends BattlePhase {
mysteryEncounter.populateDialogueTokensFromRequirements(this.scene); mysteryEncounter.populateDialogueTokensFromRequirements(this.scene);
}, this.scene.currentBattle.waveIndex); }, this.scene.currentBattle.waveIndex);
// Add any special encounter animations to load
if (mysteryEncounter.encounterAnimations && mysteryEncounter.encounterAnimations.length > 0) {
loadEnemyAssets.push(initEncounterAnims(this.scene, mysteryEncounter.encounterAnimations).then(() => loadEncounterAnimAssets(this.scene, true)));
}
// Add intro visuals for mystery encounter // Add intro visuals for mystery encounter
mysteryEncounter.initIntroVisuals(this.scene); mysteryEncounter.initIntroVisuals(this.scene);
this.scene.field.add(mysteryEncounter.introVisuals); this.scene.field.add(mysteryEncounter.introVisuals);

View File

@ -89,8 +89,8 @@ describe("Mystery Encounter Phases", () => {
expect(dialogueSpy).toHaveBeenCalledTimes(1); expect(dialogueSpy).toHaveBeenCalledTimes(1);
expect(messageSpy).toHaveBeenCalledTimes(2); expect(messageSpy).toHaveBeenCalledTimes(2);
expect(dialogueSpy).toHaveBeenCalledWith("What's this?", "???", null, expect.any(Function)); expect(dialogueSpy).toHaveBeenCalledWith("What's this?", "???", null, expect.any(Function));
expect(messageSpy).toHaveBeenCalledWith("Mysterious challengers have appeared!", null, expect.any(Function), 750, true); expect(messageSpy).toHaveBeenCalledWith("Mysterious challengers have appeared!", null, expect.any(Function), 300, true);
expect(messageSpy).toHaveBeenCalledWith("The trainer steps forward...", null, expect.any(Function), 750, true); expect(messageSpy).toHaveBeenCalledWith("The trainer steps forward...", null, expect.any(Function), 300, true);
}); });
}); });