mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2024-12-12 00:26:15 +00:00
1196 lines
49 KiB
TypeScript
1196 lines
49 KiB
TypeScript
//import { battleAnimRawData } from "./battle-anim-raw-data";
|
|
import BattleScene from "../battle-scene";
|
|
import { AttackMove, ChargeAttr, DelayedAttackAttr, MoveFlags, SelfStatusMove, allMoves } from "./move";
|
|
import Pokemon from "../field/pokemon";
|
|
import * as Utils from "../utils";
|
|
import { BattlerIndex } from "../battle";
|
|
import stringify, { Element } from "json-stable-stringify";
|
|
import { Moves } from "./enums/moves";
|
|
import { getTypeRgb } from "./type";
|
|
//import fs from 'vite-plugin-fs/browser';
|
|
|
|
export enum AnimFrameTarget {
|
|
USER,
|
|
TARGET,
|
|
GRAPHIC
|
|
}
|
|
|
|
enum AnimFocus {
|
|
TARGET = 1,
|
|
USER,
|
|
USER_TARGET,
|
|
SCREEN
|
|
}
|
|
|
|
enum AnimBlendType {
|
|
NORMAL,
|
|
ADD,
|
|
SUBTRACT
|
|
}
|
|
|
|
export enum ChargeAnim {
|
|
FLY_CHARGING = 1000,
|
|
BOUNCE_CHARGING,
|
|
DIG_CHARGING,
|
|
FUTURE_SIGHT_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,
|
|
PHANTOM_FORCE_CHARGING,
|
|
GEOMANCY_CHARGING,
|
|
SHADOW_BLADE_CHARGING,
|
|
SOLAR_BLADE_CHARGING,
|
|
BEAK_BLAST_CHARGING,
|
|
METEOR_BEAM_CHARGING,
|
|
ELECTRO_SHOT_CHARGING
|
|
}
|
|
|
|
export enum CommonAnim {
|
|
USE_ITEM = 2000,
|
|
HEALTH_UP,
|
|
POISON = 2010,
|
|
TOXIC,
|
|
PARALYSIS,
|
|
SLEEP,
|
|
FROZEN,
|
|
BURN,
|
|
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,
|
|
THUNDER_CAGE,
|
|
ORDER_UP_CURLY,
|
|
ORDER_UP_DROOPY,
|
|
ORDER_UP_STRETCHY,
|
|
RAGING_BULL_FIRE,
|
|
RAGING_BULL_WATER,
|
|
SALT_CURE,
|
|
SUNNY = 2100,
|
|
RAIN,
|
|
SANDSTORM,
|
|
HAIL,
|
|
WIND,
|
|
HEAVY_RAIN,
|
|
HARSH_SUN,
|
|
STRONG_WINDS,
|
|
MISTY_TERRAIN = 2110,
|
|
ELECTRIC_TERRAIN,
|
|
GRASSY_TERRAIN,
|
|
PSYCHIC_TERRAIN,
|
|
LOCK_ON = 2120
|
|
}
|
|
|
|
export class AnimConfig {
|
|
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;
|
|
const frames: any[][] = source.frames;
|
|
frames.map(animFrames => {
|
|
for (let f = 0; f < animFrames.length; f++)
|
|
animFrames[f] = new ImportedAnimFrame(animFrames[f]);
|
|
});
|
|
this.frames = 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;
|
|
public blendType: AnimBlendType;
|
|
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;
|
|
|
|
constructor(x: number, y: number, zoomX: number, zoomY: number, angle: number, mirror: boolean, visible: boolean, blendType: AnimBlendType, pattern: integer,
|
|
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, init?: boolean) {
|
|
this.x = !init ? ((x || 0) - 128) * 0.5 : x;
|
|
this.y = !init ? ((y || 0) - 224) * 0.5 : y;
|
|
if (zoomX)
|
|
this.zoomX = zoomX;
|
|
else if (init)
|
|
this.zoomX = 0;
|
|
if (zoomY)
|
|
this.zoomY = zoomY;
|
|
else if (init)
|
|
this.zoomY = 0;
|
|
if (angle)
|
|
this.angle = angle;
|
|
else if (init)
|
|
this.angle = 0;
|
|
if (mirror)
|
|
this.mirror = mirror;
|
|
else if (init)
|
|
this.mirror = false;
|
|
if (visible)
|
|
this.visible = visible;
|
|
else if (init)
|
|
this.visible = false;
|
|
if (blendType)
|
|
this.blendType = blendType;
|
|
else if (init)
|
|
this.blendType = AnimBlendType.NORMAL;
|
|
if (!init) {
|
|
let target = AnimFrameTarget.GRAPHIC;
|
|
switch (pattern) {
|
|
case -2:
|
|
target = AnimFrameTarget.TARGET;
|
|
break;
|
|
case -1:
|
|
target = AnimFrameTarget.USER;
|
|
break;
|
|
}
|
|
this.target = target;
|
|
this.graphicFrame = pattern >= 0 ? pattern : 0;
|
|
}
|
|
if (opacity)
|
|
this.opacity = opacity;
|
|
else if (init)
|
|
this.opacity = 0;
|
|
if (colorR || colorG || colorB || colorA)
|
|
this.color = [ colorR || 0, colorG || 0, colorB || 0, colorA || 0 ];
|
|
else if (init)
|
|
this.color = [ 0, 0, 0, 0 ];
|
|
if (toneR || toneG || toneB || toneA)
|
|
this.tone = [ toneR || 0, toneG || 0, toneB || 0, toneA || 0 ];
|
|
else if (init)
|
|
this.tone = [ 0, 0, 0, 0 ];
|
|
if (flashR || flashG || flashB || flashA)
|
|
this.flash = [ flashR || 0, flashG || 0, flashB || 0, flashA || 0 ];
|
|
else if (init)
|
|
this.flash = [ 0, 0, 0, 0 ];
|
|
if (locked)
|
|
this.locked = locked;
|
|
else if (init)
|
|
this.locked = false;
|
|
if (priority)
|
|
this.priority = priority;
|
|
else if (init)
|
|
this.priority = 0;
|
|
this.focus = focus || AnimFocus.TARGET;
|
|
}
|
|
}
|
|
|
|
class ImportedAnimFrame extends AnimFrame {
|
|
constructor(source: any) {
|
|
const color: integer[] = source.color || [ 0, 0, 0, 0 ];
|
|
const tone: integer[] = source.tone || [ 0, 0, 0, 0 ];
|
|
const flash: integer[] = source.flash || [ 0, 0, 0, 0 ];
|
|
super(source.x, source.y, source.zoomX, source.zoomY, source.angle, source.mirror, source.visible, source.blendType, source.graphicFrame, source.opacity, color[0], color[1], color[2], color[3], tone[0], tone[1], tone[2], tone[3], flash[0], flash[1], flash[2], flash[3], source.locked, source.priority, source.focus, true);
|
|
this.target = source.target;
|
|
this.graphicFrame = source.graphicFrame;
|
|
}
|
|
}
|
|
|
|
abstract class AnimTimedEvent {
|
|
public frameIndex: integer;
|
|
public resourceName: string;
|
|
|
|
constructor(frameIndex: integer, resourceName: string) {
|
|
this.frameIndex = frameIndex;
|
|
this.resourceName = resourceName;
|
|
}
|
|
|
|
abstract execute(scene: BattleScene, battleAnim: BattleAnim): integer;
|
|
|
|
abstract getEventType(): string;
|
|
}
|
|
|
|
class AnimTimedSoundEvent extends AnimTimedEvent {
|
|
public volume: number = 100;
|
|
public pitch: number = 100;
|
|
|
|
constructor(frameIndex: integer, resourceName: string, source?: any) {
|
|
super(frameIndex, resourceName);
|
|
|
|
if (source) {
|
|
this.volume = source.volume;
|
|
this.pitch = source.pitch;
|
|
}
|
|
}
|
|
|
|
execute(scene: BattleScene, battleAnim: BattleAnim): integer {
|
|
const soundConfig = { rate: (this.pitch * 0.01), volume: (this.volume * 0.01) };
|
|
if (this.resourceName) {
|
|
try {
|
|
scene.playSound(this.resourceName, soundConfig);
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
return Math.ceil((scene.sound.get(this.resourceName).totalDuration * 1000) / 33.33);
|
|
} else
|
|
return Math.ceil((battleAnim.user.cry(soundConfig).totalDuration * 1000) / 33.33);
|
|
}
|
|
|
|
getEventType(): string {
|
|
return 'AnimTimedSoundEvent';
|
|
}
|
|
}
|
|
|
|
abstract class AnimTimedBgEvent extends AnimTimedEvent {
|
|
public bgX: number = 0;
|
|
public bgY: number = 0;
|
|
public opacity: integer = 0;
|
|
/*public colorRed: integer = 0;
|
|
public colorGreen: integer = 0;
|
|
public colorBlue: integer = 0;
|
|
public colorAlpha: integer = 0;*/
|
|
public duration: integer = 0;
|
|
/*public flashScope: integer = 0;
|
|
public flashRed: integer = 0;
|
|
public flashGreen: integer = 0;
|
|
public flashBlue: integer = 0;
|
|
public flashAlpha: integer = 0;
|
|
public flashDuration: integer = 0;*/
|
|
|
|
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);
|
|
}
|
|
|
|
execute(scene: BattleScene, moveAnim: MoveAnim): integer {
|
|
const tweenProps = {};
|
|
if (this.bgX !== undefined)
|
|
tweenProps['x'] = (this.bgX * 0.5) - 320;
|
|
if (this.bgY !== undefined)
|
|
tweenProps['y'] = (this.bgY * 0.5) - 284;
|
|
if (this.opacity !== undefined)
|
|
tweenProps['alpha'] = (this.opacity || 0) / 255;
|
|
if (Object.keys(tweenProps).length) {
|
|
scene.tweens.add(Object.assign({
|
|
targets: moveAnim.bgSprite,
|
|
duration: Utils.getFrameMs(this.duration * 3)
|
|
}, tweenProps));
|
|
}
|
|
return this.duration * 2;
|
|
}
|
|
|
|
getEventType(): string {
|
|
return 'AnimTimedUpdateBgEvent';
|
|
}
|
|
}
|
|
|
|
class AnimTimedAddBgEvent extends AnimTimedBgEvent {
|
|
constructor(frameIndex: integer, resourceName: string, source?: any) {
|
|
super(frameIndex, resourceName, source);
|
|
}
|
|
|
|
execute(scene: BattleScene, moveAnim: MoveAnim): integer {
|
|
if (moveAnim.bgSprite)
|
|
moveAnim.bgSprite.destroy();
|
|
moveAnim.bgSprite = this.resourceName
|
|
? scene.add.tileSprite(this.bgX - 320, this.bgY - 284, 896, 576, this.resourceName)
|
|
: scene.add.rectangle(this.bgX - 320, this.bgY - 284, 896, 576, 0);
|
|
moveAnim.bgSprite.setOrigin(0, 0);
|
|
moveAnim.bgSprite.setScale(1.25);
|
|
moveAnim.bgSprite.setAlpha(this.opacity / 255);
|
|
scene.field.add(moveAnim.bgSprite);
|
|
const fieldPokemon = scene.getEnemyPokemon() || scene.getPlayerPokemon();
|
|
if (fieldPokemon?.isOnField())
|
|
scene.field.moveBelow(moveAnim.bgSprite as Phaser.GameObjects.GameObject, fieldPokemon);
|
|
|
|
scene.tweens.add({
|
|
targets: moveAnim.bgSprite,
|
|
duration: Utils.getFrameMs(this.duration * 3)
|
|
});
|
|
|
|
return this.duration * 2;
|
|
}
|
|
|
|
getEventType(): string {
|
|
return 'AnimTimedAddBgEvent';
|
|
}
|
|
}
|
|
|
|
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>();
|
|
|
|
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())
|
|
.then(cas => commonAnims.set(commonAnimId, new AnimConfig(cas))));
|
|
}
|
|
Promise.allSettled(commonAnimFetches).then(() => resolve());
|
|
});
|
|
}
|
|
|
|
export function initMoveAnim(move: Moves): Promise<void> {
|
|
return new Promise(resolve => {
|
|
if (moveAnims.has(move)) {
|
|
if (moveAnims.get(move) !== null)
|
|
resolve();
|
|
else {
|
|
let loadedCheckTimer = setInterval(() => {
|
|
if (moveAnims.get(move) !== null) {
|
|
const chargeAttr = allMoves[move].getAttrs(ChargeAttr).find(() => true) as ChargeAttr || allMoves[move].getAttrs(DelayedAttackAttr).find(() => true) as DelayedAttackAttr;
|
|
if (chargeAttr && chargeAnims.get(chargeAttr.chargeAnim) === null)
|
|
return;
|
|
clearInterval(loadedCheckTimer);
|
|
resolve();
|
|
}
|
|
}, 50);
|
|
}
|
|
} else {
|
|
moveAnims.set(move, null);
|
|
const defaultMoveAnim = allMoves[move] instanceof AttackMove ? Moves.TACKLE : allMoves[move] instanceof SelfStatusMove ? Moves.FOCUS_ENERGY : Moves.TAIL_WHIP;
|
|
const fetchAnimAndResolve = (move: Moves) => {
|
|
fetch(`./battle-anims/${Moves[move].toLowerCase().replace(/\_/g, '-')}.json`)
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
console.error(response.statusText);
|
|
if (move !== defaultMoveAnim)
|
|
fetchAnimAndResolve(defaultMoveAnim);
|
|
else
|
|
resolve();
|
|
return;
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(ba => {
|
|
if (Array.isArray(ba)) {
|
|
populateMoveAnim(move, ba[0]);
|
|
populateMoveAnim(move, ba[1]);
|
|
} else
|
|
populateMoveAnim(move, ba);
|
|
const chargeAttr = allMoves[move].getAttrs(ChargeAttr).find(() => true) as ChargeAttr || allMoves[move].getAttrs(DelayedAttackAttr).find(() => true) as DelayedAttackAttr;
|
|
if (chargeAttr)
|
|
initMoveChargeAnim(chargeAttr.chargeAnim).then(() => resolve());
|
|
else
|
|
resolve();
|
|
});
|
|
};
|
|
fetchAnimAndResolve(move);
|
|
}
|
|
});
|
|
}
|
|
|
|
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);
|
|
resolve();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function populateMoveAnim(move: Moves, animSource: any): void {
|
|
const moveAnim = new AnimConfig(animSource);
|
|
if (moveAnims.get(move) === null) {
|
|
moveAnims.set(move, moveAnim);
|
|
return;
|
|
}
|
|
moveAnims.set(move, [ moveAnims.get(move) as AnimConfig, moveAnim ]);
|
|
}
|
|
|
|
function populateMoveChargeAnim(chargeAnim: ChargeAnim, animSource: any) {
|
|
const moveChargeAnim = new AnimConfig(animSource);
|
|
if (chargeAnims.get(chargeAnim) === null) {
|
|
chargeAnims.set(chargeAnim, moveChargeAnim);
|
|
return;
|
|
}
|
|
chargeAnims.set(chargeAnim, [ chargeAnims.get(chargeAnim) as AnimConfig, moveChargeAnim ]);
|
|
}
|
|
|
|
export function loadCommonAnimAssets(scene: BattleScene, startLoad?: boolean): Promise<void> {
|
|
return new Promise(resolve => {
|
|
loadAnimAssets(scene, Array.from(commonAnims.values()), startLoad).then(() => resolve());
|
|
});
|
|
}
|
|
|
|
export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLoad?: boolean): Promise<void> {
|
|
return new Promise(resolve => {
|
|
const moveAnimations = moveIds.map(m => moveAnims.get(m) as AnimConfig).flat();
|
|
for (let moveId of moveIds) {
|
|
const chargeAttr = allMoves[moveId].getAttrs(ChargeAttr).find(() => true) as ChargeAttr || allMoves[moveId].getAttrs(DelayedAttackAttr).find(() => true) as DelayedAttackAttr;
|
|
if (chargeAttr) {
|
|
const moveChargeAnims = chargeAnims.get(chargeAttr.chargeAnim);
|
|
moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims[0]);
|
|
if (Array.isArray(moveChargeAnims))
|
|
moveAnimations.push(moveChargeAnims[1]);
|
|
}
|
|
}
|
|
loadAnimAssets(scene, moveAnimations, startLoad).then(() => resolve());
|
|
});
|
|
}
|
|
|
|
function loadAnimAssets(scene: BattleScene, anims: AnimConfig[], startLoad?: boolean): Promise<void> {
|
|
return new Promise(resolve => {
|
|
const backgrounds = new Set<string>();
|
|
const sounds = new Set<string>();
|
|
for (let a of anims) {
|
|
if (!a.frames?.length)
|
|
continue;
|
|
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);
|
|
}
|
|
for (let bg of backgrounds)
|
|
scene.loadImage(bg, 'battle_anims');
|
|
for (let s of sounds)
|
|
scene.loadSe(s, 'battle_anims', s);
|
|
if (startLoad) {
|
|
scene.load.once(Phaser.Loader.Events.COMPLETE, () => resolve());
|
|
if (!scene.load.isLoading())
|
|
scene.load.start();
|
|
} else
|
|
resolve();
|
|
});
|
|
}
|
|
|
|
interface GraphicFrameData {
|
|
x: number,
|
|
y: number,
|
|
scaleX: number,
|
|
scaleY: number,
|
|
angle: number
|
|
}
|
|
|
|
const userFocusX = 106;
|
|
const userFocusY = 148 - 32;
|
|
const targetFocusX = 234;
|
|
const targetFocusY = 84 - 32;
|
|
|
|
function transformPoint(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, px: number, py: number): [ x: number, y: number ] {
|
|
const yIntersect = yAxisIntersect(x1, y1, x2, y2, px, py);
|
|
return repositionY(x3, y3, x4, y4, yIntersect[0], yIntersect[1]);
|
|
}
|
|
|
|
function yAxisIntersect(x1: number, y1: number, x2: number, y2: number, px: number, py: number): [ x: number, y: number ] {
|
|
const dx = x2 - x1;
|
|
const dy = y2 - y1;
|
|
const x = dx === 0 ? 0 : (px - x1) / dx;
|
|
const y = dy === 0 ? 0 : (py - y1) / dy;
|
|
return [ x, y ];
|
|
}
|
|
|
|
function repositionY(x1: number, y1: number, x2: number, y2: number, tx: number, ty: number): [ x: number, y: number ] {
|
|
const dx = x2 - x1;
|
|
const dy = y2 - y1;
|
|
const x = x1 + (tx * dx);
|
|
const y = y1 + (ty * dy);
|
|
return [ x, y ];
|
|
}
|
|
|
|
function isReversed(src1: number, src2: number, dst1: number, dst2: number) {
|
|
if (src1 === src2)
|
|
return false;
|
|
if (src1 < src2)
|
|
return dst1 > dst2;
|
|
return dst1 < dst2;
|
|
}
|
|
|
|
interface SpriteCache {
|
|
[key: integer]: Phaser.GameObjects.Sprite[]
|
|
}
|
|
|
|
export abstract class BattleAnim {
|
|
public user: Pokemon;
|
|
public target: Pokemon;
|
|
public sprites: Phaser.GameObjects.Sprite[];
|
|
public bgSprite: Phaser.GameObjects.TileSprite | Phaser.GameObjects.Rectangle;
|
|
|
|
private srcLine: number[];
|
|
private dstLine: number[];
|
|
|
|
constructor(user: Pokemon, target: Pokemon) {
|
|
this.user = user;
|
|
this.target = target;
|
|
this.sprites = [];
|
|
}
|
|
|
|
abstract getAnim(): AnimConfig;
|
|
|
|
abstract isOppAnim(): boolean;
|
|
|
|
protected isHideUser(): boolean {
|
|
return false;
|
|
}
|
|
|
|
protected isHideTarget(): boolean {
|
|
return false;
|
|
}
|
|
|
|
private getGraphicFrameData(scene: BattleScene, frames: AnimFrame[]): Map<integer, Map<AnimFrameTarget, GraphicFrameData>> {
|
|
const ret: Map<integer, Map<AnimFrameTarget, GraphicFrameData>> = new Map([
|
|
[AnimFrameTarget.GRAPHIC, new Map<AnimFrameTarget, GraphicFrameData>() ],
|
|
[AnimFrameTarget.USER, new Map<AnimFrameTarget, GraphicFrameData>() ],
|
|
[AnimFrameTarget.TARGET, new Map<AnimFrameTarget, GraphicFrameData>() ]
|
|
]);
|
|
|
|
const isOppAnim = this.isOppAnim();
|
|
const user = !isOppAnim ? this.user : this.target;
|
|
const target = !isOppAnim ? this.target : this.user;
|
|
|
|
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;
|
|
let u = 0;
|
|
let t = 0;
|
|
|
|
for (let frame of frames) {
|
|
let x = frame.x + 106;
|
|
let y = frame.y + 116;
|
|
let scaleX = (frame.zoomX / 100) * (!frame.mirror ? 1 : -1);
|
|
let scaleY = (frame.zoomY / 100);
|
|
switch (frame.focus) {
|
|
case AnimFocus.TARGET:
|
|
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 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 });
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
play(scene: BattleScene, callback?: Function) {
|
|
const isOppAnim = this.isOppAnim();
|
|
const user = !isOppAnim ? this.user : this.target;
|
|
const target = !isOppAnim ? this.target : this.user;
|
|
|
|
if (!target.isOnField()) {
|
|
if (callback)
|
|
callback();
|
|
return;
|
|
}
|
|
|
|
const userSprite = user.getSprite();
|
|
const targetSprite = target.getSprite();
|
|
|
|
const anim = this.getAnim();
|
|
|
|
const userInitialX = user.x;
|
|
const userInitialY = user.y;
|
|
const targetInitialX = target.x;
|
|
const targetInitialY = target.y;
|
|
|
|
this.srcLine = [ userFocusX, userFocusY, targetFocusX, targetFocusY ];
|
|
this.dstLine = [ userInitialX, userInitialY, targetInitialX, targetInitialY ];
|
|
|
|
let r = anim.frames.length;
|
|
let f = 0;
|
|
|
|
const spriteCache: SpriteCache = {
|
|
[AnimFrameTarget.GRAPHIC]: [],
|
|
[AnimFrameTarget.USER]: [],
|
|
[AnimFrameTarget.TARGET]: []
|
|
};
|
|
const spritePriorities: integer[] = [];
|
|
|
|
scene.tweens.addCounter({
|
|
duration: Utils.getFrameMs(3),
|
|
repeat: anim.frames.length,
|
|
onRepeat: () => {
|
|
if (!f) {
|
|
userSprite.setVisible(false);
|
|
targetSprite.setVisible(false);
|
|
}
|
|
|
|
const spriteFrames = anim.frames[f];
|
|
const frameData = this.getGraphicFrameData(scene, anim.frames[f]);
|
|
let u = 0;
|
|
let t = 0;
|
|
let g = 0;
|
|
for (let frame of spriteFrames) {
|
|
if (frame.target !== AnimFrameTarget.GRAPHIC) {
|
|
const isUser = frame.target === AnimFrameTarget.USER;
|
|
if (isUser && target === user)
|
|
continue;
|
|
const sprites = spriteCache[isUser ? AnimFrameTarget.USER : AnimFrameTarget.TARGET];
|
|
const spriteSource = isUser ? userSprite : targetSprite;
|
|
if ((isUser ? u : t) === sprites.length) {
|
|
let sprite: Phaser.GameObjects.Sprite;
|
|
sprite = scene.addPokemonSprite(isUser ? user : target, 0, 0, spriteSource.texture, spriteSource.frame.name, true);
|
|
[ 'spriteColors', 'fusionSpriteColors' ].map(k => sprite.pipelineData[k] = (isUser ? user : target).getSprite().pipelineData[k]);
|
|
sprite.pipelineData['ignoreFieldPos'] = true;
|
|
spriteSource.on('animationupdate', (_anim, frame) => sprite.setFrame(frame.textureFrame));
|
|
scene.field.add(sprite);
|
|
sprites.push(sprite);
|
|
}
|
|
|
|
const spriteIndex = isUser ? u++ : t++;
|
|
const pokemonSprite = sprites[spriteIndex];
|
|
const graphicFrameData = frameData.get(frame.target).get(spriteIndex);
|
|
pokemonSprite.setPosition(graphicFrameData.x, graphicFrameData.y - ((spriteSource.height / 2) * (spriteSource.parentContainer.scale - 1)));
|
|
|
|
pokemonSprite.setAngle(graphicFrameData.angle);
|
|
pokemonSprite.setScale(graphicFrameData.scaleX * spriteSource.parentContainer.scale, graphicFrameData.scaleY * spriteSource.parentContainer.scale);
|
|
|
|
pokemonSprite.setData('locked', frame.locked);
|
|
|
|
pokemonSprite.setAlpha(frame.opacity / 255);
|
|
pokemonSprite.pipelineData['tone'] = frame.tone;
|
|
pokemonSprite.setVisible(frame.visible && (isUser ? user.visible : target.visible));
|
|
pokemonSprite.setBlendMode(frame.blendType === AnimBlendType.NORMAL ? Phaser.BlendModes.NORMAL : frame.blendType === AnimBlendType.ADD ? Phaser.BlendModes.ADD : Phaser.BlendModes.DIFFERENCE);
|
|
} else {
|
|
const sprites = spriteCache[AnimFrameTarget.GRAPHIC];
|
|
if (g === sprites.length) {
|
|
let newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim.graphic, 1);
|
|
sprites.push(newSprite);
|
|
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:
|
|
scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, scene.getEnemyPokemon() || scene.getPlayerPokemon());
|
|
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 as Phaser.GameObjects.GameObject, this.bgSprite);
|
|
else
|
|
scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.user);
|
|
break;
|
|
case AnimFocus.TARGET:
|
|
scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.target);
|
|
break;
|
|
default:
|
|
setSpritePriority(1);
|
|
break;
|
|
}
|
|
break;
|
|
case 3:
|
|
switch (frame.focus) {
|
|
case AnimFocus.USER:
|
|
scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.user);
|
|
break;
|
|
case AnimFocus.TARGET:
|
|
scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.target);
|
|
break;
|
|
default:
|
|
setSpritePriority(1);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
setSpritePriority(1);
|
|
}
|
|
};
|
|
setSpritePriority(frame.priority);
|
|
}
|
|
moveSprite.setFrame(frame.graphicFrame);
|
|
//console.log(AnimFocus[frame.focus]);
|
|
|
|
const graphicFrameData = frameData.get(frame.target).get(graphicIndex);
|
|
moveSprite.setPosition(graphicFrameData.x, graphicFrameData.y);
|
|
moveSprite.setAngle(graphicFrameData.angle);
|
|
moveSprite.setScale(graphicFrameData.scaleX, graphicFrameData.scaleY);
|
|
|
|
moveSprite.setAlpha(frame.opacity / 255);
|
|
moveSprite.setVisible(frame.visible);
|
|
moveSprite.setBlendMode(frame.blendType === AnimBlendType.NORMAL ? Phaser.BlendModes.NORMAL : frame.blendType === AnimBlendType.ADD ? Phaser.BlendModes.ADD : Phaser.BlendModes.DIFFERENCE);
|
|
}
|
|
}
|
|
if (anim.frameTimedEvents.has(f)) {
|
|
for (let event of anim.frameTimedEvents.get(f))
|
|
r = Math.max((anim.frames.length - f) + event.execute(scene, this), r);
|
|
}
|
|
const targets = Utils.getEnumValues(AnimFrameTarget);
|
|
for (let i of targets) {
|
|
const count = i === AnimFrameTarget.GRAPHIC ? g : i === AnimFrameTarget.USER ? u : t;
|
|
if (count < spriteCache[i].length) {
|
|
const spritesToRemove = spriteCache[i].slice(count, spriteCache[i].length);
|
|
for (let rs of spritesToRemove) {
|
|
if (!rs.getData('locked') as boolean) {
|
|
const spriteCacheIndex = spriteCache[i].indexOf(rs);
|
|
spriteCache[i].splice(spriteCacheIndex, 1);
|
|
if (i === AnimFrameTarget.GRAPHIC)
|
|
spritePriorities.splice(spriteCacheIndex, 1);
|
|
rs.destroy();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
f++;
|
|
r--;
|
|
},
|
|
onComplete: () => {
|
|
const cleanUpAndComplete = () => {
|
|
userSprite.setPosition(0, 0);
|
|
userSprite.setScale(1);
|
|
userSprite.setAlpha(1);
|
|
userSprite.pipelineData['tone'] = [ 0.0, 0.0, 0.0, 0.0 ];
|
|
userSprite.setAngle(0);
|
|
targetSprite.setPosition(0, 0);
|
|
targetSprite.setScale(1);
|
|
targetSprite.setAlpha(1);
|
|
targetSprite.pipelineData['tone'] = [ 0.0, 0.0, 0.0, 0.0 ];
|
|
targetSprite.setAngle(0);
|
|
if (!this.isHideUser())
|
|
userSprite.setVisible(true);
|
|
if (!this.isHideTarget() && (targetSprite !== userSprite || !this.isHideUser()))
|
|
targetSprite.setVisible(true);
|
|
for (let ms of Object.values(spriteCache).flat()) {
|
|
if (ms)
|
|
ms.destroy();
|
|
}
|
|
if (this.bgSprite)
|
|
this.bgSprite.destroy();
|
|
if (callback)
|
|
callback();
|
|
};
|
|
for (let ms of Object.values(spriteCache).flat()) {
|
|
if (ms && !ms.getData('locked'))
|
|
ms.destroy();
|
|
}
|
|
if (r) {
|
|
scene.tweens.addCounter({
|
|
duration: Utils.getFrameMs(r),
|
|
onComplete: () => cleanUpAndComplete()
|
|
});
|
|
} else
|
|
cleanUpAndComplete();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
export class CommonBattleAnim extends BattleAnim {
|
|
public commonAnim: CommonAnim;
|
|
|
|
constructor(commonAnim: CommonAnim, user: Pokemon, target?: Pokemon) {
|
|
super(user, target || user);
|
|
|
|
this.commonAnim = commonAnim;
|
|
}
|
|
|
|
getAnim(): AnimConfig {
|
|
return commonAnims.get(this.commonAnim);
|
|
}
|
|
|
|
isOppAnim(): boolean {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export class MoveAnim extends BattleAnim {
|
|
public move: Moves;
|
|
|
|
constructor(move: Moves, user: Pokemon, target: BattlerIndex) {
|
|
super(user, user.scene.getField()[target]);
|
|
|
|
this.move = move;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
isOppAnim(): boolean {
|
|
return !this.user.isPlayer() && Array.isArray(moveAnims.get(this.move));
|
|
}
|
|
|
|
protected isHideUser(): boolean {
|
|
return allMoves[this.move].hasFlag(MoveFlags.HIDE_USER);
|
|
}
|
|
|
|
protected isHideTarget(): boolean {
|
|
return allMoves[this.move].hasFlag(MoveFlags.HIDE_TARGET);
|
|
}
|
|
}
|
|
|
|
export class MoveChargeAnim extends MoveAnim {
|
|
private chargeAnim: ChargeAnim;
|
|
|
|
constructor(chargeAnim: ChargeAnim, move: Moves, user: Pokemon) {
|
|
super(move, user, 0);
|
|
|
|
this.chargeAnim = chargeAnim;
|
|
}
|
|
|
|
isOppAnim(): boolean {
|
|
return !this.user.isPlayer() && Array.isArray(chargeAnims.get(this.chargeAnim));
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
export async function populateAnims() {
|
|
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[];
|
|
const commonNamePattern = /name: (?:Common:)?(Opp )?(.*)/;
|
|
const moveNameToId = {};
|
|
for (let move of Utils.getEnumValues(Moves).slice(1)) {
|
|
const moveName = Moves[move].toUpperCase().replace(/\_/g, '');
|
|
moveNameToId[moveName] = move;
|
|
}
|
|
|
|
const seNames = [];//(await fs.readdir('./public/audio/se/battle_anims/')).map(se => se.toString());
|
|
|
|
const animsData = [];//battleAnimRawData.split('!ruby/array:PBAnimation').slice(1);
|
|
for (let a = 0; a < animsData.length; a++) {
|
|
const fields = animsData[a].split('@').slice(1);
|
|
|
|
const nameField = fields.find(f => f.startsWith('name: '));
|
|
|
|
let isOppMove: boolean;
|
|
let commonAnimId: CommonAnim;
|
|
let chargeAnimId: ChargeAnim;
|
|
if (!nameField.startsWith('name: Move:') && !(isOppMove = nameField.startsWith('name: OppMove:'))) {
|
|
const nameMatch = commonNamePattern.exec(nameField);
|
|
const name = nameMatch[2].toLowerCase();
|
|
if (commonAnimMatchNames.indexOf(name) > -1)
|
|
commonAnimId = commonAnimIds[commonAnimMatchNames.indexOf(name)];
|
|
else if (chargeAnimMatchNames.indexOf(name) > -1) {
|
|
isOppMove = nameField.startsWith('name: Opp ');
|
|
chargeAnimId = chargeAnimIds[chargeAnimMatchNames.indexOf(name)];
|
|
}
|
|
}
|
|
const nameIndex = nameField.indexOf(':', 5) + 1;
|
|
const animName = nameField.slice(nameIndex, nameField.indexOf('\n', nameIndex));
|
|
if (!moveNameToId.hasOwnProperty(animName) && !commonAnimId && !chargeAnimId)
|
|
continue;
|
|
let anim = commonAnimId || chargeAnimId ? new AnimConfig() : new AnimConfig();
|
|
if (anim instanceof AnimConfig)
|
|
(anim as AnimConfig).id = moveNameToId[animName];
|
|
if (commonAnimId)
|
|
commonAnims.set(commonAnimId, anim);
|
|
else if (chargeAnimId)
|
|
chargeAnims.set(chargeAnimId, !isOppMove ? anim : [ chargeAnims.get(chargeAnimId) as AnimConfig, anim ]);
|
|
else
|
|
moveAnims.set(moveNameToId[animName], !isOppMove ? anim as AnimConfig : [ moveAnims.get(moveNameToId[animName]) as AnimConfig, anim as AnimConfig ]);
|
|
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]),
|
|
parseInt(values[4]) === 1, parseInt(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]), parseInt(values[20]) === 1, parseInt(values[25]), parseInt(values[26]) as AnimFocus);
|
|
anim.frames[fd].push(targetFrame);
|
|
}
|
|
}
|
|
break;
|
|
case 'graphic':
|
|
const graphic = fieldData !== "''" ? fieldData : '';
|
|
anim.graphic = graphic.indexOf('.') > -1
|
|
? graphic.slice(0, fieldData.indexOf('.'))
|
|
: graphic;
|
|
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]);
|
|
let resourceName = /name: "(.*?)"/.exec(timingData)[1].replace("''", '');
|
|
const timingType = parseInt(/timingType: (\d)/.exec(timingData)[1]);
|
|
let timedEvent: AnimTimedEvent;
|
|
switch (timingType) {
|
|
case 0:
|
|
if (resourceName && resourceName.indexOf('.') === -1) {
|
|
let ext: string;
|
|
[ 'wav', 'mp3', 'm4a' ].every(e => {
|
|
if (seNames.indexOf(`${resourceName}.${e}`) > -1) {
|
|
ext = e;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
if (!ext)
|
|
ext = '.wav';
|
|
resourceName += `.${ext}`;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
const animReplacer = (k, v) => {
|
|
if (k === 'id' && !v)
|
|
return undefined;
|
|
if (v instanceof Map)
|
|
return Object.fromEntries(v);
|
|
if (v instanceof AnimTimedEvent)
|
|
v['eventType'] = v.getEventType();
|
|
return v;
|
|
};
|
|
|
|
const animConfigProps = [ 'id', 'graphic', 'frames', 'frameTimedEvents', 'position', 'hue' ];
|
|
const animFrameProps = [ 'x', 'y', 'zoomX', 'zoomY', 'angle', 'mirror', 'visible', 'blendType', 'target', 'graphicFrame', 'opacity', 'color', 'tone', 'flash', 'locked', 'priority', 'focus' ];
|
|
const propSets = [ animConfigProps, animFrameProps ];
|
|
|
|
const animComparator = (a: Element, b: Element) => {
|
|
let props: string[];
|
|
let p = 0;
|
|
for (let p = 0; p < propSets.length; p++) {
|
|
props = propSets[p];
|
|
let ai = props.indexOf(a.key);
|
|
if (ai === -1)
|
|
continue;
|
|
let bi = props.indexOf(b.key);
|
|
|
|
return ai < bi ? -1 : ai > bi ? 1 : 0;
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
/*for (let ma of moveAnims.keys()) {
|
|
const data = moveAnims.get(ma);
|
|
(async () => {
|
|
await fs.writeFile(`../public/battle-anims/${Moves[ma].toLowerCase().replace(/\_/g, '-')}.json`, stringify(data, { replacer: animReplacer, cmp: animComparator, space: ' ' }));
|
|
})();
|
|
}
|
|
|
|
for (let ca of chargeAnims.keys()) {
|
|
const data = chargeAnims.get(ca);
|
|
(async () => {
|
|
await fs.writeFile(`../public/battle-anims/${chargeAnimNames[chargeAnimIds.indexOf(ca)].replace(/\_/g, '-')}.json`, stringify(data, { replacer: animReplacer, cmp: animComparator, space: ' ' }));
|
|
})();
|
|
}
|
|
|
|
for (let cma of commonAnims.keys()) {
|
|
const data = commonAnims.get(cma);
|
|
(async () => {
|
|
await fs.writeFile(`../public/battle-anims/common-${commonAnimNames[commonAnimIds.indexOf(cma)].replace(/\_/g, '-')}.json`, stringify(data, { replacer: animReplacer, cmp: animComparator, space: ' ' }));
|
|
})();
|
|
}*/
|
|
} |