Finish evolution logic and sequence

pull/1/head
Flashfyre 2023-04-10 13:54:06 -04:00
parent 824106c2a3
commit 5b097b347b
17 changed files with 693 additions and 361 deletions

View File

@ -3,6 +3,9 @@
## To do
- Title screen
- Starter select screen
- UI
- Get starters from save data caught Pokemon
- Moves
- Move logic
- Can't use when PP consumed
@ -15,7 +18,6 @@
- EXP logic
- Fix algorithm (currently inaccurate)
- Pokemon summary screen
- Move learning when full
- Move remembering (no cost?)
- Capture logic
- Critical capture
@ -24,10 +26,6 @@
- Battle info
- Owned icon
- Status effect indicator
- Evolution
- Evolution screen
- Background (ripped video?)
- Particles
- Modifiers
- PP Up
- Ether/elixir

BIN
public/audio/se/beam.wav Normal file

Binary file not shown.

BIN
public/audio/se/charge.wav Normal file

Binary file not shown.

BIN
public/audio/se/shine.wav Normal file

Binary file not shown.

View File

@ -117,12 +117,13 @@ export function initAutoPlay() {
let nextPartyMemberIndex = -1;
const originalMessageUiHandlerShowText = MessageUiHandler.prototype.showText;
MessageUiHandler.prototype.showText = function (text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean) {
MessageUiHandler.prototype.showText = function (text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) {
if (thisArg.auto) {
delay = 1;
callbackDelay = 0;
promptDelay = 0;
}
originalMessageUiHandlerShowText.apply(this, [ text, delay, callback, callbackDelay, prompt ]);
originalMessageUiHandlerShowText.apply(this, [ text, delay, callback, callbackDelay, prompt, promptDelay ]);
};
const originalMessageUiHandlerShowPrompt = MessageUiHandler.prototype.showPrompt;

View File

@ -6,6 +6,7 @@ import { getGenderSymbol, getGenderColor } from './gender';
export default class BattleInfo extends Phaser.GameObjects.Container {
private player: boolean;
private lastName: string;
private lastHp: integer;
private lastMaxHp: integer;
private lastHpFrame: string;
@ -75,6 +76,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
initInfo(pokemon: Pokemon) {
this.nameText.setText(pokemon.name);
this.lastName = pokemon.name;
const nameSizeTest = addTextObject(this.scene, 0, 0, pokemon.name, TextStyle.BATTLE_INFO);
const nameTextWidth = nameSizeTest.displayWidth;
@ -108,6 +110,17 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
return;
}
if (this.lastName !== pokemon.species.name) {
this.nameText.setText(pokemon.name);
this.lastName = pokemon.name;
const nameSizeTest = addTextObject(this.scene, 0, 0, pokemon.name, TextStyle.BATTLE_INFO);
const nameTextWidth = nameSizeTest.displayWidth;
nameSizeTest.destroy();
this.genderText.setPositionRelative(this.nameText, nameTextWidth, 0);
}
const updatePokemonHp = () => {
const duration = !instant ? Utils.clampInt(Math.abs((this.lastHp) - pokemon.hp) * 5, 250, 5000) : 0;
this.scene.tweens.add({
@ -205,7 +218,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
}
setLevel(level: integer) {
this.levelNumbersContainer.removeAll();
this.levelNumbersContainer.removeAll(true);
const levelStr = level.toString();
for (let i = 0; i < levelStr.length; i++)
this.levelNumbersContainer.add(this.scene.add.image(i * 8, 0, 'numbers', levelStr[i]));
@ -215,7 +228,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
setHpNumbers(hp: integer, maxHp: integer) {
if (!this.player)
return;
this.hpNumbersContainer.removeAll();
this.hpNumbersContainer.removeAll(true);
const hpStr = hp.toString();
const maxHpStr = maxHp.toString();
let offset = 0;

View File

@ -1,19 +1,18 @@
import BattleScene from "./battle-scene";
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove } from "./pokemon";
import * as Utils from './utils';
import { allMoves, Moves as Move, MoveCategory, Moves } from "./move";
import { allMoves, MoveCategory, Moves } from "./move";
import { Mode } from './ui/ui';
import { Command } from "./ui/command-ui-handler";
import { Stat } from "./pokemon-stat";
import { ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, getModifierTypesForWave, ModifierType, PokemonModifierType, regenerateModifierPoolThresholds } from "./modifier";
import PartyUiHandler, { PartyUiMode } from "./ui/party-ui-handler";
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor as getPokeballTintColor, PokeballType } from "./pokeball";
import { pokemonLevelMoves } from "./pokemon-level-moves";
import { MoveAnim, initAnim, loadMoveAnimAssets } from "./battle-anims";
import { StatusEffect } from "./status-effect";
import { SummaryUiMode } from "./ui/summary-ui-handler";
import { Species } from "./species";
import { SpeciesEvolution } from "./pokemon-evolutions";
import EvolutionSceneHandler from "./ui/evolution-scene-handler";
import { EvolutionPhase } from "./evolution-phase";
export class BattlePhase {
protected scene: BattleScene;
@ -63,7 +62,8 @@ export class EncounterPhase extends BattlePhase {
if (this.scene.getPlayerPokemon().visible)
this.scene.field.moveBelow(enemyPokemon, this.scene.getPlayerPokemon());
enemyPokemon.tint(0, 0.5);
this.doEncounter();
this.scene.ui.setMode(Mode.MESSAGE).then(() => this.doEncounter());
});
}
@ -735,7 +735,7 @@ export class ExpPhase extends PartyMemberPokemonPhase {
pokemon.addExp(exp.value);
newLevel = pokemon.level;
if (newLevel > lastLevel)
this.scene.unshiftPhase(new LevelUpPhase(this.scene, this.partyMemberIndex, newLevel));
this.scene.unshiftPhase(new LevelUpPhase(this.scene, this.partyMemberIndex, lastLevel, newLevel));
pokemon.updateInfo().then(() => this.end());
}, null, true);
}
@ -744,11 +744,13 @@ export class ExpPhase extends PartyMemberPokemonPhase {
}
export class LevelUpPhase extends PartyMemberPokemonPhase {
private lastLevel: integer;
private level: integer;
constructor(scene: BattleScene, partyMemberIndex: integer, level: integer) {
constructor(scene: BattleScene, partyMemberIndex: integer, lastLevel: integer, level: integer) {
super(scene, partyMemberIndex);
this.lastLevel = lastLevel;
this.level = level;
}
@ -762,25 +764,20 @@ export class LevelUpPhase extends PartyMemberPokemonPhase {
this.scene.pauseBgm();
this.scene.sound.play('level_up_fanfare');
this.scene.ui.showText(`${this.getPokemon().name} grew to\nLV. ${this.level}!`, null, () => this.scene.ui.getMessageHandler().promptLevelUpStats(prevStats, false, () => this.end()), null, true);
const levelMoves = pokemonLevelMoves[pokemon.species.speciesId];
if (levelMoves) {
for (let lm of levelMoves) {
const level = lm[0];
if (level < this.level)
continue;
else if (level > this.level)
break;
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, lm[1]));
}
}
const levelMoves = this.getPokemon().getLevelMoves(this.lastLevel + 1);
for (let lm of levelMoves)
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, lm));
const evolution = pokemon.getEvolution();
if (evolution)
this.scene.unshiftPhase(new EvolutionPhase(this.scene, this.partyMemberIndex, evolution, this.lastLevel));
this.scene.time.delayedCall(1500, () => this.scene.resumeBgm());
}
}
export class LearnMovePhase extends PartyMemberPokemonPhase {
private moveId: Move;
private moveId: Moves;
constructor(scene: BattleScene, partyMemberIndex: integer, moveId: Move) {
constructor(scene: BattleScene, partyMemberIndex: integer, moveId: Moves) {
super(scene, partyMemberIndex);
this.moveId = moveId;
@ -791,7 +788,6 @@ export class LearnMovePhase extends PartyMemberPokemonPhase {
const pokemon = this.getPokemon();
const move = allMoves[this.moveId - 1];
console.log(move, this.moveId);
const existingMoveIndex = pokemon.moveset.findIndex(m => m?.moveId === move.id);
@ -804,45 +800,52 @@ export class LearnMovePhase extends PartyMemberPokemonPhase {
? pokemon.moveset.length
: pokemon.moveset.findIndex(m => m === null);
const messageMode = this.scene.ui.getHandler() instanceof EvolutionSceneHandler
? Mode.EVOLUTION_SCENE
: Mode.MESSAGE;
if (emptyMoveIndex > -1) {
pokemon.moveset[emptyMoveIndex] = new PokemonMove(this.moveId, 0, 0);
initAnim(this.moveId).then(() => {
loadMoveAnimAssets(this.scene, [ this.moveId ], true)
.then(() => {
this.scene.ui.setMode(Mode.MESSAGE).then(() => {
this.scene.ui.setMode(messageMode).then(() => {
this.scene.pauseBgm();
this.scene.sound.play('level_up_fanfare');
this.scene.ui.showText(`${pokemon.name} learned\n${Utils.toPokemonUpperCase(move.name)}!`, null, () => this.end(), null, true);
this.scene.ui.showText(`${pokemon.name} learned\n${Utils.toPokemonUpperCase(move.name)}!`, null, () => this.end(), messageMode === Mode.EVOLUTION_SCENE ? 1000 : null, true);
this.scene.time.delayedCall(1500, () => this.scene.resumeBgm());
});
});
});
} else {
this.scene.ui.setMode(Mode.MESSAGE).then(() => {
this.scene.ui.setMode(messageMode).then(() => {
this.scene.ui.showText(`${pokemon.name} wants to learn the\nmove ${move.name}.`, null, () => {
this.scene.ui.showText(`However, ${pokemon.name} already\nknows four moves.`, null, () => {
this.scene.ui.showText(`Should a move be deleted and\nreplaced with ${move.name}?`, null, () => {
const noHandler = () => {
this.scene.ui.setMode(Mode.MESSAGE).then(() => {
this.scene.ui.setMode(messageMode).then(() => {
this.scene.ui.showText(`Stop trying to teach\n${move.name}?`, null, () => {
this.scene.ui.setMode(Mode.CONFIRM, () => {
this.scene.ui.setMode(Mode.MESSAGE);
this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => {
this.scene.ui.setMode(messageMode);
this.scene.ui.showText(`${pokemon.name} did not learn the\nmove ${move.name}.`, null, () => this.end(), null, true);
}, () => {
this.scene.ui.setMode(messageMode);
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId));
this.end();
});
});
});
};
this.scene.ui.setMode(Mode.CONFIRM, () => {
this.scene.ui.setMode(Mode.MESSAGE);
this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => {
this.scene.ui.setMode(messageMode);
this.scene.ui.showText('Which move should be forgotten?', null, () => {
this.scene.ui.setMode(Mode.SUMMARY, this.getPokemon(), SummaryUiMode.LEARN_MOVE, move, (moveIndex: integer) => {
this.scene.ui.setModeWithoutClear(Mode.SUMMARY, this.getPokemon(), SummaryUiMode.LEARN_MOVE, move, (moveIndex: integer) => {
if (moveIndex === 4) {
noHandler();
return;
}
this.scene.ui.setMode(Mode.MESSAGE).then(() => {
this.scene.ui.showText('1, 2, and… … … Poof!', null, () => {
this.scene.ui.setMode(messageMode).then(() => {
this.scene.ui.showText('@d{32}1, @d{15}2, and@d{15}@d{15}@d{15}@d{15}@s{pb_bounce_1}Poof!', null, () => {
this.scene.ui.showText(`${pokemon.name} forgot how to\nuse ${pokemon.moveset[moveIndex].getName()}.`, null, () => {
this.scene.ui.showText('And…', null, () => {
pokemon.moveset[moveIndex] = null;
@ -1016,277 +1019,6 @@ export class AttemptCapturePhase extends BattlePhase {
}
}
export class EvolutionPhase extends BattlePhase {
private partyMemberIndex: integer;
private evolution: SpeciesEvolution;
private evolutionContainer: Phaser.GameObjects.Container;
private evolutionBaseBg: Phaser.GameObjects.Image;
private evolutionBg: Phaser.GameObjects.Video;
private evolutionBgOverlay: Phaser.GameObjects.Rectangle;
private pokemonSprite: Phaser.GameObjects.Sprite;
private pokemonTintSprite: Phaser.GameObjects.Sprite;
private pokemonEvoSprite: Phaser.GameObjects.Sprite;
private pokemonEvoTintSprite: Phaser.GameObjects.Sprite;
constructor(scene: BattleScene, partyMemberIndex: integer, evolution: SpeciesEvolution) {
super(scene);
this.partyMemberIndex = partyMemberIndex;
this.evolution = evolution;
}
start() {
super.start();
if (!this.evolution) {
this.end();
return;
}
this.scene.pauseBgm();
this.evolutionContainer = this.scene.add.container(0, 0);
this.scene.field.add(this.evolutionContainer);
this.evolutionBaseBg = this.scene.add.image(0, 0, 'plains_bg');
this.evolutionBaseBg.setOrigin(0, 0);
this.evolutionContainer.add(this.evolutionBaseBg);
this.evolutionBg = this.scene.add.video(0, 0, 'evo_bg').stop();
this.evolutionBg.setOrigin(0, 0);
this.evolutionBg.setScale(0.4359673025);
this.evolutionBg.setVisible(false);
this.evolutionContainer.add(this.evolutionBg);
this.evolutionBgOverlay = this.scene.add.rectangle(0, 0, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6, 0x262626);
this.evolutionBgOverlay.setOrigin(0, 0);
this.evolutionBgOverlay.setAlpha(0);
this.evolutionContainer.add(this.evolutionBgOverlay);
const getPokemonSprite = () => {
return this.scene.add.sprite(this.evolutionBaseBg.displayWidth / 2, this.evolutionBaseBg.displayHeight / 2, `pkmn__sub`);
};
this.evolutionContainer.add((this.pokemonSprite = getPokemonSprite()));
this.evolutionContainer.add((this.pokemonTintSprite = getPokemonSprite()));
this.evolutionContainer.add((this.pokemonEvoSprite = getPokemonSprite()));
this.evolutionContainer.add((this.pokemonEvoTintSprite = getPokemonSprite()));
this.pokemonTintSprite.setAlpha(0);
this.pokemonTintSprite.setTintFill(0xFFFFFF);
this.pokemonEvoSprite.setVisible(false);
this.pokemonEvoTintSprite.setVisible(false);
this.pokemonEvoTintSprite.setTintFill(0xFFFFFF);
const pokemon = this.scene.getParty()[this.partyMemberIndex];
this.pokemonSprite.play(pokemon.getSpriteKey());
this.pokemonTintSprite.play(pokemon.getSpriteKey());
this.pokemonEvoSprite.play(pokemon.getSpriteKey());
this.pokemonEvoTintSprite.play(pokemon.getSpriteKey());
this.scene.ui.showText(`What?\n${pokemon.name} is evolving!`, null, () => {
pokemon.cry();
pokemon.evolve(this.evolution).then(() => {
this.pokemonEvoSprite.play(pokemon.getSpriteKey());
this.pokemonEvoTintSprite.play(pokemon.getSpriteKey());
});
this.scene.time.delayedCall(1000, () => {
this.scene.tweens.add({
targets: this.evolutionBgOverlay,
alpha: 1,
delay: 500,
duration: 1500,
ease: 'Sine.easeOut',
onComplete: () => {
this.scene.time.delayedCall(1000, () => {
this.scene.tweens.add({
targets: this.evolutionBgOverlay,
alpha: 0,
duration: 250,
onComplete: () => this.evolutionBgOverlay.setVisible(false)
});
this.evolutionBg.setVisible(true);
this.evolutionBg.play();
});
this.doSpiralUpward();
this.scene.tweens.addCounter({
from: 0,
to: 1,
duration: 2000,
onUpdate: t => {
this.pokemonTintSprite.setAlpha(t.getValue());
},
onComplete: () => {
this.pokemonSprite.setVisible(false);
this.scene.time.delayedCall(1000, () => {
this.doArcDownward();
this.scene.time.delayedCall(1500, () => {
this.pokemonEvoTintSprite.setScale(0.25);
this.pokemonEvoTintSprite.setVisible(true);
this.doCycle(1).then(() => {
this.scene.sound.play('shiny');
this.pokemonEvoSprite.setVisible(true);
});
});
});
}
})
}
});
//this.scene.sound.play('evolution');
});
}, 1000);
}
sin(index: integer, amplitude: integer) {
return amplitude * Math.sin(index * (Math.PI / 128));
}
cos(index: integer, amplitude: integer) {
return amplitude * Math.cos(index * (Math.PI / 128));
}
doSpiralUpward() {
let f = 0;
this.scene.tweens.addCounter({
repeat: 64,
duration: 1,
useFrames: true,
onRepeat: () => {
if (f < 64) {
if (!(f & 7)) {
for (let i = 0; i < 4; i++)
this.doSpiralUpwardParticle((f & 120) * 2 + i * 64);
}
f++;
}
}
});
}
doArcDownward() {
let f = 0;
this.scene.tweens.addCounter({
repeat: 96,
duration: 1,
useFrames: true,
onRepeat: () => {
if (f < 96) {
if (f < 6) {
for (let i = 0; i < 9; i++)
this.doArcDownParticle(i * 16);
}
f++;
}
}
});
}
doCycle(l: number): Promise<void> {
return new Promise(resolve => {
const isLastCycle = l === 15;
this.scene.tweens.add({
targets: this.pokemonTintSprite,
scale: 0.25,
ease: 'Cubic.easeInOut',
duration: 500 / l,
yoyo: !isLastCycle
});
this.scene.tweens.add({
targets: this.pokemonEvoTintSprite,
scale: 1,
ease: 'Cubic.easeInOut',
duration: 500 / l,
yoyo: !isLastCycle,
onComplete: () => {
if (l < 15)
this.doCycle(l + 0.5).then(() => resolve());
else {
this.pokemonTintSprite.setVisible(false);
resolve();
}
}
});
});
}
doSpiralUpwardParticle(trigIndex: integer) {
const initialX = (this.scene.game.canvas.width / 6) / 2;
const particle = this.scene.add.image(initialX, 0, 'evo_sparkle');
this.evolutionContainer.add(particle);
let f = 0;
let amp = 48;
const particleTimer = this.scene.tweens.addCounter({
repeat: -1,
duration: 1,
useFrames: true,
onRepeat: () => {
updateParticle();
}
});
const updateParticle = () => {
if (!f || particle.y > 8) {
particle.setPosition(initialX, 88 - (f * f) / 80);
particle.y += this.sin(trigIndex, amp) / 4;
particle.x += this.cos(trigIndex, amp);
particle.setScale(1 - (f / 80));
trigIndex += 4;
if (f & 1)
amp--;
f++;
} else {
particle.destroy();
particleTimer.remove();
}
};
updateParticle();
}
doArcDownParticle(trigIndex: integer) {
const initialX = (this.scene.game.canvas.width / 6) / 2;
const particle = this.scene.add.image(initialX, 0, 'evo_sparkle');
particle.setScale(0.5);
this.evolutionContainer.add(particle);
let f = 0;
let amp = 8;
const particleTimer = this.scene.tweens.addCounter({
repeat: -1,
duration: 1,
useFrames: true,
onRepeat: () => {
updateParticle();
}
});
const updateParticle = () => {
if (!f || particle.y < 88) {
particle.setPosition(initialX, 8 + (f * f) / 5);
particle.y += this.sin(trigIndex, amp) / 4;
particle.x += this.cos(trigIndex, amp);
amp = 8 + this.sin(f * 4, 40);
f++;
} else {
particle.destroy();
particleTimer.remove();
}
};
updateParticle();
}
}
export class SelectModifierPhase extends BattlePhase {
constructor(scene: BattleScene) {
super(scene);

View File

@ -162,7 +162,7 @@ export default class BattleScene extends Phaser.Scene {
// Load pokemon-related images
this.loadImage(`pkmn__back__sub`, 'pokemon/back', 'sub.png');
this.loadImage(`pkmn__sub`, 'pokemon', 'sub.png');
this.loadAtlas('shiny', 'effects');
this.loadAtlas('sparkle', 'effects');
this.loadImage('evo_sparkle', 'effects');
this.load.video('evo_bg', 'images/effects/evo_bg.mp4', null, false, true);
@ -184,8 +184,11 @@ export default class BattleScene extends Phaser.Scene {
this.loadSe('flee');
this.loadSe('exp');
this.loadSe('level_up');
this.loadSe('shiny');
this.loadSe('sparkle');
this.loadSe('restore');
this.loadSe('shine');
this.loadSe('charge');
this.loadSe('beam');
this.loadSe('error');
this.loadSe('pb');
@ -257,7 +260,7 @@ export default class BattleScene extends Phaser.Scene {
for (let s = 0; s < 3; s++) {
const playerSpecies = getPokemonSpecies(s === 0 ? Species.TORCHIC : s === 1 ? Species.TREECKO : Species.MUDKIP); //this.randomSpecies();
const playerPokemon = new PlayerPokemon(this, playerSpecies, 16);
const playerPokemon = new PlayerPokemon(this, playerSpecies, 5);
playerPokemon.setVisible(false);
loadPokemonAssets.push(playerPokemon.loadAssets());
@ -345,7 +348,6 @@ export default class BattleScene extends Phaser.Scene {
this.unshiftPhase(new NewBiomeEncounterPhase(this));
}
} else {
this.pushPhase(new EvolutionPhase(this, 0, this.getPlayerPokemon().getEvolution()));
//this.pushPhase(new SelectStarterPhase(this));
this.pushPhase(new EncounterPhase(this));
this.pushPhase(new SummonPhase(this));
@ -356,7 +358,7 @@ export default class BattleScene extends Phaser.Scene {
}
newBiome(): BiomeArena {
const biome = this.currentBattle ? Utils.randInt(20) as Biome : Biome.LAKE;
const biome = this.currentBattle ? Utils.randInt(20) as Biome : Biome.PLAINS;
this.arena = new BiomeArena(this, biome, Biome[biome].toLowerCase());
return this.arena;
}
@ -401,7 +403,13 @@ export default class BattleScene extends Phaser.Scene {
});
}
playBgm(bgmName: string): void {
playBgm(bgmName?: string): void {
if (!bgmName && this.bgm) {
this.bgm.play({
volume: 1
});
return;
}
if (this.bgm && this.bgm.isPlaying)
this.bgm.stop();
this.bgm = this.sound.add(bgmName, { loop: true });
@ -418,6 +426,10 @@ export default class BattleScene extends Phaser.Scene {
this.bgm.resume();
}
fadeOutBgm(destroy?: boolean): void {
this.arena.fadeOutBgm(500, destroy);
}
getCurrentPhase(): BattlePhase {
return this.currentPhase;
}

View File

@ -132,8 +132,11 @@ export class BiomeArena {
this.scene.load.once(Phaser.Loader.Events.COMPLETE, () => this.scene.playBgm(this.bgm));
}
fadeOutBgm(duration: integer) {
SoundFade.fadeOut(this.scene, this.scene.sound.get(this.bgm), duration);
fadeOutBgm(duration: integer, destroy?: boolean) {
if (destroy === undefined)
destroy = true;
const bgm = this.scene.sound.get(this.bgm);
SoundFade.fadeOut(this.scene, bgm, duration, destroy);
}
}

445
src/evolution-phase.ts Normal file
View File

@ -0,0 +1,445 @@
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import { BattlePhase, LearnMovePhase } from "./battle-phase";
import BattleScene from "./battle-scene";
import { SpeciesEvolution } from "./pokemon-evolutions";
import EvolutionSceneHandler from "./ui/evolution-scene-handler";
import * as Utils from "./utils";
import { Mode } from "./ui/ui";
export class EvolutionPhase extends BattlePhase {
private partyMemberIndex: integer;
private evolution: SpeciesEvolution;
private lastLevel: integer;
private evolutionContainer: Phaser.GameObjects.Container;
private evolutionBaseBg: Phaser.GameObjects.Image;
private evolutionBg: Phaser.GameObjects.Video;
private evolutionBgOverlay: Phaser.GameObjects.Rectangle;
private evolutionOverlay: Phaser.GameObjects.Rectangle;
private pokemonSprite: Phaser.GameObjects.Sprite;
private pokemonTintSprite: Phaser.GameObjects.Sprite;
private pokemonEvoSprite: Phaser.GameObjects.Sprite;
private pokemonEvoTintSprite: Phaser.GameObjects.Sprite;
constructor(scene: BattleScene, partyMemberIndex: integer, evolution: SpeciesEvolution, lastLevel: integer) {
super(scene);
this.partyMemberIndex = partyMemberIndex;
this.evolution = evolution;
this.lastLevel = lastLevel;
}
start() {
super.start();
this.scene.ui.setModeForceTransition(Mode.EVOLUTION_SCENE).then(() => {
if (!this.evolution) {
this.end();
return;
}
this.scene.fadeOutBgm(false);
this.evolutionContainer = (this.scene.ui.getHandler() as EvolutionSceneHandler).evolutionContainer;
this.evolutionBaseBg = this.scene.add.image(0, 0, 'plains_bg');
this.evolutionBaseBg.setOrigin(0, 0);
this.evolutionContainer.add(this.evolutionBaseBg);
this.evolutionBg = this.scene.add.video(0, 0, 'evo_bg').stop();
this.evolutionBg.setOrigin(0, 0);
this.evolutionBg.setScale(0.4359673025);
this.evolutionBg.setVisible(false);
this.evolutionContainer.add(this.evolutionBg);
this.evolutionBgOverlay = this.scene.add.rectangle(0, 0, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6, 0x262626);
this.evolutionBgOverlay.setOrigin(0, 0);
this.evolutionBgOverlay.setAlpha(0);
this.evolutionContainer.add(this.evolutionBgOverlay);
const getPokemonSprite = () => {
return this.scene.add.sprite(this.evolutionBaseBg.displayWidth / 2, this.evolutionBaseBg.displayHeight / 2, `pkmn__sub`);
};
this.evolutionContainer.add((this.pokemonSprite = getPokemonSprite()));
this.evolutionContainer.add((this.pokemonTintSprite = getPokemonSprite()));
this.evolutionContainer.add((this.pokemonEvoSprite = getPokemonSprite()));
this.evolutionContainer.add((this.pokemonEvoTintSprite = getPokemonSprite()));
this.pokemonTintSprite.setAlpha(0);
this.pokemonTintSprite.setTintFill(0xFFFFFF);
this.pokemonEvoSprite.setVisible(false);
this.pokemonEvoTintSprite.setVisible(false);
this.pokemonEvoTintSprite.setTintFill(0xFFFFFF);
this.evolutionOverlay = this.scene.add.rectangle(0, -this.scene.game.canvas.height / 6, this.scene.game.canvas.width / 6, this.scene.game.canvas.height / 6, 0xFFFFFF);
this.evolutionOverlay.setOrigin(0, 0);
this.evolutionOverlay.setAlpha(0);
this.scene.fieldUI.add(this.evolutionOverlay);
const pokemon = this.scene.getParty()[this.partyMemberIndex];
const preName = pokemon.name;
this.pokemonSprite.play(pokemon.getSpriteKey());
this.pokemonTintSprite.play(pokemon.getSpriteKey());
this.pokemonEvoSprite.play(pokemon.getSpriteKey());
this.pokemonEvoTintSprite.play(pokemon.getSpriteKey());
this.scene.ui.showText(`What?\n${preName} is evolving!`, null, () => {
pokemon.cry();
pokemon.evolve(this.evolution).then(() => {
this.pokemonEvoSprite.play(pokemon.getSpriteKey());
this.pokemonEvoTintSprite.play(pokemon.getSpriteKey());
});
const levelMoves = pokemon.getLevelMoves(this.lastLevel + 1);
for (let lm of levelMoves)
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, lm));
this.scene.time.delayedCall(1000, () => {
const evolutionBgm = this.scene.sound.add('evolution');
evolutionBgm.play();
this.scene.tweens.add({
targets: this.evolutionBgOverlay,
alpha: 1,
delay: 500,
duration: 1500,
ease: 'Sine.easeOut',
onComplete: () => {
this.scene.time.delayedCall(1000, () => {
this.scene.tweens.add({
targets: this.evolutionBgOverlay,
alpha: 0,
duration: 250
});
this.evolutionBg.setVisible(true);
this.evolutionBg.play();
});
this.scene.sound.play('charge');
this.doSpiralUpward();
this.scene.tweens.addCounter({
from: 0,
to: 1,
duration: 2000,
onUpdate: t => {
this.pokemonTintSprite.setAlpha(t.getValue());
},
onComplete: () => {
this.pokemonSprite.setVisible(false);
this.scene.time.delayedCall(1100, () => {
this.scene.sound.play('beam');
this.doArcDownward();
this.scene.time.delayedCall(1500, () => {
this.pokemonEvoTintSprite.setScale(0.25);
this.pokemonEvoTintSprite.setVisible(true);
this.doCycle(1).then(() => {
this.scene.sound.play('sparkle');
this.pokemonEvoSprite.setVisible(true);
this.doCircleInward();
this.scene.time.delayedCall(900, () => {
this.scene.sound.play('shine');
this.doSpray();
this.scene.tweens.add({
targets: this.evolutionOverlay,
alpha: 1,
duration: 250,
easing: 'Sine.easeIn',
onComplete: () => {
this.evolutionBgOverlay.setAlpha(1);
this.evolutionBg.setVisible(false);
this.scene.tweens.add({
targets: [ this.evolutionOverlay, this.pokemonEvoTintSprite ],
alpha: 0,
duration: 2000,
delay: 150,
easing: 'Sine.easeIn',
onComplete: () => {
this.scene.tweens.add({
targets: this.evolutionBgOverlay,
alpha: 0,
duration: 250,
onComplete: () => {
SoundFade.fadeOut(this.scene, evolutionBgm, 100);
this.scene.time.delayedCall(250, () => {
pokemon.cry();
this.scene.time.delayedCall(1250, () => {
this.scene.sound.play('evolution_fanfare');
this.scene.ui.showText(`Congratulations! Your ${preName}\nevolved into ${pokemon.name}!`, null, () => this.end(), null, true, 3000);
this.scene.time.delayedCall(4250, () => this.scene.playBgm());
});
});
}
});
}
});
}
})
});
});
});
});
}
})
}
});
});
}, 1000);
});
}
sin(index: integer, amplitude: integer): number {
return amplitude * Math.sin(index * (Math.PI / 128));
}
cos(index: integer, amplitude: integer): number {
return amplitude * Math.cos(index * (Math.PI / 128));
}
doSpiralUpward() {
let f = 0;
this.scene.tweens.addCounter({
repeat: 64,
duration: 1,
useFrames: true,
onRepeat: () => {
if (f < 64) {
if (!(f & 7)) {
for (let i = 0; i < 4; i++)
this.doSpiralUpwardParticle((f & 120) * 2 + i * 64);
}
f++;
}
}
});
}
doArcDownward() {
let f = 0;
this.scene.tweens.addCounter({
repeat: 96,
duration: 1,
useFrames: true,
onRepeat: () => {
if (f < 96) {
if (f < 6) {
for (let i = 0; i < 9; i++)
this.doArcDownParticle(i * 16);
}
f++;
}
}
});
}
doCycle(l: number): Promise<void> {
return new Promise(resolve => {
const isLastCycle = l === 15;
this.scene.tweens.add({
targets: this.pokemonTintSprite,
scale: 0.25,
ease: 'Cubic.easeInOut',
duration: 500 / l,
yoyo: !isLastCycle
});
this.scene.tweens.add({
targets: this.pokemonEvoTintSprite,
scale: 1,
ease: 'Cubic.easeInOut',
duration: 500 / l,
yoyo: !isLastCycle,
onComplete: () => {
if (l < 15)
this.doCycle(l + 0.5).then(() => resolve());
else {
this.pokemonTintSprite.setVisible(false);
resolve();
}
}
});
});
}
doCircleInward() {
let f = 0;
this.scene.tweens.addCounter({
repeat: 48,
duration: 1,
useFrames: true,
onRepeat: () => {
if (!f) {
for (let i = 0; i < 16; i++)
this.doCircleInwardParticle(i * 16, 4);
} else if (f === 32) {
for (let i = 0; i < 16; i++)
this.doCircleInwardParticle(i * 16, 8);
}
f++;
}
});
}
doSpray() {
let f = 0;
this.scene.tweens.addCounter({
repeat: 48,
duration: 1,
useFrames: true,
onRepeat: () => {
if (!f) {
for (let i = 0; i < 8; i++)
this.doSprayParticle(i);
} else if (f < 50)
this.doSprayParticle(Utils.randInt(8));
f++;
}
});
}
doSpiralUpwardParticle(trigIndex: integer) {
const initialX = this.evolutionBaseBg.displayWidth / 2;
const particle = this.scene.add.image(initialX, 0, 'evo_sparkle');
this.evolutionContainer.add(particle);
let f = 0;
let amp = 48;
const particleTimer = this.scene.tweens.addCounter({
repeat: -1,
duration: 1,
useFrames: true,
onRepeat: () => {
updateParticle();
}
});
const updateParticle = () => {
if (!f || particle.y > 8) {
particle.setPosition(initialX, 88 - (f * f) / 80);
particle.y += this.sin(trigIndex, amp) / 4;
particle.x += this.cos(trigIndex, amp);
particle.setScale(1 - (f / 80));
trigIndex += 4;
if (f & 1)
amp--;
f++;
} else {
particle.destroy();
particleTimer.remove();
}
};
updateParticle();
}
doArcDownParticle(trigIndex: integer) {
const initialX = this.evolutionBaseBg.displayWidth / 2;
const particle = this.scene.add.image(initialX, 0, 'evo_sparkle');
particle.setScale(0.5);
this.evolutionContainer.add(particle);
let f = 0;
let amp = 8;
const particleTimer = this.scene.tweens.addCounter({
repeat: -1,
duration: 1,
useFrames: true,
onRepeat: () => {
updateParticle();
}
});
const updateParticle = () => {
if (!f || particle.y < 88) {
particle.setPosition(initialX, 8 + (f * f) / 5);
particle.y += this.sin(trigIndex, amp) / 4;
particle.x += this.cos(trigIndex, amp);
amp = 8 + this.sin(f * 4, 40);
f++;
} else {
particle.destroy();
particleTimer.remove();
}
};
updateParticle();
}
doCircleInwardParticle(trigIndex: integer, speed: integer) {
const initialX = this.evolutionBaseBg.displayWidth / 2;
const initialY = this.evolutionBaseBg.displayHeight / 2;
const particle = this.scene.add.image(initialX, initialY, 'evo_sparkle');
this.evolutionContainer.add(particle);
let amp = 120;
const particleTimer = this.scene.tweens.addCounter({
repeat: -1,
duration: 1,
useFrames: true,
onRepeat: () => {
updateParticle();
}
});
const updateParticle = () => {
if (amp > 8) {
particle.setPosition(initialX, initialY);
particle.y += this.sin(trigIndex, amp);
particle.x += this.cos(trigIndex, amp);
amp -= speed;
trigIndex += 4;
} else {
particle.destroy();
particleTimer.remove();
}
};
updateParticle();
}
doSprayParticle(trigIndex: integer) {
const initialX = this.evolutionBaseBg.displayWidth / 2;
const initialY = this.evolutionBaseBg.displayHeight / 2;
const particle = this.scene.add.image(initialX, initialY, 'evo_sparkle');
this.evolutionContainer.add(particle);
let f = 0;
let yOffset = 0;
let speed = 3 - Utils.randInt(8);
let amp = 48 + Utils.randInt(64);
const particleTimer = this.scene.tweens.addCounter({
repeat: -1,
duration: 1,
useFrames: true,
onRepeat: () => {
updateParticle();
}
});
const updateParticle = () => {
if (!(f & 3))
yOffset++;
if (trigIndex < 128) {
particle.setPosition(initialX + (speed * f) / 3, initialY + yOffset);
particle.y += -this.sin(trigIndex, amp);
if (f > 108)
particle.setScale((1 - (f - 108) / 20));
trigIndex++;
f++;
} else {
particle.destroy();
particleTimer.remove();
}
};
updateParticle();
}
}

View File

@ -342,7 +342,7 @@ export class PokemonLevelIncrementModifier extends ConsumablePokemonModifier {
pokemon.levelExp = 0;
const scene = pokemon.scene as BattleScene;
scene.unshiftPhase(new LevelUpPhase(scene, scene.getParty().indexOf(pokemon), pokemon.level));
scene.unshiftPhase(new LevelUpPhase(scene, scene.getParty().indexOf(pokemon), pokemon.level - 1, pokemon.level));
return true;
}

View File

@ -298,15 +298,23 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return null;
}
evolve(evolution: SpeciesEvolution): Promise<void> {
return new Promise(resolve => {
console.log(evolution?.speciesId)
this.species = getPokemonSpecies(evolution.speciesId);
this.loadAssets().then(() => {
this.calculateStats();
this.updateInfo().then(() => resolve());
});
});
getLevelMoves(startingLevel?: integer): Moves[] {
const ret: Moves[] = [];
const levelMoves = pokemonLevelMoves[this.species.speciesId];
if (levelMoves) {
if (!startingLevel)
startingLevel = this.level;
for (let lm of levelMoves) {
const level = lm[0];
if (level < startingLevel)
continue;
else if (level > this.level)
break;
ret.push(lm[1]);
}
}
return ret;
}
generateAndPopulateMoveset(): void {
@ -593,7 +601,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
sparkle(): void {
if (this.shinySparkle) {
this.shinySparkle.play('sparkle');
this.scene.sound.play('shiny');
this.scene.sound.play('sparkle');
}
}
}
@ -631,6 +639,20 @@ export class PlayerPokemon extends Pokemon {
}
}
}
evolve(evolution: SpeciesEvolution): Promise<void> {
return new Promise(resolve => {
this.species = getPokemonSpecies(evolution.speciesId);
this.name = this.species.name.toUpperCase();
this.species.generateIconAnim(this.scene as BattleScene);
this.compatibleTms.splice(0, this.compatibleTms.length);
this.generateCompatibleTms();
this.loadAssets().then(() => {
this.calculateStats();
this.updateInfo().then(() => resolve());
});
});
}
}
export class EnemyPokemon extends Pokemon {

View File

@ -0,0 +1,28 @@
import BattleScene from "../battle-scene";
import { Mode } from "./ui";
import UiHandler from "./uiHandler";
export default class EvolutionSceneHandler extends UiHandler {
public evolutionContainer: Phaser.GameObjects.Container;
constructor(scene: BattleScene) {
super(scene, Mode.EVOLUTION_SCENE);
}
setup() {
this.evolutionContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6);
this.scene.fieldUI.add(this.evolutionContainer);
}
processInput(keyCode: integer) {
this.scene.ui.getMessageHandler().processInput(keyCode);
}
setCursor(_cursor: integer): boolean {
return false;
}
clear() {
this.evolutionContainer.removeAll(true);
}
}

View File

@ -16,9 +16,25 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler {
this.pendingPrompt = false;
}
showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean) {
showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) {
if (delay === null || delay === undefined)
delay = 20;
let delayMap = new Map<integer, integer>();
let soundMap = new Map<integer, string>();
const actionPattern = /@(d|s)\{(.*?)\}/;
let actionMatch: RegExpExecArray;
while ((actionMatch = actionPattern.exec(text))) {
switch (actionMatch[1]) {
case 'd':
delayMap.set(actionMatch.index, parseInt(actionMatch[2]));
break;
case 's':
soundMap.set(actionMatch.index, actionMatch[2]);
break;
}
text = text.slice(0, actionMatch.index) + text.slice(actionMatch.index + actionMatch[2].length + 4);
}
if (this.textTimer) {
this.textTimer.remove();
if (this.textCallbackTimer)
@ -26,7 +42,13 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler {
};
if (prompt) {
const originalCallback = callback;
callback = () => this.showPrompt(originalCallback, callbackDelay);
callback = () => {
const showPrompt = () => this.showPrompt(originalCallback, callbackDelay);
if (promptDelay)
this.scene.time.delayedCall(promptDelay, showPrompt);
else
showPrompt();
};
}
if (delay) {
this.clearText();
@ -35,19 +57,38 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler {
this.textTimer = this.scene.time.addEvent({
delay: delay,
callback: () => {
this.message.setText(text.slice(0, text.length - this.textTimer.repeatCount));
if (callback && !this.textTimer.repeatCount) {
if (callbackDelay && !prompt) {
this.textCallbackTimer = this.scene.time.delayedCall(callbackDelay, () => {
if (this.textCallbackTimer) {
this.textCallbackTimer.destroy();
this.textCallbackTimer = null;
}
const charIndex = text.length - this.textTimer.repeatCount;
const charSound = soundMap.get(charIndex);
const charDelay = delayMap.get(charIndex);
this.message.setText(text.slice(0, charIndex));
const advance = () => {
if (charSound)
this.scene.sound.play(charSound);
if (callback && !this.textTimer.repeatCount) {
if (callbackDelay && !prompt) {
this.textCallbackTimer = this.scene.time.delayedCall(callbackDelay, () => {
if (this.textCallbackTimer) {
this.textCallbackTimer.destroy();
this.textCallbackTimer = null;
}
callback();
});
} else
callback();
});
} else
callback();
}
}
};
if (charDelay) {
this.textTimer.paused = true;
this.scene.tweens.addCounter({
duration: charDelay,
useFrames: true,
onComplete: () => {
this.textTimer.paused = false;
advance();
}
});
} else
advance();
},
repeat: text.length
});

View File

@ -411,6 +411,12 @@ export default class SummaryUiHandler extends UiHandler {
extraRowText.setOrigin(0, 1);
this.extraMoveRowContainer.add(extraRowText);
if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) {
const newMoveTypeIcon = this.scene.add.sprite(0, 0, 'types', Type[this.newMove.type].toLowerCase());
newMoveTypeIcon.setOrigin(0, 1);
this.extraMoveRowContainer.add(newMoveTypeIcon);
}
this.moveRowsContainer = this.scene.add.container(0, 0);
this.movesContainer.add(this.moveRowsContainer);
@ -518,7 +524,24 @@ export default class SummaryUiHandler extends UiHandler {
this.pokemon = null;
this.cursor = -1;
this.newMove = null;
this.moveSelectFunction = null;
if (this.moveSelect) {
this.moveSelect = false;
this.moveSelectFunction = null;
this.extraMoveRowContainer.setVisible(false);
if (this.moveCursorBlinkTimer) {
this.moveCursorBlinkTimer.destroy();
this.moveCursorBlinkTimer = null;
}
if (this.moveCursorObj) {
this.moveCursorObj.destroy();
this.moveCursorObj = null;
}
if (this.selectedMoveCursorObj) {
this.selectedMoveCursorObj.destroy();
this.selectedMoveCursorObj = null;
}
this.hideMoveEffect(true);
}
this.summaryContainer.setVisible(false);
this.summaryPageContainer.setVisible(false);
}

View File

@ -10,7 +10,7 @@ import ModifierSelectUiHandler from './modifier-select-ui-handler';
import BallUiHandler from './ball-ui-handler';
import SummaryUiHandler from './summary-ui-handler';
import StarterSelectUiHandler from './starter-select-ui-handler';
import EvolutionUiHandler from './evolution-ui-handler';
import EvolutionSceneHandler from './evolution-scene-handler';
export enum Mode {
MESSAGE = 0,
@ -21,13 +21,19 @@ export enum Mode {
MODIFIER_SELECT,
PARTY,
SUMMARY,
STARTER_SELECT
STARTER_SELECT,
EVOLUTION_SCENE
};
const transitionModes = [
Mode.PARTY,
Mode.SUMMARY,
Mode.STARTER_SELECT,
Mode.EVOLUTION_SCENE
];
const noTransitionModes = [
Mode.CONFIRM
];
export default class UI extends Phaser.GameObjects.Container {
@ -50,7 +56,8 @@ export default class UI extends Phaser.GameObjects.Container {
new ModifierSelectUiHandler(scene),
new PartyUiHandler(scene),
new SummaryUiHandler(scene),
new StarterSelectUiHandler(scene)
new StarterSelectUiHandler(scene),
new EvolutionSceneHandler(scene)
];
}
@ -79,12 +86,12 @@ export default class UI extends Phaser.GameObjects.Container {
this.getHandler().processInput(keyCode);
}
showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean): void {
showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer): void {
const handler = this.getHandler();
if (handler instanceof MessageUiHandler)
(handler as MessageUiHandler).showText(text, delay, callback, callbackDelay, prompt);
(handler as MessageUiHandler).showText(text, delay, callback, callbackDelay, prompt, promptDelay);
else
this.getMessageHandler().showText(text, delay, callback, callbackDelay, prompt);
this.getMessageHandler().showText(text, delay, callback, callbackDelay, prompt, promptDelay);
}
clearText(): void {
@ -111,20 +118,23 @@ export default class UI extends Phaser.GameObjects.Container {
this.scene.sound.play('error');
}
private setModeInternal(mode: Mode, clear: boolean, args: any[]): Promise<void> {
private setModeInternal(mode: Mode, clear: boolean, forceTransition: boolean, args: any[]): Promise<void> {
return new Promise(resolve => {
if (this.mode === mode) {
if (this.mode === mode && !forceTransition) {
resolve();
return;
}
const doSetMode = () => {
if (clear)
this.getHandler().clear();
this.mode = mode;
this.getHandler().show(args);
if (this.mode !== mode) {
if (clear)
this.getHandler().clear();
this.mode = mode;
this.getHandler().show(args);
}
resolve();
};
if ((transitionModes.indexOf(this.mode) > -1 || transitionModes.indexOf(mode) > -1) && !(this.scene as BattleScene).auto) {
if ((transitionModes.indexOf(this.mode) > -1 || transitionModes.indexOf(mode) > -1)
&& (noTransitionModes.indexOf(this.mode) === -1 && noTransitionModes.indexOf(mode) === -1) && !(this.scene as BattleScene).auto) {
this.transitioning = true;
this.overlay.setAlpha(0);
this.overlay.setVisible(true);
@ -153,10 +163,14 @@ export default class UI extends Phaser.GameObjects.Container {
}
setMode(mode: Mode, ...args: any[]): Promise<void> {
return this.setModeInternal(mode, true, args);
return this.setModeInternal(mode, true, false, args);
}
setModeForceTransition(mode: Mode, ...args: any[]): Promise<void> {
return this.setModeInternal(mode, true, true, args);
}
setModeWithoutClear(mode: Mode, ...args: any[]): Promise<void> {
return this.setModeInternal(mode, false, args);
return this.setModeInternal(mode, false, false, args);
}
}