pokerogue/src/data/battle-anims.ts

974 lines
38 KiB
TypeScript
Raw Normal View History

2023-04-04 01:47:41 +01:00
//import { battleAnimRawData } from "./battle-anim-raw-data";
2023-04-20 20:46:05 +01:00
import BattleScene from "../battle-scene";
2023-04-13 17:16:36 +01:00
import { ChargeAttr, Moves, allMoves } from "./move";
2023-04-20 20:46:05 +01:00
import Pokemon from "../pokemon";
import * as Utils from "../utils";
2023-04-13 17:16:36 +01:00
//import fs from 'vite-plugin-fs/browser';
2023-04-04 01:47:41 +01:00
export enum AnimFrameTarget {
USER,
TARGET,
GRAPHIC
}
enum AnimFocus {
USER = 1,
TARGET,
USER_TARGET,
SCREEN
}
2023-04-14 04:04:51 +01:00
enum AnimBlendType {
NORMAL,
ADD,
SUBTRACT
}
2023-04-11 18:00:04 +01:00
export enum ChargeAnim {
FLY_CHARGING = 1000,
BOUNCE_CHARGING,
DIG_CHARGING,
DIVE_CHARGING,
SOLAR_BEAM_CHARGING,
SHADOW_FORCE_CHARGING,
SKULL_BASH_CHARGING,
FREEZE_SHOCK_CHARGING,
SKY_DROP_CHARGING,
SKY_ATTACK_CHARGING,
ICE_BURN_CHARGING,
DOOM_DESIRE_CHARGING,
RAZOR_WIND_CHARGING
}
export enum CommonAnim {
2023-04-20 20:46:05 +01:00
USE_ITEM = 2000,
HEALTH_UP,
POISON = 2010,
2023-04-11 18:00:04 +01:00
TOXIC,
PARALYSIS,
2023-04-12 00:08:03 +01:00
SLEEP,
2023-04-11 18:00:04 +01:00
FROZEN,
2023-04-12 00:08:03 +01:00
BURN,
2023-04-11 18:00:04 +01:00
CONFUSION,
ATTRACT,
BIND,
WRAP,
CURSE_NO_GHOST,
LEECH_SEED,
FIRE_SPIN,
PROTECT,
COVET,
WHIRLPOOL,
BIDE,
SAND_TOMB,
QUICK_GUARD,
WIDE_GUARD,
CURSE,
MAGMA_STORM,
CLAMP,
SUNNY = 2100,
RAIN,
SANDSTORM,
HAIL,
WIND,
HEAVY_RAIN,
HARSH_SUN,
STRONG_WINDS,
MISTY_TERRAIN = 2110,
ELECTRIC_TERRAIN,
GRASSY_TERRAIN,
PSYCHIC_TERRAIN
}
2023-04-20 20:46:05 +01:00
export class AnimConfig {
2023-04-04 01:47:41 +01:00
public id: integer;
public graphic: string;
public frames: AnimFrame[][];
public frameTimedEvents: Map<integer, AnimTimedEvent[]>;
public position: integer;
public hue: integer;
constructor(source?: any) {
this.frameTimedEvents = new Map<integer, AnimTimedEvent[]>;
if (source) {
this.id = source.id;
this.graphic = source.graphic;
this.frames = source.frames;
const frameTimedEvents = source.frameTimedEvents;
for (let fte of Object.keys(frameTimedEvents)) {
const timedEvents: AnimTimedEvent[] = [];
for (let te of frameTimedEvents[fte]) {
let timedEvent: AnimTimedEvent;
switch (te.eventType) {
case 'AnimTimedSoundEvent':
timedEvent = new AnimTimedSoundEvent(te.frameIndex, te.resourceName, te);
break;
case 'AnimTimedAddBgEvent':
timedEvent = new AnimTimedAddBgEvent(te.frameIndex, te.resourceName, te);
break;
case 'AnimTimedUpdateBgEvent':
timedEvent = new AnimTimedUpdateBgEvent(te.frameIndex, te.resourceName, te);
break;
}
timedEvents.push(timedEvent);
}
this.frameTimedEvents.set(parseInt(fte), timedEvents);
}
this.position = source.position;
this.hue = source.hue;
} else
this.frames = [];
}
getSoundResourceNames(): string[] {
const sounds = new Set<string>();
for (let ftes of this.frameTimedEvents.values()) {
for (let fte of ftes) {
if (fte instanceof AnimTimedSoundEvent && fte.resourceName)
sounds.add(fte.resourceName);
}
}
return Array.from(sounds.values());
}
getBackgroundResourceNames(): string[] {
const backgrounds = new Set<string>();
for (let ftes of this.frameTimedEvents.values()) {
for (let fte of ftes) {
if (fte instanceof AnimTimedAddBgEvent && fte.resourceName)
backgrounds.add(fte.resourceName);
}
}
return Array.from(backgrounds.values());
}
}
class AnimFrame {
public x: number;
public y: number;
public zoomX: number;
public zoomY: number;
public angle: number;
public mirror: boolean;
public visible: boolean;
2023-04-14 04:04:51 +01:00
public blendType: AnimBlendType;
2023-04-04 01:47:41 +01:00
public target: AnimFrameTarget;
public graphicFrame: integer;
public opacity: integer;
public color: integer[];
public tone: integer[];
public flash: integer[];
public locked: boolean;
public priority: integer;
public focus: AnimFocus;
2023-04-14 04:04:51 +01:00
constructor(x: number, y: number, zoomX: number, zoomY: number, angle: number, mirror: boolean, visible: boolean, blendType: AnimBlendType, pattern: integer,
2023-04-04 01:47:41 +01:00
opacity: integer, colorR: integer, colorG: integer, colorB: integer, colorA: integer, toneR: integer, toneG: integer, toneB: integer, toneA: integer,
flashR: integer, flashG: integer, flashB: integer, flashA: integer, locked: boolean, priority: integer, focus: AnimFocus) {
this.x = x;
this.y = y;
this.zoomX = zoomX;
this.zoomY = zoomY;
this.angle = angle;
this.mirror = mirror;
this.visible = visible;
this.blendType = blendType;
let target = AnimFrameTarget.GRAPHIC;
switch (pattern) {
case -2:
target = AnimFrameTarget.TARGET;
this.x -= 384;
this.y -= 96;
break;
case -1:
target = AnimFrameTarget.USER;
this.x -= 128;
this.y -= 224;
break;
default:
this.x = (this.x - 128) * 0.5;
this.y = (this.y - 224) * 0.5;
}
this.target = target;
this.graphicFrame = pattern >= 0 ? pattern : 0;
this.opacity = opacity;
this.color = [ colorR, colorG, colorB, colorA ];
this.tone = [ toneR, toneG, toneB, toneA ];
this.flash = [ flashR, flashG, flashB, flashA ];
this.locked = locked;
this.priority = priority;
this.focus = focus;
}
}
abstract class AnimTimedEvent {
public frameIndex: integer;
public resourceName: string;
constructor(frameIndex: integer, resourceName: string) {
this.frameIndex = frameIndex;
this.resourceName = resourceName;
}
2023-04-12 00:08:03 +01:00
abstract execute(scene: BattleScene, battleAnim: BattleAnim): integer;
2023-04-04 01:47:41 +01:00
abstract getEventType(): string;
}
class AnimTimedSoundEvent extends AnimTimedEvent {
public volume: number;
public pitch: number;
constructor(frameIndex: integer, resourceName: string, source?: any) {
2023-04-20 20:46:05 +01:00
super(frameIndex, resourceName + (resourceName && resourceName.indexOf('.') === -1 ? resourceName.startsWith('PRSFX-') ? '.wav' : '.ogg' : ''));
2023-04-04 01:47:41 +01:00
if (source) {
this.volume = source.volume;
this.pitch = source.pitch;
} else
this.pitch = 100;
}
2023-04-12 00:08:03 +01:00
execute(scene: BattleScene, battleAnim: BattleAnim): integer {
2023-04-04 01:47:41 +01:00
const soundConfig = { rate: (this.pitch * 0.01), volume: (this.volume * 0.01) };
2023-04-12 00:08:03 +01:00
if (this.resourceName) {
2023-04-14 04:04:51 +01:00
try {
scene.sound.play(this.resourceName, soundConfig);
} catch (err) {
console.error(err);
}
2023-04-12 00:08:03 +01:00
return Math.ceil((scene.sound.get(this.resourceName).totalDuration * 1000) / 33.33);
} else
return Math.ceil(battleAnim.user.cry(soundConfig) / 33.33);
2023-04-04 01:47:41 +01:00
}
getEventType(): string {
return 'AnimTimedSoundEvent';
}
}
abstract class AnimTimedBgEvent extends AnimTimedEvent {
public bgX: number;
public bgY: number;
public opacity: integer;
public colorRed: integer;
public colorGreen: integer;
public colorBlue: integer;
public colorAlpha: integer;
public duration: integer;
public flashScope: integer;
public flashRed: integer;
public flashGreen: integer;
public flashBlue: integer;
public flashAlpha: integer;
public flashDuration: integer;
constructor(frameIndex: integer, resourceName: string, source: any) {
super(frameIndex, resourceName);
if (source) {
this.bgX = source.bgX;
this.bgY = source.bgY;
this.opacity = source.opacity;
this.colorRed = source.colorRed;
this.colorGreen = source.colorGreen;
this.colorBlue = source.colorBlue;
this.colorAlpha = source.colorAlpha;
this.duration = source.duration;
this.flashScope = source.flashScope;
this.flashRed = source.flashRed;
this.flashGreen = source.flashGreen;
this.flashBlue = source.flashBlue;
this.flashAlpha = source.flashAlpha;
this.flashDuration = source.flashDuration;
}
}
}
class AnimTimedUpdateBgEvent extends AnimTimedBgEvent {
constructor(frameIndex: integer, resourceName: string, source?: any) {
super(frameIndex, resourceName, source);
}
2023-04-12 00:08:03 +01:00
execute(scene: BattleScene, moveAnim: MoveAnim): integer {
2023-04-04 01:47:41 +01:00
const tweenProps = {};
if (this.bgX !== undefined)
2023-04-18 17:30:47 +01:00
tweenProps['x'] = (this.bgX * 0.5) - 320;
2023-04-04 01:47:41 +01:00
if (this.bgY !== undefined)
tweenProps['y'] = (this.bgY * 0.5) - 284;
if (this.opacity !== undefined)
tweenProps['alpha'] = this.opacity / 255;
if (Object.keys(tweenProps).length) {
scene.tweens.add(Object.assign({
targets: moveAnim.bgSprite,
2023-04-14 04:04:51 +01:00
duration: this.duration * 3,
2023-04-04 01:47:41 +01:00
useFrames: true
}, tweenProps))
}
2023-04-12 00:08:03 +01:00
return this.duration * 2;
2023-04-04 01:47:41 +01:00
}
getEventType(): string {
return 'AnimTimedUpdateBgEvent';
}
}
class AnimTimedAddBgEvent extends AnimTimedBgEvent {
constructor(frameIndex: integer, resourceName: string, source?: any) {
super(frameIndex, resourceName, source);
}
2023-04-12 00:08:03 +01:00
execute(scene: BattleScene, moveAnim: MoveAnim): integer {
2023-04-14 04:04:51 +01:00
if (moveAnim.bgSprite)
moveAnim.bgSprite.destroy();
2023-04-18 17:30:47 +01:00
moveAnim.bgSprite = scene.add.tileSprite(this.bgX - 320, this.bgY - 284, 896, 576, this.resourceName);
2023-04-04 01:47:41 +01:00
moveAnim.bgSprite.setOrigin(0, 0);
moveAnim.bgSprite.setScale(1.25);
moveAnim.bgSprite.setAlpha(0);
scene.field.add(moveAnim.bgSprite);
2023-04-21 03:26:38 +01:00
scene.field.moveBelow(moveAnim.bgSprite, scene.getEnemyPokemon() || scene.getPlayerPokemon());
2023-04-04 01:47:41 +01:00
scene.tweens.add({
targets: moveAnim.bgSprite,
alpha: 1,
2023-04-14 04:04:51 +01:00
duration: this.duration * 3,
2023-04-04 01:47:41 +01:00
useFrames: true
});
2023-04-12 00:08:03 +01:00
return this.duration * 2;
2023-04-04 01:47:41 +01:00
}
getEventType(): string {
return 'AnimTimedAddBgEvent';
}
}
2023-04-20 20:46:05 +01:00
export const moveAnims = new Map<Moves, AnimConfig | [AnimConfig, AnimConfig]>();
export const chargeAnims = new Map<ChargeAnim, AnimConfig | [AnimConfig, AnimConfig]>();
export const commonAnims = new Map<CommonAnim, AnimConfig>();
2023-04-04 01:47:41 +01:00
2023-04-12 00:08:03 +01:00
export function initCommonAnims(): Promise<void> {
return new Promise(resolve => {
const commonAnimNames = Utils.getEnumKeys(CommonAnim);
const commonAnimIds = Utils.getEnumValues(CommonAnim);
const commonAnimFetches = [];
for (let ca = 0; ca < commonAnimIds.length; ca++) {
const commonAnimId = commonAnimIds[ca];
commonAnimFetches.push(fetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/\_/g, '-')}.json`)
.then(response => response.json())
2023-04-20 20:46:05 +01:00
.then(cas => commonAnims.set(commonAnimId, new AnimConfig(cas))));
2023-04-12 00:08:03 +01:00
}
Promise.allSettled(commonAnimFetches).then(() => resolve());
});
}
export function initMoveAnim(move: Moves): Promise<void> {
2023-04-04 01:47:41 +01:00
return new Promise(resolve => {
if (moveAnims.has(move)) {
if (moveAnims.get(move) !== null)
resolve();
else {
let loadedCheckTimer = setInterval(() => {
if (moveAnims.get(move) !== null) {
clearInterval(loadedCheckTimer);
resolve();
}
}, 50);
}
} else {
moveAnims.set(move, null);
fetch(`./battle-anims/${Moves[move].toLowerCase().replace(/\_/g, '-')}.json`)
.then(response => response.json())
.then(ba => {
if (Array.isArray(ba)) {
populateMoveAnim(move, ba[0]);
populateMoveAnim(move, ba[1]);
} else
populateMoveAnim(move, ba);
2023-04-21 02:32:48 +01:00
const chargeAttr = allMoves[move].getAttrs(ChargeAttr) as ChargeAttr[];
2023-04-13 17:16:36 +01:00
if (chargeAttr.length)
initMoveChargeAnim(chargeAttr[0].chargeAnim).then(() => resolve());
else
resolve();
});
}
});
}
export function initMoveChargeAnim(chargeAnim: ChargeAnim): Promise<void> {
return new Promise(resolve => {
if (chargeAnims.has(chargeAnim)) {
if (chargeAnims.get(chargeAnim) !== null)
resolve();
else {
let loadedCheckTimer = setInterval(() => {
if (chargeAnims.get(chargeAnim) !== null) {
clearInterval(loadedCheckTimer);
resolve();
}
}, 50);
}
} else {
chargeAnims.set(chargeAnim, null);
fetch(`./battle-anims/${ChargeAnim[chargeAnim].toLowerCase().replace(/\_/g, '-')}.json`)
.then(response => response.json())
.then(ca => {
if (Array.isArray(ca)) {
populateMoveChargeAnim(chargeAnim, ca[0]);
populateMoveChargeAnim(chargeAnim, ca[1]);
} else
populateMoveChargeAnim(chargeAnim, ca);
2023-04-04 01:47:41 +01:00
resolve();
});
}
});
}
2023-04-20 20:46:05 +01:00
function populateMoveAnim(move: Moves, animSource: any): void {
const moveAnim = new AnimConfig(animSource);
2023-04-04 01:47:41 +01:00
if (moveAnims.get(move) === null) {
moveAnims.set(move, moveAnim);
return;
}
2023-04-20 20:46:05 +01:00
moveAnims.set(move, [ moveAnims.get(move) as AnimConfig, moveAnim ]);
2023-04-04 01:47:41 +01:00
}
2023-04-13 17:16:36 +01:00
function populateMoveChargeAnim(chargeAnim: ChargeAnim, animSource: any) {
2023-04-20 20:46:05 +01:00
const moveChargeAnim = new AnimConfig(animSource);
2023-04-13 17:16:36 +01:00
if (chargeAnims.get(chargeAnim) === null) {
chargeAnims.set(chargeAnim, moveChargeAnim);
return;
}
2023-04-20 20:46:05 +01:00
chargeAnims.set(chargeAnim, [ chargeAnims.get(chargeAnim) as AnimConfig, moveChargeAnim ]);
2023-04-13 17:16:36 +01:00
}
2023-04-12 00:08:03 +01:00
export function loadCommonAnimAssets(scene: BattleScene, startLoad?: boolean): Promise<void> {
return new Promise(resolve => {
loadAnimAssets(scene, Array.from(commonAnims.values()), startLoad).then(() => resolve());
});
}
2023-04-04 01:47:41 +01:00
export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLoad?: boolean): Promise<void> {
return new Promise(resolve => {
2023-04-20 20:46:05 +01:00
const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat();
2023-04-13 17:16:36 +01:00
for (let moveId of moveIds) {
2023-04-21 02:32:48 +01:00
const chargeAttr = allMoves[moveId].getAttrs(ChargeAttr) as ChargeAttr[];
2023-04-13 17:16:36 +01:00
if (chargeAttr.length) {
const moveChargeAnims = chargeAnims.get(chargeAttr[0].chargeAnim);
2023-04-20 20:46:05 +01:00
moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims[0]);
2023-04-14 04:04:51 +01:00
if (Array.isArray(moveChargeAnims))
moveAnimations.push(moveChargeAnims[1]);
2023-04-13 17:16:36 +01:00
}
}
2023-04-12 00:08:03 +01:00
loadAnimAssets(scene, moveAnimations, startLoad).then(() => resolve());
});
}
2023-04-20 20:46:05 +01:00
function loadAnimAssets(scene: BattleScene, anims: AnimConfig[], startLoad?: boolean): Promise<void> {
2023-04-12 00:08:03 +01:00
return new Promise(resolve => {
2023-04-04 01:47:41 +01:00
const backgrounds = new Set<string>();
const sounds = new Set<string>();
2023-04-12 00:08:03 +01:00
for (let a of anims) {
const animSounds = a.getSoundResourceNames();
for (let ms of animSounds)
sounds.add(ms);
const animBackgrounds = a.getBackgroundResourceNames();
for (let abg of animBackgrounds)
backgrounds.add(abg);
if (a.graphic)
scene.loadSpritesheet(a.graphic, 'battle_anims', 96);
2023-04-04 01:47:41 +01:00
}
for (let bg of backgrounds)
scene.loadImage(bg, 'battle_anims');
for (let s of sounds)
scene.loadSe(s, 'battle_anims', s);
2023-04-13 00:09:15 +01:00
if (startLoad) {
scene.load.once(Phaser.Loader.Events.COMPLETE, () => resolve());
if (!scene.load.isLoading())
scene.load.start();
} else
resolve();
2023-04-04 01:47:41 +01:00
});
}
2023-04-14 04:04:51 +01:00
interface GraphicFrameData {
x: number,
y: number,
angle: number
}
2023-04-12 00:08:03 +01:00
export abstract class BattleAnim {
2023-04-04 01:47:41 +01:00
public user: Pokemon;
public target: Pokemon;
2023-04-12 00:08:03 +01:00
public sprites: Phaser.GameObjects.Sprite[];
2023-04-04 01:47:41 +01:00
public bgSprite: Phaser.GameObjects.TileSprite;
2023-04-12 00:08:03 +01:00
constructor(user: Pokemon, target: Pokemon) {
2023-04-04 01:47:41 +01:00
this.user = user;
this.target = target;
2023-04-12 00:08:03 +01:00
this.sprites = [];
2023-04-04 01:47:41 +01:00
}
2023-04-20 20:46:05 +01:00
abstract getAnim(): AnimConfig;
2023-04-12 00:08:03 +01:00
abstract isOppAnim(): boolean;
abstract isReverseCoords(): boolean;
2023-04-14 04:04:51 +01:00
getGraphicScale(): number {
return 1;
}
private getGraphicFrameData(scene: BattleScene, frames: AnimFrame[]): Map<integer, GraphicFrameData> {
const ret = new Map<integer, GraphicFrameData>();
const isOppAnim = this.isOppAnim();
const user = !isOppAnim ? this.user : this.target;
const target = !isOppAnim ? this.target : this.user;
const isReverseCoords = this.isReverseCoords();
const graphicScale = this.getGraphicScale();
const userInitialX = user.x;
const userInitialY = user.y;
const userHalfHeight = user.getSprite().displayHeight / 2;
const targetInitialX = target.x;
const targetInitialY = target.y;
const targetHalfHeight = target.getSprite().displayHeight / 2;
let g = 0;
for (let frame of frames) {
if (frame.target !== AnimFrameTarget.GRAPHIC)
continue;
const xProgress = frame.focus !== AnimFocus.SCREEN ? Math.min(Math.max(frame.x, 0) / 128, 1) : 0;
const initialX = targetInitialX;
const initialY = targetInitialY;
let xOffset = (!isReverseCoords ? (userInitialX - targetInitialX) : (targetInitialX - userInitialX));
let yOffset = (!isReverseCoords ? (userInitialY - targetInitialY) : (targetInitialY - userInitialY));
const ySpriteOffset = ((userHalfHeight * (1 - xProgress)) + (targetHalfHeight * xProgress)) * -1;
if (graphicScale > 1) {
xOffset -= ((scene.game.canvas.width / 6) * (graphicScale - 1)) / 2;
yOffset -= ((scene.game.canvas.height / 6) * (graphicScale - 1)) / 2;
}
const x = initialX + xOffset * (!isReverseCoords ? 1 : -1) + (frame.x * graphicScale) * (!isReverseCoords ? 1 : -1);
const y = ((initialY + yOffset * (!isReverseCoords || frame.focus === AnimFocus.USER || frame.focus === AnimFocus.SCREEN ? 1 : -1)
+ (frame.y * graphicScale) * (!isReverseCoords || (frame.focus !== AnimFocus.USER_TARGET) ? 1 : -1) + ySpriteOffset));
const angle = -frame.angle * (!isReverseCoords ? 1 : -1);
ret.set(g++, { x: x, y: y, angle: angle });
}
return ret;
}
2023-04-04 01:47:41 +01:00
play(scene: BattleScene, callback?: Function) {
2023-04-13 17:16:36 +01:00
const isOppAnim = this.isOppAnim();
const user = !isOppAnim ? this.user : this.target;
const target = !isOppAnim ? this.target : this.user;
2023-04-12 00:08:03 +01:00
const anim = this.getAnim();
2023-04-04 01:47:41 +01:00
2023-04-13 17:16:36 +01:00
const userInitialX = user.x;
const userInitialY = user.y;
const targetInitialX = target.x;
const targetInitialY = target.y;
2023-04-04 01:47:41 +01:00
2023-04-14 04:04:51 +01:00
const isReverseCoords = this.isReverseCoords();
2023-04-12 00:08:03 +01:00
let r = anim.frames.length;
2023-04-04 01:47:41 +01:00
let f = 0;
2023-04-12 00:08:03 +01:00
const sprites: Phaser.GameObjects.Sprite[] = [];
2023-04-14 04:04:51 +01:00
const spritePriorities: integer[] = [];
2023-04-04 01:47:41 +01:00
scene.tweens.addCounter({
useFrames: true,
2023-04-14 04:04:51 +01:00
duration: 3,
2023-04-04 01:47:41 +01:00
repeat: anim.frames.length,
onRepeat: () => {
const spriteFrames = anim.frames[f];
2023-04-14 04:04:51 +01:00
const frameData = this.getGraphicFrameData(scene, anim.frames[f]);
2023-04-04 01:47:41 +01:00
let g = 0;
for (let frame of spriteFrames) {
switch (frame.target) {
case AnimFrameTarget.USER:
2023-04-14 04:04:51 +01:00
user.setPosition(userInitialX + frame.x / (!isReverseCoords ? 2 : -2), userInitialY + frame.y / (!isOppAnim ? 2 : -2));
2023-04-04 01:47:41 +01:00
break;
case AnimFrameTarget.TARGET:
2023-04-14 04:04:51 +01:00
target.setPosition(targetInitialX + frame.x / (!isReverseCoords ? 2 : -2), targetInitialY + frame.y / (!isOppAnim ? 2 : -2));
2023-04-04 01:47:41 +01:00
break;
case AnimFrameTarget.GRAPHIC:
2023-04-12 00:08:03 +01:00
if (g === sprites.length) {
2023-04-04 01:47:41 +01:00
const newSprite = scene.add.sprite(0, 0, anim.graphic, 1);
2023-04-12 00:08:03 +01:00
sprites.push(newSprite);
2023-04-14 04:04:51 +01:00
scene.field.add(newSprite);
spritePriorities.push(1);
}
const graphicIndex = g++;
const moveSprite = sprites[graphicIndex];
if (spritePriorities[graphicIndex] !== frame.priority) {
spritePriorities[graphicIndex] = frame.priority;
const setSpritePriority = (priority: integer) => {
switch (priority) {
case 0:
2023-04-23 03:14:53 +01:00
scene.field.moveBelow(moveSprite, scene.getEnemyPokemon() || scene.getPlayerPokemon());
2023-04-14 04:04:51 +01:00
break;
case 1:
scene.field.moveTo(moveSprite, scene.field.getAll().length - 1);
break;
case 2:
switch (frame.focus) {
case AnimFocus.USER:
if (this.bgSprite)
scene.field.moveAbove(moveSprite, this.bgSprite);
else
scene.field.moveBelow(moveSprite, this.user);
break;
case AnimFocus.TARGET:
scene.field.moveBelow(moveSprite, this.target);
break;
default:
setSpritePriority(1);
break;
}
break;
case 3:
switch (frame.focus) {
case AnimFocus.USER:
scene.field.moveAbove(moveSprite, this.user);
break;
case AnimFocus.TARGET:
scene.field.moveAbove(moveSprite, this.target);
break;
default:
setSpritePriority(1);
break;
}
break;
default:
setSpritePriority(1);
}
};
setSpritePriority(frame.priority);
2023-04-04 01:47:41 +01:00
}
moveSprite.setFrame(frame.graphicFrame);
2023-04-14 04:04:51 +01:00
//console.log(AnimFocus[frame.focus]);
const graphicScale = this.getGraphicScale();
moveSprite.setPosition(frameData.get(graphicIndex).x, frameData.get(graphicIndex).y);
moveSprite.setAngle(frameData.get(graphicIndex).angle);
const scaleX = graphicScale * (isReverseCoords === frame.mirror ? 1 : -1);
const scaleY = graphicScale;
moveSprite.setScale(scaleX, scaleY);
moveSprite.setAlpha(frame.opacity / 255);
moveSprite.setBlendMode(frame.blendType === AnimBlendType.NORMAL ? Phaser.BlendModes.NORMAL : frame.blendType === AnimBlendType.ADD ? Phaser.BlendModes.ADD : Phaser.BlendModes.DIFFERENCE);
2023-04-04 01:47:41 +01:00
break;
}
if (frame.target !== AnimFrameTarget.GRAPHIC) {
2023-04-13 17:16:36 +01:00
const pokemon = frame.target === AnimFrameTarget.USER ? user : target;
2023-04-14 04:04:51 +01:00
pokemon.setScale(!frame.mirror ? 1 : -1)
pokemon.setAlpha(frame.opacity / 255);
pokemon.setAngle(-frame.angle * (!isReverseCoords ? 1 : -1));
2023-04-04 01:47:41 +01:00
const zoomScaleX = frame.zoomX / 100;
const zoomScaleY = frame.zoomY / 100;
const zoomSprite = pokemon.getZoomSprite();
zoomSprite.setY(zoomSprite.displayHeight * (zoomScaleY - 1) * 0.5);
zoomSprite.setScale(zoomScaleX, zoomScaleY);
}
}
if (anim.frameTimedEvents.has(f)) {
for (let event of anim.frameTimedEvents.get(f))
2023-04-12 00:08:03 +01:00
r = Math.max((anim.frames.length - f) + event.execute(scene, this), r);
2023-04-04 01:47:41 +01:00
}
2023-04-12 00:08:03 +01:00
if (g < sprites.length) {
const removedSprites = sprites.splice(g, sprites.length - g);
2023-04-14 04:04:51 +01:00
spritePriorities.splice(g, sprites.length - g);
2023-04-04 01:47:41 +01:00
for (let rs of removedSprites)
rs.destroy();
}
f++;
2023-04-12 00:08:03 +01:00
r--;
2023-04-04 01:47:41 +01:00
},
onComplete: () => {
2023-04-13 17:16:36 +01:00
const cleanUpAndComplete = () => {
user.setPosition(userInitialX, userInitialY);
2023-04-14 04:04:51 +01:00
user.setScale(1);
2023-04-13 17:16:36 +01:00
user.setAlpha(1);
user.setAngle(0);
target.setPosition(targetInitialX, targetInitialY);
2023-04-14 04:04:51 +01:00
target.setScale(1);
2023-04-13 17:16:36 +01:00
target.setAlpha(1);
target.setAngle(0);
2023-04-14 04:04:51 +01:00
if (this.bgSprite)
this.bgSprite.destroy();
2023-04-13 17:16:36 +01:00
if (callback)
callback();
};
2023-04-14 04:04:51 +01:00
for (let ms of sprites) {
if (ms)
ms.destroy();
}
2023-04-13 17:16:36 +01:00
if (r) {
2023-04-12 00:08:03 +01:00
scene.tweens.addCounter({
duration: r,
useFrames: true,
2023-04-13 17:16:36 +01:00
onComplete: () => cleanUpAndComplete()
2023-04-12 00:08:03 +01:00
});
2023-04-13 17:16:36 +01:00
} else
cleanUpAndComplete();
2023-04-04 01:47:41 +01:00
}
});
}
}
2023-04-12 00:08:03 +01:00
export class CommonBattleAnim extends BattleAnim {
public commonAnim: CommonAnim;
constructor(commonAnim: CommonAnim, user: Pokemon, target?: Pokemon) {
super(user, target || user);
this.commonAnim = commonAnim;
}
2023-04-20 20:46:05 +01:00
getAnim(): AnimConfig {
2023-04-12 00:08:03 +01:00
return commonAnims.get(this.commonAnim);
}
isOppAnim(): boolean {
return false;
}
isReverseCoords(): boolean {
return false;
}
}
export class MoveAnim extends BattleAnim {
public move: Moves;
constructor(move: Moves, user: Pokemon, target: Pokemon) {
super(user, target);
this.move = move;
}
2023-04-20 20:46:05 +01:00
getAnim(): AnimConfig {
return moveAnims.get(this.move) instanceof AnimConfig
? moveAnims.get(this.move) as AnimConfig
: moveAnims.get(this.move)[this.user.isPlayer() ? 0 : 1] as AnimConfig;
2023-04-12 00:08:03 +01:00
}
isOppAnim(): boolean {
2023-04-18 17:30:47 +01:00
const ret = !this.user.isPlayer() && Array.isArray(moveAnims.get(this.move));
switch (this.move) {
case Moves.TELEPORT:
return !ret;
}
return ret;
2023-04-12 00:08:03 +01:00
}
isReverseCoords(): boolean {
2023-04-18 17:30:47 +01:00
return !this.user.isPlayer() === !this.isOppAnim();
2023-04-12 00:08:03 +01:00
}
2023-04-14 04:04:51 +01:00
getGraphicScale(): number {
switch (this.move) {
case Moves.FISSURE:
return 1.25;
}
return 1;
}
2023-04-12 00:08:03 +01:00
}
2023-04-13 17:16:36 +01:00
export class MoveChargeAnim extends MoveAnim {
private chargeAnim: ChargeAnim;
constructor(chargeAnim: ChargeAnim, move: Moves, user: Pokemon, target: Pokemon) {
super(move, user, target);
this.chargeAnim = chargeAnim;
}
2023-04-20 20:46:05 +01:00
getAnim(): AnimConfig {
return chargeAnims.get(this.chargeAnim) instanceof AnimConfig
? chargeAnims.get(this.chargeAnim) as AnimConfig
: chargeAnims.get(this.chargeAnim)[this.user.isPlayer() ? 0 : 1] as AnimConfig;
2023-04-13 17:16:36 +01:00
}
}
2023-04-04 01:47:41 +01:00
export function populateAnims() {
return;
2023-04-11 18:00:04 +01:00
const commonAnimNames = Utils.getEnumKeys(CommonAnim).map(k => k.toLowerCase());
const commonAnimMatchNames = commonAnimNames.map(k => k.replace(/\_/g, ''));
const commonAnimIds = Utils.getEnumValues(CommonAnim) as CommonAnim[];
const chargeAnimNames = Utils.getEnumKeys(ChargeAnim).map(k => k.toLowerCase());
const chargeAnimMatchNames = chargeAnimNames.map(k => k.replace(/\_/g, ' '));
const chargeAnimIds = Utils.getEnumValues(ChargeAnim) as ChargeAnim[];
2023-04-13 17:16:36 +01:00
const commonNamePattern = /name: (?:Common:)?(Opp )?(.*)/;
2023-04-04 01:47:41 +01:00
const moveNameToId = {};
2023-04-21 02:32:48 +01:00
for (let move of Utils.getEnumValues(Moves).slice(1)) {
2023-04-04 01:47:41 +01:00
const moveName = Moves[move].toUpperCase().replace(/\_/g, '');
moveNameToId[moveName] = move;
}
const animsData = [];//battleAnimRawData.split('!ruby/array:PBAnimation').slice(1);
for (let a = 0; a < animsData.length; a++) {
const fields = animsData[a].split('@').slice(1);
let isOppMove: boolean;
2023-04-11 18:00:04 +01:00
let commonAnimId: CommonAnim;
let chargeAnimId: ChargeAnim;
if (!fields[1].startsWith('name: Move:') && !(isOppMove = fields[1].startsWith('name: OppMove:'))) {
const nameMatch = commonNamePattern.exec(fields[1]);
2023-04-13 17:16:36 +01:00
const name = nameMatch[2].toLowerCase();
2023-04-11 18:00:04 +01:00
if (commonAnimMatchNames.indexOf(name) > -1)
commonAnimId = commonAnimIds[commonAnimMatchNames.indexOf(name)];
else if (chargeAnimMatchNames.indexOf(name) > -1)
chargeAnimId = chargeAnimIds[chargeAnimMatchNames.indexOf(name)];
}
2023-04-04 01:47:41 +01:00
const nameIndex = fields[1].indexOf(':', 5) + 1;
2023-04-11 18:00:04 +01:00
const animName = fields[1].slice(nameIndex, fields[1].indexOf('\n', nameIndex));
if (!moveNameToId.hasOwnProperty(animName) && !commonAnimId && !chargeAnimId)
2023-04-04 01:47:41 +01:00
continue;
2023-04-20 20:46:05 +01:00
let anim = commonAnimId || chargeAnimId ? new AnimConfig() : new AnimConfig();
if (anim instanceof AnimConfig)
(anim as AnimConfig).id = moveNameToId[animName];
2023-04-11 18:00:04 +01:00
if (commonAnimId)
commonAnims.set(commonAnimId, anim);
else if (chargeAnimId)
2023-04-20 20:46:05 +01:00
chargeAnims.set(chargeAnimId, !isOppMove ? anim : [ chargeAnims.get(chargeAnimId) as AnimConfig, anim ]);
2023-04-11 18:00:04 +01:00
else
2023-04-20 20:46:05 +01:00
moveAnims.set(moveNameToId[animName], !isOppMove ? anim as AnimConfig : [ moveAnims.get(moveNameToId[animName]) as AnimConfig, anim as AnimConfig ]);
2023-04-04 01:47:41 +01:00
for (let f = 0; f < fields.length; f++) {
const field = fields[f];
const fieldName = field.slice(0, field.indexOf(':'));
const fieldData = field.slice(fieldName.length + 1, field.lastIndexOf('\n')).trim();
switch (fieldName) {
case 'array':
const framesData = fieldData.split(' - - - ').slice(1);
for (let fd = 0; fd < framesData.length; fd++) {
anim.frames.push([]);
const frameData = framesData[fd];
const focusFramesData = frameData.split(' - - ');
for (let tf = 0; tf < focusFramesData.length; tf++) {
const values = focusFramesData[tf].replace(/ \- /g, '').split('\n');
const targetFrame = new AnimFrame(parseFloat(values[0]), parseFloat(values[1]), parseFloat(values[2]), parseFloat(values[11]), parseFloat(values[3]),
values[4] === '1', values[6] === '1', parseInt(values[5]), parseInt(values[7]), parseInt(values[8]), parseInt(values[12]), parseInt(values[13]),
parseInt(values[14]), parseInt(values[15]), parseInt(values[16]), parseInt(values[17]), parseInt(values[18]), parseInt(values[19]),
parseInt(values[21]), parseInt(values[22]), parseInt(values[23]), parseInt(values[24]), values[20] === '1', parseInt(values[25]), parseInt(values[26]) as AnimFocus);
anim.frames[fd].push(targetFrame);
}
}
break;
case 'graphic':
anim.graphic = fieldData !== "''" ? fieldData.slice(0, fieldData.indexOf('.')) : '';
break;
case 'timing':
const timingEntries = fieldData.split('- !ruby/object:PBAnimTiming ').slice(1);
for (let t = 0; t < timingEntries.length; t++) {
const timingData = timingEntries[t].replace(/\n/g, ' ').replace(/[ ]{2,}/g, ' ').replace(/[a-z]+: ! '', /ig, '').replace(/name: (.*?),/, 'name: "$1",')
.replace(/flashColor: !ruby\/object:Color { alpha: ([\d\.]+), blue: ([\d\.]+), green: ([\d\.]+), red: ([\d\.]+)}/, 'flashRed: $4, flashGreen: $3, flashBlue: $2, flashAlpha: $1');
const frameIndex = parseInt(/frame: (\d+)/.exec(timingData)[1]);
const resourceName = /name: "(.*?)"/.exec(timingData)[1].replace("''", '');
const timingType = parseInt(/timingType: (\d)/.exec(timingData)[1]);
let timedEvent: AnimTimedEvent;
switch (timingType) {
case 0:
timedEvent = new AnimTimedSoundEvent(frameIndex, resourceName);
break;
case 1:
timedEvent = new AnimTimedAddBgEvent(frameIndex, resourceName.slice(0, resourceName.indexOf('.')));
break;
case 2:
timedEvent = new AnimTimedUpdateBgEvent(frameIndex, resourceName.slice(0, resourceName.indexOf('.')));
break;
}
if (!timedEvent)
continue;
const propPattern = /([a-z]+): (.*?)(?:,|\})/ig;
let propMatch: RegExpExecArray;
while ((propMatch = propPattern.exec(timingData))) {
const prop = propMatch[1];
let value: any = propMatch[2];
switch (prop) {
case 'bgX':
case 'bgY':
value = parseFloat(value);
break;
case 'volume':
case 'pitch':
case 'opacity':
case 'colorRed':
case 'colorGreen':
case 'colorBlue':
case 'colorAlpha':
case 'duration':
case 'flashScope':
case 'flashRed':
case 'flashGreen':
case 'flashBlue':
case 'flashAlpha':
case 'flashDuration':
value = parseInt(value);
break;
}
if (timedEvent.hasOwnProperty(prop))
timedEvent[prop] = value;
}
if (!anim.frameTimedEvents.has(frameIndex))
anim.frameTimedEvents.set(frameIndex, []);
anim.frameTimedEvents.get(frameIndex).push(timedEvent);
}
break;
case 'position':
anim.position = parseInt(fieldData);
break;
case 'hue':
anim.hue = parseInt(fieldData);
break;
}
}
}
2023-04-11 18:00:04 +01:00
const animReplacer = (k, v) => {
2023-04-20 20:46:05 +01:00
if (k === 'id' && !v)
return undefined;
2023-04-11 18:00:04 +01:00
if (v instanceof Map)
return Object.fromEntries(v);
if (v instanceof AnimTimedEvent)
v['eventType'] = v.getEventType();
return v;
}
2023-04-10 22:20:37 +01:00
/*for (let ma of moveAnims.keys()) {
2023-04-04 01:47:41 +01:00
const data = moveAnims.get(ma);
(async () => {
2023-04-20 20:46:05 +01:00
await fs.writeFile(`../public/battle-anims/${Moves[ma].toLowerCase().replace(/\_/g, '-')}.json`, JSON.stringify(data, animReplacer, ' '));
2023-04-11 18:00:04 +01:00
})();
}
for (let ca of chargeAnims.keys()) {
const data = chargeAnims.get(ca);
(async () => {
2023-04-20 20:46:05 +01:00
await fs.writeFile(`../public/battle-anims/${chargeAnimNames[chargeAnimIds.indexOf(ca)].replace(/\_/g, '-')}.json`, JSON.stringify(data, animReplacer, ' '));
2023-04-11 18:00:04 +01:00
})();
}
for (let cma of commonAnims.keys()) {
const data = commonAnims.get(cma);
(async () => {
2023-04-20 20:46:05 +01:00
await fs.writeFile(`../public/battle-anims/common-${commonAnimNames[commonAnimIds.indexOf(cma)].replace(/\_/g, '-')}.json`, JSON.stringify(data, animReplacer, ' '));
2023-04-11 18:00:04 +01:00
})();
2023-04-10 22:20:37 +01:00
}*/
2023-04-04 01:47:41 +01:00
}