2023-04-10 19:12:01 +01:00
|
|
|
import BattleScene from "./battle-scene";
|
2023-04-15 06:32:16 +01:00
|
|
|
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult } from "./pokemon";
|
2023-04-18 03:44:41 +01:00
|
|
|
import * as Utils from './utils';
|
2023-04-18 17:30:47 +01:00
|
|
|
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, ConditionalMoveAttr, HitsTagAttr, MissEffectAttr, MoveCategory, MoveEffectAttr, MoveFlags, MoveHitEffectAttr, Moves, MultiHitAttr, OverrideMoveEffectAttr } from "./move";
|
2023-04-10 19:12:01 +01:00
|
|
|
import { Mode } from './ui/ui';
|
|
|
|
import { Command } from "./ui/command-ui-handler";
|
|
|
|
import { Stat } from "./pokemon-stat";
|
2023-04-14 06:08:44 +01:00
|
|
|
import { ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, HitHealModifier } from "./modifier";
|
2023-04-11 16:04:39 +01:00
|
|
|
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
|
|
|
|
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./pokeball";
|
2023-04-15 06:32:16 +01:00
|
|
|
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
2023-04-17 05:46:50 +01:00
|
|
|
import { StatusEffect, getStatusEffectActivationText, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "./status-effect";
|
2023-04-10 19:12:01 +01:00
|
|
|
import { SummaryUiMode } from "./ui/summary-ui-handler";
|
|
|
|
import EvolutionSceneHandler from "./ui/evolution-scene-handler";
|
|
|
|
import { EvolutionPhase } from "./evolution-phase";
|
|
|
|
import { BattlePhase } from "./battle-phase";
|
2023-04-11 04:15:06 +01:00
|
|
|
import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./battle-stat";
|
2023-04-12 05:37:56 +01:00
|
|
|
import { Biome, biomeLinks } from "./biome";
|
2023-04-13 00:09:15 +01:00
|
|
|
import { ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, getModifierTypeOptionsForWave, regenerateModifierPoolThresholds } from "./modifier-type";
|
|
|
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
2023-04-15 06:32:16 +01:00
|
|
|
import { BattleTagLapseType, BattleTagType, HideSpriteTag as HiddenTag } from "./battle-tag";
|
|
|
|
import { getPokemonMessage } from "./messages";
|
2023-04-18 03:44:41 +01:00
|
|
|
import { Starter } from "./ui/starter-select-ui-handler";
|
|
|
|
import { Gender } from "./gender";
|
2023-04-10 19:12:01 +01:00
|
|
|
|
|
|
|
export class SelectStarterPhase extends BattlePhase {
|
|
|
|
constructor(scene: BattleScene) {
|
|
|
|
super(scene);
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
2023-04-13 02:44:12 +01:00
|
|
|
this.scene.sound.play('menu', { loop: true });
|
2023-04-13 00:09:15 +01:00
|
|
|
|
2023-04-18 03:44:41 +01:00
|
|
|
this.scene.ui.setMode(Mode.STARTER_SELECT, (starters: Starter[]) => {
|
2023-04-13 00:09:15 +01:00
|
|
|
const party = this.scene.getParty();
|
|
|
|
const loadPokemonAssets: Promise<void>[] = [];
|
2023-04-18 03:44:41 +01:00
|
|
|
for (let starter of starters) {
|
|
|
|
const starterGender = starter.species.malePercent !== null
|
|
|
|
? !starter.female ? Gender.MALE : Gender.FEMALE
|
|
|
|
: Gender.GENDERLESS;
|
|
|
|
const starterPokemon = new PlayerPokemon(this.scene, starter.species, 5, starter.formIndex, starterGender, starter.shiny);
|
|
|
|
starterPokemon.setVisible(false);
|
|
|
|
party.push(starterPokemon);
|
|
|
|
loadPokemonAssets.push(starterPokemon.loadAssets());
|
2023-04-13 00:09:15 +01:00
|
|
|
}
|
|
|
|
Promise.all(loadPokemonAssets).then(() => {
|
|
|
|
this.scene.ui.clearText();
|
|
|
|
this.scene.ui.setMode(Mode.MESSAGE).then(() => {
|
|
|
|
SoundFade.fadeOut(this.scene.sound.get('menu'), 500, true);
|
|
|
|
this.scene.time.delayedCall(500, () => this.scene.arena.playBgm());
|
|
|
|
this.end();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class EncounterPhase extends BattlePhase {
|
|
|
|
constructor(scene: BattleScene) {
|
|
|
|
super(scene);
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
const battle = this.scene.currentBattle;
|
|
|
|
const enemySpecies = this.scene.arena.randomSpecies(1, battle.enemyLevel);
|
|
|
|
battle.enemyPokemon = new EnemyPokemon(this.scene, enemySpecies, battle.enemyLevel);
|
|
|
|
const enemyPokemon = this.scene.getEnemyPokemon();
|
2023-04-11 04:15:06 +01:00
|
|
|
enemyPokemon.resetSummonData();
|
2023-04-10 19:12:01 +01:00
|
|
|
|
2023-04-18 06:32:26 +01:00
|
|
|
this.scene.gameData.setPokemonSeen(enemyPokemon);
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
console.log(enemyPokemon.species.name, enemyPokemon.species.speciesId, enemyPokemon.stats);
|
|
|
|
|
|
|
|
enemyPokemon.loadAssets().then(() => {
|
|
|
|
this.scene.field.add(enemyPokemon);
|
|
|
|
if (this.scene.getPlayerPokemon().visible)
|
|
|
|
this.scene.field.moveBelow(enemyPokemon, this.scene.getPlayerPokemon());
|
|
|
|
enemyPokemon.tint(0, 0.5);
|
|
|
|
|
|
|
|
this.scene.ui.setMode(Mode.MESSAGE).then(() => this.doEncounter());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
doEncounter() {
|
|
|
|
const enemyPokemon = this.scene.getEnemyPokemon();
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: [ this.scene.arenaEnemy, enemyPokemon, this.scene.arenaPlayer, this.scene.trainer ],
|
|
|
|
x: (_target, _key, value, targetIndex: integer) => targetIndex < 2 ? value + 300 : value - 300,
|
|
|
|
duration: 2000,
|
|
|
|
onComplete: () => {
|
|
|
|
enemyPokemon.untint(100, 'Sine.easeOut');
|
|
|
|
enemyPokemon.cry();
|
|
|
|
enemyPokemon.showInfo();
|
|
|
|
this.scene.ui.showText(`A wild ${enemyPokemon.name} appeared!`, null, () => this.end(), 1500);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
end() {
|
|
|
|
if (this.scene.getEnemyPokemon().shiny)
|
|
|
|
this.scene.unshiftPhase(new ShinySparklePhase(this.scene, false));
|
|
|
|
// TODO: Remove
|
|
|
|
//this.scene.unshiftPhase(new SelectModifierPhase(this.scene));
|
|
|
|
|
|
|
|
super.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class NextEncounterPhase extends EncounterPhase {
|
|
|
|
constructor(scene: BattleScene) {
|
|
|
|
super(scene);
|
|
|
|
}
|
|
|
|
|
|
|
|
doEncounter(): void {
|
|
|
|
const enemyPokemon = this.scene.getEnemyPokemon();
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: [ this.scene.arenaEnemy, this.scene.arenaNextEnemy, enemyPokemon ],
|
|
|
|
x: '+=300',
|
|
|
|
duration: 2000,
|
|
|
|
onComplete: () => {
|
|
|
|
this.scene.arenaEnemy.setX(this.scene.arenaNextEnemy.x);
|
|
|
|
this.scene.arenaNextEnemy.setX(this.scene.arenaNextEnemy.x - 300);
|
|
|
|
enemyPokemon.untint(100, 'Sine.easeOut');
|
|
|
|
enemyPokemon.cry();
|
|
|
|
enemyPokemon.showInfo();
|
|
|
|
this.scene.ui.showText(`A wild ${enemyPokemon.name} appeared!`, null, () => this.end(), 1500);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
end() {
|
|
|
|
if (this.scene.getEnemyPokemon().shiny)
|
|
|
|
this.scene.unshiftPhase(new ShinySparklePhase(this.scene, false));
|
|
|
|
|
|
|
|
this.scene.unshiftPhase(new CheckSwitchPhase(this.scene));
|
|
|
|
|
|
|
|
super.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class NewBiomeEncounterPhase extends NextEncounterPhase {
|
|
|
|
constructor(scene: BattleScene) {
|
|
|
|
super(scene);
|
|
|
|
}
|
|
|
|
|
|
|
|
doEncounter(): void {
|
|
|
|
const enemyPokemon = this.scene.getEnemyPokemon();
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: [ this.scene.arenaEnemy, enemyPokemon ],
|
|
|
|
x: (_target, _key, value, targetIndex: integer) => targetIndex < 2 ? value + 300 : value - 300,
|
|
|
|
duration: 2000,
|
|
|
|
onComplete: () => {
|
|
|
|
enemyPokemon.untint(100, 'Sine.easeOut');
|
|
|
|
enemyPokemon.cry();
|
|
|
|
enemyPokemon.showInfo();
|
|
|
|
this.scene.ui.showText(`A wild ${enemyPokemon.name} appeared!`, null, () => this.end(), 1500);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-12 05:37:56 +01:00
|
|
|
export class SelectBiomePhase extends BattlePhase {
|
2023-04-10 19:12:01 +01:00
|
|
|
constructor(scene: BattleScene) {
|
|
|
|
super(scene);
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
this.scene.arena.fadeOutBgm(2000);
|
|
|
|
|
2023-04-12 05:37:56 +01:00
|
|
|
const currentBiome = this.scene.arena.biomeType;
|
|
|
|
|
|
|
|
const setNextBiome = (nextBiome: Biome) => {
|
|
|
|
this.scene.unshiftPhase(new SwitchBiomePhase(this.scene, nextBiome));
|
|
|
|
this.end();
|
|
|
|
};
|
|
|
|
|
|
|
|
if (Array.isArray(biomeLinks[currentBiome]))
|
|
|
|
this.scene.ui.setMode(Mode.BIOME_SELECT, currentBiome, (biomeIndex: integer) => {
|
|
|
|
this.scene.ui.setMode(Mode.MESSAGE);
|
|
|
|
setNextBiome((biomeLinks[currentBiome] as Biome[])[biomeIndex]);
|
|
|
|
});
|
|
|
|
else
|
|
|
|
setNextBiome(biomeLinks[currentBiome] as Biome)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class SwitchBiomePhase extends BattlePhase {
|
|
|
|
private nextBiome: Biome;
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, nextBiome: Biome) {
|
|
|
|
super(scene);
|
|
|
|
|
|
|
|
this.nextBiome = nextBiome;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: this.scene.arenaEnemy,
|
|
|
|
x: '+=300',
|
|
|
|
duration: 2000,
|
|
|
|
onComplete: () => {
|
|
|
|
this.scene.arenaEnemy.setX(this.scene.arenaEnemy.x - 600);
|
|
|
|
|
2023-04-18 03:44:41 +01:00
|
|
|
this.scene.newArena(this.nextBiome);
|
2023-04-10 19:12:01 +01:00
|
|
|
|
|
|
|
const biomeKey = this.scene.arena.getBiomeKey();
|
|
|
|
const bgTexture = `${biomeKey}_bg`;
|
|
|
|
const playerTexture = `${biomeKey}_a`;
|
|
|
|
const enemyTexture = `${biomeKey}_b`;
|
|
|
|
this.scene.arenaBgTransition.setTexture(bgTexture)
|
|
|
|
this.scene.arenaBgTransition.setAlpha(0);
|
|
|
|
this.scene.arenaBgTransition.setVisible(true);
|
|
|
|
this.scene.arenaPlayerTransition.setTexture(playerTexture)
|
|
|
|
this.scene.arenaPlayerTransition.setAlpha(0);
|
|
|
|
this.scene.arenaPlayerTransition.setVisible(true);
|
|
|
|
|
|
|
|
this.scene.time.delayedCall(1000, () => this.scene.arena.playBgm());
|
|
|
|
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: [ this.scene.arenaBgTransition, this.scene.arenaPlayerTransition ],
|
|
|
|
duration: 1000,
|
|
|
|
delay: 1000,
|
|
|
|
ease: 'Sine.easeInOut',
|
|
|
|
alpha: 1,
|
|
|
|
onComplete: () => {
|
|
|
|
this.scene.arenaBg.setTexture(bgTexture);
|
|
|
|
this.scene.arenaPlayer.setTexture(playerTexture);
|
|
|
|
this.scene.arenaEnemy.setTexture(enemyTexture);
|
|
|
|
this.scene.arenaNextEnemy.setTexture(enemyTexture);
|
|
|
|
this.scene.arenaBgTransition.setVisible(false);
|
|
|
|
this.scene.arenaPlayerTransition.setVisible(false);
|
|
|
|
|
|
|
|
this.end();
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class SummonPhase extends BattlePhase {
|
|
|
|
constructor(scene: BattleScene) {
|
|
|
|
super(scene);
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
this.preSummon();
|
|
|
|
}
|
|
|
|
|
|
|
|
preSummon(): void {
|
|
|
|
this.scene.ui.showText(`Go! ${this.scene.getPlayerPokemon().name}!`);
|
|
|
|
this.scene.trainer.play('trainer_m_pb');
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: this.scene.trainer,
|
|
|
|
x: -36,
|
|
|
|
duration: 1000
|
|
|
|
});
|
|
|
|
this.scene.time.delayedCall(750, () => this.summon());
|
|
|
|
}
|
|
|
|
|
|
|
|
summon(): void {
|
|
|
|
const pokeball = this.scene.add.sprite(36, 80, 'pb', 'pb');
|
|
|
|
pokeball.setVisible(false);
|
|
|
|
pokeball.setOrigin(0.5, 0.625);
|
|
|
|
this.scene.field.add(pokeball);
|
|
|
|
|
|
|
|
const playerPokemon = this.scene.getPlayerPokemon();
|
|
|
|
|
|
|
|
pokeball.setVisible(true);
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: pokeball,
|
|
|
|
ease: 'Cubic.easeOut',
|
|
|
|
duration: 150,
|
|
|
|
x: 54,
|
|
|
|
y: 70,
|
|
|
|
onComplete: () => {
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: pokeball,
|
|
|
|
duration: 500,
|
|
|
|
angle: 1440,
|
|
|
|
x: 100,
|
|
|
|
y: 132,
|
|
|
|
ease: 'Cubic.easeIn',
|
|
|
|
onComplete: () => {
|
|
|
|
this.scene.sound.play('pb_rel');
|
|
|
|
pokeball.destroy();
|
|
|
|
this.scene.add.existing(playerPokemon);
|
|
|
|
this.scene.field.add(playerPokemon);
|
|
|
|
playerPokemon.showInfo();
|
|
|
|
playerPokemon.playAnim();
|
|
|
|
playerPokemon.setVisible(true);
|
|
|
|
playerPokemon.setScale(0.5);
|
|
|
|
playerPokemon.tint(getPokeballTintColor(playerPokemon.pokeball));
|
|
|
|
playerPokemon.untint(250, 'Sine.easeIn');
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: playerPokemon,
|
|
|
|
duration: 250,
|
|
|
|
ease: 'Sine.easeIn',
|
|
|
|
scale: 1,
|
|
|
|
onComplete: () => {
|
|
|
|
playerPokemon.cry();
|
|
|
|
playerPokemon.getSprite().clearTint();
|
|
|
|
playerPokemon.resetSummonData();
|
|
|
|
this.scene.time.delayedCall(1000, () => this.end());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
end() {
|
|
|
|
if (this.scene.getPlayerPokemon().shiny)
|
|
|
|
this.scene.unshiftPhase(new ShinySparklePhase(this.scene, true));
|
|
|
|
|
|
|
|
super.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class SwitchSummonPhase extends SummonPhase {
|
|
|
|
private slotIndex: integer;
|
|
|
|
private doReturn: boolean;
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, slotIndex: integer, doReturn: boolean) {
|
|
|
|
super(scene);
|
|
|
|
|
|
|
|
this.slotIndex = slotIndex;
|
|
|
|
this.doReturn = doReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
preSummon(): void {
|
|
|
|
if (!this.doReturn) {
|
|
|
|
this.switchAndSummon();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const playerPokemon = this.scene.getPlayerPokemon();
|
|
|
|
|
|
|
|
this.scene.ui.showText(`Come back, ${this.scene.getPlayerPokemon().name}!`);
|
|
|
|
this.scene.sound.play('pb_rel');
|
|
|
|
playerPokemon.hideInfo();
|
|
|
|
playerPokemon.tint(getPokeballTintColor(playerPokemon.pokeball), 1, 250, 'Sine.easeIn');
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: playerPokemon,
|
|
|
|
duration: 250,
|
|
|
|
ease: 'Sine.easeIn',
|
|
|
|
scale: 0.5,
|
|
|
|
onComplete: () => {
|
|
|
|
playerPokemon.setVisible(false);
|
|
|
|
this.scene.field.remove(playerPokemon);
|
|
|
|
this.scene.time.delayedCall(750, () => this.switchAndSummon());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
switchAndSummon() {
|
|
|
|
const party = this.scene.getParty();
|
|
|
|
const switchedPokemon = party[this.slotIndex];
|
|
|
|
party[this.slotIndex] = this.scene.getPlayerPokemon();
|
|
|
|
party[0] = switchedPokemon;
|
|
|
|
this.scene.ui.showText(`Go! ${switchedPokemon.name}!`);
|
|
|
|
this.summon();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class CheckSwitchPhase extends BattlePhase {
|
|
|
|
constructor(scene: BattleScene) {
|
|
|
|
super(scene)
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
2023-04-14 04:50:48 +01:00
|
|
|
if (this.scene.field.getAll().indexOf(this.scene.getPlayerPokemon()) === -1) {
|
|
|
|
this.scene.unshiftPhase(new SummonMissingPhase(this.scene));
|
|
|
|
super.end();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-04-15 06:32:16 +01:00
|
|
|
if (this.scene.getPlayerPokemon().getTag(BattleTagType.FRENZY)) {
|
|
|
|
super.end();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
this.scene.ui.showText('Will you switch\nPOKéMON?', null, () => {
|
|
|
|
this.scene.ui.setMode(Mode.CONFIRM, () => {
|
|
|
|
this.scene.unshiftPhase(new SwitchPhase(this.scene, false, true));
|
|
|
|
this.end();
|
2023-04-18 03:44:41 +01:00
|
|
|
}, () => this.end());
|
2023-04-10 19:12:01 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-14 04:50:48 +01:00
|
|
|
export class SummonMissingPhase extends SummonPhase {
|
|
|
|
constructor(scene: BattleScene) {
|
|
|
|
super(scene);
|
|
|
|
}
|
|
|
|
|
|
|
|
preSummon(): void {
|
|
|
|
this.scene.ui.showText(`Go! ${this.scene.getPlayerPokemon().name}!`);
|
|
|
|
this.scene.time.delayedCall(250, () => this.summon());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
export class CommandPhase extends BattlePhase {
|
|
|
|
constructor(scene: BattleScene) {
|
|
|
|
super(scene)
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
2023-04-13 17:16:36 +01:00
|
|
|
const playerPokemon = this.scene.getPlayerPokemon();
|
2023-04-10 19:12:01 +01:00
|
|
|
|
2023-04-13 17:16:36 +01:00
|
|
|
this.scene.currentBattle.addParticipant(playerPokemon);
|
|
|
|
|
|
|
|
playerPokemon.resetTurnData();
|
|
|
|
this.scene.getEnemyPokemon().resetTurnData();
|
|
|
|
|
|
|
|
if (playerPokemon.summonData.moveQueue.length)
|
|
|
|
this.handleCommand(Command.FIGHT, playerPokemon.moveset.findIndex(m => m.moveId === playerPokemon.summonData.moveQueue[0].move));
|
|
|
|
else
|
|
|
|
this.scene.ui.setMode(Mode.COMMAND);
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
|
|
|
|
2023-04-12 00:08:03 +01:00
|
|
|
handleCommand(command: Command, cursor: integer): boolean {
|
2023-04-10 19:12:01 +01:00
|
|
|
const playerPokemon = this.scene.getPlayerPokemon();
|
|
|
|
const enemyPokemon = this.scene.getEnemyPokemon();
|
|
|
|
let success: boolean;
|
2023-04-12 00:08:03 +01:00
|
|
|
|
|
|
|
const playerSpeed = playerPokemon.getBattleStat(Stat.SPD);
|
|
|
|
const enemySpeed = enemyPokemon.getBattleStat(Stat.SPD);
|
|
|
|
|
2023-04-15 22:40:18 +01:00
|
|
|
let isDelayed = (command: Command, playerMove: PokemonMove, enemyMove: PokemonMove) => {
|
|
|
|
switch (command) {
|
|
|
|
case Command.FIGHT:
|
2023-04-18 03:44:41 +01:00
|
|
|
const playerMovePriority = playerMove.getMove().priority;
|
|
|
|
const enemyMovePriority = enemyMove.getMove().priority;
|
|
|
|
if (playerMovePriority !== enemyMovePriority)
|
|
|
|
return playerMovePriority < enemyMovePriority;
|
2023-04-15 22:40:18 +01:00
|
|
|
break;
|
|
|
|
case Command.BALL:
|
|
|
|
case Command.POKEMON:
|
|
|
|
return false;
|
|
|
|
case Command.RUN:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return playerSpeed < enemySpeed || (playerSpeed === enemySpeed && Utils.randInt(2) === 1);
|
|
|
|
};
|
|
|
|
|
|
|
|
let playerMove: PokemonMove;
|
2023-04-10 19:12:01 +01:00
|
|
|
|
|
|
|
switch (command) {
|
|
|
|
case Command.FIGHT:
|
|
|
|
if (playerPokemon.trySelectMove(cursor)) {
|
2023-04-15 22:40:18 +01:00
|
|
|
playerMove = playerPokemon.moveset[cursor];
|
|
|
|
const playerPhase = new PlayerMovePhase(this.scene, playerPokemon, playerMove);
|
2023-04-10 19:12:01 +01:00
|
|
|
this.scene.pushPhase(playerPhase);
|
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Command.BALL:
|
|
|
|
if (cursor < 4) {
|
|
|
|
this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, cursor as PokeballType));
|
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Command.POKEMON:
|
|
|
|
this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, cursor, true));
|
|
|
|
success = true;
|
|
|
|
break;
|
2023-04-14 04:04:51 +01:00
|
|
|
case Command.RUN:
|
2023-04-18 17:32:37 +01:00
|
|
|
//this.scene.unshiftPhase(new MoveAnimTestPhase(this.scene, [ Moves.TELEPORT ]));
|
|
|
|
//success = true;
|
2023-04-14 04:04:51 +01:00
|
|
|
break;
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
2023-04-12 05:37:56 +01:00
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
if (success) {
|
|
|
|
const enemyMove = enemyPokemon.getNextMove();
|
|
|
|
const enemyPhase = new EnemyMovePhase(this.scene, enemyPokemon, enemyMove);
|
2023-04-15 22:40:18 +01:00
|
|
|
if (isDelayed(command, playerMove, enemyMove))
|
2023-04-10 19:12:01 +01:00
|
|
|
this.scene.unshiftPhase(enemyPhase);
|
|
|
|
else
|
|
|
|
this.scene.pushPhase(enemyPhase);
|
2023-04-12 00:08:03 +01:00
|
|
|
|
|
|
|
const statusEffectPhases: PostTurnStatusEffectPhase[] = [];
|
|
|
|
if (playerPokemon.status && playerPokemon.status.isPostTurn())
|
|
|
|
statusEffectPhases.push(new PostTurnStatusEffectPhase(this.scene, true));
|
|
|
|
if (enemyPokemon.status && enemyPokemon.status.isPostTurn()) {
|
|
|
|
const enemyStatusEffectPhase = new PostTurnStatusEffectPhase(this.scene, false);
|
2023-04-15 22:40:18 +01:00
|
|
|
if (isDelayed(command, playerMove, enemyMove))
|
2023-04-12 00:08:03 +01:00
|
|
|
statusEffectPhases.unshift(enemyStatusEffectPhase);
|
|
|
|
else
|
|
|
|
statusEffectPhases.push(enemyStatusEffectPhase);
|
|
|
|
}
|
|
|
|
for (let sef of statusEffectPhases)
|
|
|
|
this.scene.pushPhase(sef);
|
|
|
|
|
2023-04-13 17:16:36 +01:00
|
|
|
this.scene.pushPhase(new TurnEndPhase(this.scene));
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
this.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
end() {
|
|
|
|
this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-13 17:16:36 +01:00
|
|
|
export class TurnEndPhase extends BattlePhase {
|
|
|
|
constructor(scene: BattleScene) {
|
|
|
|
super(scene);
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
2023-04-15 22:40:18 +01:00
|
|
|
const playerPokemon = this.scene.getPlayerPokemon();
|
|
|
|
const enemyPokemon = this.scene.getEnemyPokemon();
|
|
|
|
|
2023-04-18 20:07:10 +01:00
|
|
|
if (playerPokemon) {
|
|
|
|
playerPokemon.lapseTags(BattleTagLapseType.TURN_END);
|
|
|
|
playerPokemon.battleSummonData.turnCount++;
|
|
|
|
}
|
2023-04-15 22:40:18 +01:00
|
|
|
|
2023-04-18 20:07:10 +01:00
|
|
|
if (enemyPokemon) {
|
|
|
|
enemyPokemon.lapseTags(BattleTagLapseType.TURN_END);
|
|
|
|
enemyPokemon.battleSummonData.turnCount++;
|
|
|
|
}
|
2023-04-13 17:16:36 +01:00
|
|
|
|
|
|
|
this.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
export abstract class PokemonPhase extends BattlePhase {
|
|
|
|
protected player: boolean;
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, player: boolean) {
|
|
|
|
super(scene);
|
|
|
|
|
|
|
|
this.player = player;
|
|
|
|
}
|
|
|
|
|
|
|
|
getPokemon() {
|
|
|
|
return this.player ? this.scene.getPlayerPokemon() : this.scene.getEnemyPokemon();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export abstract class PartyMemberPokemonPhase extends PokemonPhase {
|
|
|
|
protected partyMemberIndex: integer;
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, partyMemberIndex: integer) {
|
|
|
|
super(scene, true);
|
|
|
|
|
|
|
|
this.partyMemberIndex = partyMemberIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
getPokemon() {
|
|
|
|
return this.scene.getParty()[this.partyMemberIndex];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-14 06:08:44 +01:00
|
|
|
export class CommonAnimPhase extends PokemonPhase {
|
|
|
|
private anim: CommonAnim;
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, player: boolean, anim: CommonAnim) {
|
|
|
|
super(scene, player);
|
|
|
|
|
|
|
|
this.anim = anim;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
new CommonBattleAnim(this.anim, this.getPokemon()).play(this.scene, () => {
|
|
|
|
this.end();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-15 06:32:16 +01:00
|
|
|
export abstract class MovePhase extends BattlePhase {
|
2023-04-10 19:12:01 +01:00
|
|
|
protected pokemon: Pokemon;
|
|
|
|
protected move: PokemonMove;
|
2023-04-16 23:40:32 +01:00
|
|
|
protected followUp: boolean;
|
|
|
|
protected hasFollowUp: boolean;
|
2023-04-12 00:08:03 +01:00
|
|
|
protected cancelled: boolean;
|
2023-04-10 19:12:01 +01:00
|
|
|
|
2023-04-16 23:40:32 +01:00
|
|
|
constructor(scene: BattleScene, pokemon: Pokemon, move: PokemonMove, followUp?: boolean) {
|
2023-04-10 19:12:01 +01:00
|
|
|
super(scene);
|
|
|
|
|
|
|
|
this.pokemon = pokemon;
|
|
|
|
this.move = move;
|
2023-04-16 23:40:32 +01:00
|
|
|
this.followUp = !!followUp;
|
2023-04-12 00:08:03 +01:00
|
|
|
this.cancelled = false;
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
abstract getEffectPhase(): MoveEffectPhase;
|
|
|
|
|
|
|
|
canMove(): boolean {
|
|
|
|
return !!this.pokemon.hp;
|
|
|
|
}
|
|
|
|
|
2023-04-15 06:32:16 +01:00
|
|
|
cancel(): void {
|
|
|
|
this.cancelled = true;
|
|
|
|
}
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
2023-04-16 23:40:32 +01:00
|
|
|
const target = this.pokemon.isPlayer() ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon();
|
|
|
|
|
|
|
|
if (!this.followUp)
|
|
|
|
this.pokemon.lapseTags(BattleTagLapseType.MOVE);
|
2023-04-15 06:32:16 +01:00
|
|
|
|
2023-04-12 00:08:03 +01:00
|
|
|
const doMove = () => {
|
|
|
|
if (this.cancelled) {
|
|
|
|
this.end();
|
|
|
|
return;
|
|
|
|
}
|
2023-04-16 23:40:32 +01:00
|
|
|
|
2023-04-15 06:32:16 +01:00
|
|
|
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(this.pokemon, ` used\n${this.move.getName()}!`), 500));
|
2023-04-16 23:40:32 +01:00
|
|
|
if (!this.pokemon.summonData.moveQueue.length || !this.pokemon.summonData.moveQueue.shift().ignorePP)
|
2023-04-13 17:16:36 +01:00
|
|
|
this.move.ppUsed++;
|
2023-04-16 23:40:32 +01:00
|
|
|
|
2023-04-15 22:40:18 +01:00
|
|
|
const failed = new Utils.BooleanHolder(false);
|
2023-04-16 23:40:32 +01:00
|
|
|
applyMoveAttrs(ConditionalMoveAttr, this.pokemon, target, this.move.getMove(), failed);
|
2023-04-18 17:30:47 +01:00
|
|
|
if (failed.value) {
|
|
|
|
this.pokemon.summonData.moveHistory.push({ move: this.move.moveId, result: MoveResult.FAILED });
|
2023-04-15 22:40:18 +01:00
|
|
|
this.scene.unshiftPhase(new MessagePhase(this.scene, 'But it failed!'));
|
2023-04-18 17:30:47 +01:00
|
|
|
} else
|
2023-04-15 22:40:18 +01:00
|
|
|
this.scene.unshiftPhase(this.getEffectPhase());
|
|
|
|
|
2023-04-12 00:08:03 +01:00
|
|
|
this.end();
|
|
|
|
};
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
if (!this.canMove()) {
|
|
|
|
this.end();
|
|
|
|
return;
|
|
|
|
}
|
2023-04-12 00:08:03 +01:00
|
|
|
|
2023-04-16 23:40:32 +01:00
|
|
|
if (!this.followUp && this.pokemon.status && !this.pokemon.status.isPostTurn()) {
|
2023-04-12 00:08:03 +01:00
|
|
|
this.pokemon.status.incrementTurn();
|
|
|
|
let activated = false;
|
|
|
|
let healed = false;
|
2023-04-16 23:40:32 +01:00
|
|
|
|
2023-04-12 00:08:03 +01:00
|
|
|
switch (this.pokemon.status.effect) {
|
|
|
|
case StatusEffect.PARALYSIS:
|
|
|
|
if (Utils.randInt(4) === 0) {
|
|
|
|
activated = true;
|
|
|
|
this.cancelled = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case StatusEffect.SLEEP:
|
2023-04-16 23:40:32 +01:00
|
|
|
applyMoveAttrs(BypassSleepAttr, this.pokemon, target, this.move.getMove());
|
2023-04-12 00:08:03 +01:00
|
|
|
healed = this.pokemon.status.turnCount === this.pokemon.status.cureTurn;
|
2023-04-16 23:40:32 +01:00
|
|
|
activated = !healed && !this.pokemon.getTag(BattleTagType.BYPASS_SLEEP);
|
2023-04-12 00:08:03 +01:00
|
|
|
this.cancelled = activated;
|
|
|
|
break;
|
|
|
|
case StatusEffect.FREEZE:
|
|
|
|
healed = Utils.randInt(5) === 0;
|
|
|
|
activated = !healed;
|
|
|
|
this.cancelled = activated;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (activated) {
|
|
|
|
this.scene.unshiftPhase(new MessagePhase(this.scene,
|
2023-04-15 06:32:16 +01:00
|
|
|
getPokemonMessage(this.pokemon, getStatusEffectActivationText(this.pokemon.status.effect))));
|
|
|
|
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, this.pokemon.isPlayer(), CommonAnim.POISON + (this.pokemon.status.effect - 1)));
|
|
|
|
doMove();
|
2023-04-12 00:08:03 +01:00
|
|
|
} else {
|
|
|
|
if (healed) {
|
|
|
|
this.scene.unshiftPhase(new MessagePhase(this.scene,
|
2023-04-15 06:32:16 +01:00
|
|
|
getPokemonMessage(this.pokemon, getStatusEffectHealText(this.pokemon.status.effect))));
|
2023-04-12 00:08:03 +01:00
|
|
|
this.pokemon.resetStatus();
|
|
|
|
this.pokemon.updateInfo(true);
|
|
|
|
}
|
|
|
|
doMove();
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
doMove();
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
2023-04-16 23:40:32 +01:00
|
|
|
|
|
|
|
end() {
|
2023-04-18 20:07:10 +01:00
|
|
|
if (!this.followUp && this.canMove())
|
2023-04-16 23:40:32 +01:00
|
|
|
this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.isPlayer()));
|
|
|
|
|
|
|
|
super.end();
|
|
|
|
}
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export class PlayerMovePhase extends MovePhase {
|
2023-04-16 23:40:32 +01:00
|
|
|
constructor(scene: BattleScene, pokemon: PlayerPokemon, move: PokemonMove, followUp?: boolean) {
|
|
|
|
super(scene, pokemon, move, followUp);
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
getEffectPhase(): MoveEffectPhase {
|
|
|
|
return new PlayerMoveEffectPhase(this.scene, this.move);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class EnemyMovePhase extends MovePhase {
|
2023-04-16 23:40:32 +01:00
|
|
|
constructor(scene: BattleScene, pokemon: EnemyPokemon, move: PokemonMove, followUp?: boolean) {
|
|
|
|
super(scene, pokemon, move, followUp);
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
getEffectPhase(): MoveEffectPhase {
|
|
|
|
return new EnemyMoveEffectPhase(this.scene, this.move);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
abstract class MoveEffectPhase extends PokemonPhase {
|
|
|
|
protected move: PokemonMove;
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, player: boolean, move: PokemonMove) {
|
|
|
|
super(scene, player);
|
|
|
|
|
|
|
|
this.move = move;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
const user = this.getUserPokemon();
|
|
|
|
const target = this.getTargetPokemon();
|
|
|
|
|
2023-04-13 17:16:36 +01:00
|
|
|
const overridden = new Utils.BooleanHolder(false);
|
2023-04-10 21:17:25 +01:00
|
|
|
|
2023-04-14 04:04:51 +01:00
|
|
|
console.log(this.move.getName());
|
|
|
|
|
2023-04-16 05:29:55 +01:00
|
|
|
applyMoveAttrs(OverrideMoveEffectAttr, user, target, this.move.getMove(), overridden).then(() => {
|
2023-04-10 21:17:25 +01:00
|
|
|
|
2023-04-13 17:16:36 +01:00
|
|
|
if (overridden.value) {
|
2023-04-10 21:17:25 +01:00
|
|
|
this.end();
|
2023-04-13 17:16:36 +01:00
|
|
|
return;
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
2023-04-13 17:16:36 +01:00
|
|
|
|
2023-04-15 06:32:16 +01:00
|
|
|
user.lapseTags(BattleTagLapseType.MOVE_EFFECT);
|
2023-04-13 17:16:36 +01:00
|
|
|
|
|
|
|
if (user.turnData.hitsLeft === undefined) {
|
|
|
|
const hitCount = new Utils.IntegerHolder(1);
|
2023-04-16 05:29:55 +01:00
|
|
|
applyMoveAttrs(MultiHitAttr, user, target, this.move.getMove(), hitCount);
|
2023-04-13 17:16:36 +01:00
|
|
|
user.turnData.hitCount = 0;
|
|
|
|
user.turnData.hitsLeft = user.turnData.hitsTotal = hitCount.value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.hitCheck()) {
|
2023-04-15 06:32:16 +01:00
|
|
|
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(user, '\'s\nattack missed!')));
|
2023-04-13 17:16:36 +01:00
|
|
|
user.summonData.moveHistory.push({ move: this.move.moveId, result: MoveResult.MISSED });
|
2023-04-16 05:29:55 +01:00
|
|
|
applyMoveAttrs(MissEffectAttr, user, target, this.move.getMove());
|
2023-04-13 17:16:36 +01:00
|
|
|
this.end();
|
|
|
|
return;
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
2023-04-18 17:30:47 +01:00
|
|
|
|
|
|
|
const isProtected = !this.move.getMove().hasFlag(MoveFlags.IGNORE_PROTECT) && target.lapseTag(BattleTagType.PROTECTED);
|
2023-04-13 17:16:36 +01:00
|
|
|
|
|
|
|
new MoveAnim(this.move.getMove().id as Moves, user, target).play(this.scene, () => {
|
2023-04-18 17:30:47 +01:00
|
|
|
const result = !isProtected ? target.apply(user, this.move) : MoveResult.NO_EFFECT;
|
2023-04-15 06:32:16 +01:00
|
|
|
++user.turnData.hitCount;
|
|
|
|
user.summonData.moveHistory.push({ move: this.move.moveId, result: result });
|
2023-04-16 05:29:55 +01:00
|
|
|
applyMoveAttrs(MoveEffectAttr, user, target, this.move.getMove());
|
2023-04-15 06:32:16 +01:00
|
|
|
// Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present
|
2023-04-18 17:30:47 +01:00
|
|
|
if (!isProtected && target.hp && !this.move.getMove().getAttrs(ChargeAttr).filter(ca => (ca as ChargeAttr).chargeEffect).length)
|
2023-04-16 05:29:55 +01:00
|
|
|
applyMoveAttrs(MoveHitEffectAttr, user, target, this.move.getMove());
|
2023-04-15 06:32:16 +01:00
|
|
|
this.end();
|
2023-04-13 17:16:36 +01:00
|
|
|
});
|
2023-04-10 19:12:01 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-04-10 21:17:25 +01:00
|
|
|
end() {
|
|
|
|
const user = this.getUserPokemon();
|
2023-04-14 04:04:51 +01:00
|
|
|
if (--user.turnData.hitsLeft >= 1 && this.getTargetPokemon().hp)
|
2023-04-10 21:17:25 +01:00
|
|
|
this.scene.unshiftPhase(this.getNewHitPhase());
|
2023-04-13 17:16:36 +01:00
|
|
|
else {
|
|
|
|
if (user.turnData.hitsTotal > 1)
|
|
|
|
this.scene.unshiftPhase(new MessagePhase(this.scene, `Hit ${user.turnData.hitCount} time(s)!`));
|
2023-04-14 06:08:44 +01:00
|
|
|
if (this.player)
|
|
|
|
this.scene.applyModifiers(HitHealModifier, user);
|
2023-04-13 17:16:36 +01:00
|
|
|
}
|
2023-04-10 21:17:25 +01:00
|
|
|
|
|
|
|
super.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
hitCheck(): boolean {
|
2023-04-18 17:30:47 +01:00
|
|
|
if (this.move.getMove().selfTarget)
|
|
|
|
return true;
|
2023-04-13 17:16:36 +01:00
|
|
|
|
2023-04-15 06:32:16 +01:00
|
|
|
const hiddenTag = this.getTargetPokemon().getTag(HiddenTag);
|
2023-04-13 17:16:36 +01:00
|
|
|
if (hiddenTag) {
|
|
|
|
if (!this.move.getMove().getAttrs(HitsTagAttr).filter(hta => (hta as HitsTagAttr).tagType === hiddenTag.tagType).length)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-04-10 21:17:25 +01:00
|
|
|
if (this.move.getMove().category !== MoveCategory.STATUS) {
|
2023-04-11 04:15:06 +01:00
|
|
|
const userAccuracyLevel = this.getUserPokemon().summonData.battleStats[BattleStat.ACC];
|
|
|
|
const targetEvasionLevel = this.getTargetPokemon().summonData.battleStats[BattleStat.EVA];
|
2023-04-10 21:17:25 +01:00
|
|
|
const rand = Utils.randInt(100, 1);
|
|
|
|
let accuracyMultiplier = 1;
|
|
|
|
if (userAccuracyLevel !== targetEvasionLevel) {
|
|
|
|
accuracyMultiplier = userAccuracyLevel > targetEvasionLevel
|
|
|
|
? (3 + Math.min(userAccuracyLevel - targetEvasionLevel, 6)) / 3
|
|
|
|
: 3 / (3 + Math.min(targetEvasionLevel - userAccuracyLevel, 6));
|
|
|
|
}
|
2023-04-18 03:44:41 +01:00
|
|
|
return rand <= this.move.getMove().accuracy * accuracyMultiplier;
|
2023-04-10 21:17:25 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
abstract getUserPokemon(): Pokemon;
|
|
|
|
|
|
|
|
abstract getTargetPokemon(): Pokemon;
|
2023-04-10 21:17:25 +01:00
|
|
|
|
|
|
|
abstract getNewHitPhase(): MoveEffectPhase;
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export class PlayerMoveEffectPhase extends MoveEffectPhase {
|
|
|
|
constructor(scene: BattleScene, move: PokemonMove) {
|
|
|
|
super(scene, true, move);
|
|
|
|
}
|
|
|
|
|
|
|
|
getUserPokemon(): Pokemon {
|
|
|
|
return this.scene.getPlayerPokemon();
|
|
|
|
}
|
|
|
|
|
|
|
|
getTargetPokemon(): Pokemon {
|
|
|
|
/*if (this.move.getMove().category === MoveCategory.STATUS)
|
|
|
|
return this.getUserPokemon();*/
|
|
|
|
return this.scene.getEnemyPokemon();
|
|
|
|
}
|
2023-04-10 21:17:25 +01:00
|
|
|
|
|
|
|
getNewHitPhase() {
|
|
|
|
return new PlayerMoveEffectPhase(this.scene, this.move);
|
|
|
|
}
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export class EnemyMoveEffectPhase extends MoveEffectPhase {
|
|
|
|
constructor(scene: BattleScene, move: PokemonMove) {
|
|
|
|
super(scene, false, move);
|
|
|
|
}
|
|
|
|
|
|
|
|
getUserPokemon(): Pokemon {
|
|
|
|
return this.scene.getEnemyPokemon();
|
|
|
|
}
|
|
|
|
|
|
|
|
getTargetPokemon(): Pokemon {
|
|
|
|
/*if (this.move.getMove().category === MoveCategory.STATUS)
|
|
|
|
return this.getUserPokemon();*/
|
|
|
|
return this.scene.getPlayerPokemon();
|
|
|
|
}
|
2023-04-10 21:17:25 +01:00
|
|
|
|
|
|
|
getNewHitPhase() {
|
|
|
|
return new EnemyMoveEffectPhase(this.scene, this.move);
|
|
|
|
}
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
|
|
|
|
2023-04-16 23:40:32 +01:00
|
|
|
export class MoveEndPhase extends PokemonPhase {
|
|
|
|
constructor(scene: BattleScene, player: boolean) {
|
|
|
|
super(scene, player);
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
this.getPokemon().lapseTags(BattleTagLapseType.AFTER_MOVE);
|
|
|
|
|
|
|
|
this.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-14 04:04:51 +01:00
|
|
|
export class MoveAnimTestPhase extends BattlePhase {
|
|
|
|
private moveQueue: Moves[];
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, moveQueue?: Moves[]) {
|
|
|
|
super(scene);
|
|
|
|
|
|
|
|
this.moveQueue = moveQueue || Utils.getEnumValues(Moves);
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
const moveQueue = this.moveQueue.slice(0);
|
|
|
|
this.playMoveAnim(moveQueue, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
playMoveAnim(moveQueue: Moves[], player: boolean) {
|
|
|
|
const moveId = player ? moveQueue[0] : moveQueue.shift();
|
|
|
|
if (moveId === undefined) {
|
|
|
|
this.playMoveAnim(this.moveQueue.slice(0), true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
initMoveAnim(moveId).then(() => {
|
|
|
|
loadMoveAnimAssets(this.scene, [ moveId ], true)
|
|
|
|
.then(() => {
|
|
|
|
new MoveAnim(moveId, player ? this.scene.getPlayerPokemon() : this.scene.getEnemyPokemon(),
|
|
|
|
player ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon()).play(this.scene, () => {
|
|
|
|
if (player)
|
|
|
|
this.playMoveAnim(moveQueue, false);
|
|
|
|
else
|
|
|
|
this.playMoveAnim(moveQueue, true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-11 04:15:06 +01:00
|
|
|
export class StatChangePhase extends PokemonPhase {
|
|
|
|
private stats: BattleStat[];
|
|
|
|
private levels: integer;
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, player: boolean, stats: BattleStat[], levels: integer) {
|
|
|
|
super(scene, player);
|
|
|
|
|
2023-04-15 06:32:16 +01:00
|
|
|
const allStats = Utils.getEnumValues(BattleStat);
|
|
|
|
this.stats = stats.map(s => s !== BattleStat.RAND ? s : allStats[Utils.randInt(BattleStat.SPD + 1)]);
|
2023-04-11 04:15:06 +01:00
|
|
|
this.levels = levels;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
const pokemon = this.getPokemon();
|
|
|
|
|
|
|
|
const battleStats = this.getPokemon().summonData.battleStats;
|
|
|
|
const relLevels = this.stats.map(stat => (this.levels >= 1 ? Math.min(battleStats[stat] + this.levels, 6) : Math.max(battleStats[stat] + this.levels, -6)) - battleStats[stat]);
|
|
|
|
|
|
|
|
const end = () => {
|
|
|
|
const messages = this.getStatChangeMessages(relLevels);
|
|
|
|
for (let message of messages)
|
|
|
|
this.scene.unshiftPhase(new MessagePhase(this.scene, message));
|
|
|
|
|
|
|
|
for (let stat of this.stats)
|
|
|
|
pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + this.levels, 6), -6);
|
|
|
|
|
|
|
|
console.log(pokemon.summonData.battleStats);
|
|
|
|
|
|
|
|
this.end();
|
|
|
|
};
|
|
|
|
|
|
|
|
if (relLevels.filter(l => l).length) {
|
|
|
|
pokemon.enableMask();
|
|
|
|
const pokemonMaskSprite = pokemon.maskSprite;
|
|
|
|
|
2023-04-13 02:44:12 +01:00
|
|
|
const statSprite = this.scene.add.tileSprite((this.player ? 106 : 236) * 6, ((this.player ? 148 : 84) + (this.levels >= 1 ? 160 : 0)) * 6, 156, 316, 'battle_stats', this.stats.length > 1 ? 'mix' : BattleStat[this.stats[0]].toLowerCase());
|
2023-04-11 04:15:06 +01:00
|
|
|
statSprite.setAlpha(0);
|
|
|
|
statSprite.setScale(6);
|
|
|
|
statSprite.setOrigin(0.5, 1);
|
|
|
|
|
|
|
|
this.scene.sound.play(`stat_${this.levels >= 1 ? 'up' : 'down'}`);
|
|
|
|
|
|
|
|
statSprite.setMask(new Phaser.Display.Masks.BitmapMask(this.scene, pokemonMaskSprite));
|
|
|
|
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: statSprite,
|
|
|
|
duration: 250,
|
|
|
|
alpha: 0.8375,
|
|
|
|
onComplete: () => {
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: statSprite,
|
|
|
|
delay: 1000,
|
|
|
|
duration: 250,
|
|
|
|
alpha: 0
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2023-04-11 14:41:11 +01:00
|
|
|
|
2023-04-11 04:15:06 +01:00
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: statSprite,
|
|
|
|
duration: 1500,
|
|
|
|
y: `${this.levels >= 1 ? '-' : '+'}=${160 * 6}`
|
|
|
|
});
|
|
|
|
|
|
|
|
this.scene.time.delayedCall(1750, () => {
|
|
|
|
pokemon.disableMask();
|
|
|
|
end();
|
|
|
|
});
|
|
|
|
} else
|
|
|
|
end();
|
|
|
|
}
|
|
|
|
|
|
|
|
getStatChangeMessages(relLevels: integer[]): string[] {
|
|
|
|
const messages: string[] = [];
|
|
|
|
|
|
|
|
for (let s = 0; s < this.stats.length; s++)
|
2023-04-15 06:32:16 +01:00
|
|
|
messages.push(getPokemonMessage(this.getPokemon(), `'s ${getBattleStatName(this.stats[s])} ${getBattleStatLevelChangeDescription(Math.abs(relLevels[s]), this.levels >= 1)}!`));
|
2023-04-11 04:15:06 +01:00
|
|
|
return messages;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
export class ObtainStatusEffectPhase extends PokemonPhase {
|
|
|
|
private statusEffect: StatusEffect;
|
2023-04-16 23:40:32 +01:00
|
|
|
private cureTurn: integer;
|
2023-04-10 19:12:01 +01:00
|
|
|
|
2023-04-16 23:40:32 +01:00
|
|
|
constructor(scene: BattleScene, player: boolean, statusEffect: StatusEffect, cureTurn?: integer) {
|
2023-04-10 19:12:01 +01:00
|
|
|
super(scene, player);
|
2023-04-12 00:08:03 +01:00
|
|
|
|
|
|
|
this.statusEffect = statusEffect;
|
2023-04-16 23:40:32 +01:00
|
|
|
this.cureTurn = cureTurn;
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
const pokemon = this.getPokemon();
|
2023-04-12 00:08:03 +01:00
|
|
|
if (!pokemon.status) {
|
|
|
|
if (pokemon.trySetStatus(this.statusEffect)) {
|
2023-04-16 23:40:32 +01:00
|
|
|
if (this.cureTurn)
|
|
|
|
pokemon.status.cureTurn = this.cureTurn;
|
2023-04-12 00:08:03 +01:00
|
|
|
pokemon.updateInfo(true);
|
|
|
|
new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect - 1), pokemon).play(this.scene, () => {
|
2023-04-15 06:32:16 +01:00
|
|
|
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(pokemon, getStatusEffectObtainText(this.statusEffect))));
|
2023-04-12 00:08:03 +01:00
|
|
|
if (pokemon.status.isPostTurn())
|
|
|
|
this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, this.player));
|
|
|
|
this.end();
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (pokemon.status.effect === this.statusEffect)
|
2023-04-15 06:32:16 +01:00
|
|
|
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(pokemon, getStatusEffectOverlapText(this.statusEffect))));
|
2023-04-12 00:08:03 +01:00
|
|
|
this.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class PostTurnStatusEffectPhase extends PokemonPhase {
|
|
|
|
constructor(scene: BattleScene, player: boolean) {
|
|
|
|
super(scene, player);
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
const pokemon = this.getPokemon();
|
|
|
|
if (pokemon.hp && pokemon.status && pokemon.status.isPostTurn()) {
|
|
|
|
pokemon.status.incrementTurn();
|
|
|
|
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => {
|
|
|
|
this.scene.unshiftPhase(new MessagePhase(this.scene,
|
2023-04-15 06:32:16 +01:00
|
|
|
getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect))));
|
2023-04-12 00:08:03 +01:00
|
|
|
switch (pokemon.status.effect) {
|
|
|
|
case StatusEffect.POISON:
|
|
|
|
case StatusEffect.BURN:
|
2023-04-16 23:40:32 +01:00
|
|
|
pokemon.damage(Math.max(pokemon.getMaxHp() >> 3, 1));
|
2023-04-12 00:08:03 +01:00
|
|
|
break;
|
|
|
|
case StatusEffect.TOXIC:
|
2023-04-16 23:40:32 +01:00
|
|
|
pokemon.damage(Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1));
|
2023-04-12 00:08:03 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
pokemon.updateInfo().then(() => this.end());
|
|
|
|
});
|
2023-04-10 19:12:01 +01:00
|
|
|
} else
|
|
|
|
this.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-18 03:44:41 +01:00
|
|
|
export class WeatherPhase extends BattlePhase {
|
2023-04-17 05:46:50 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
export class MessagePhase extends BattlePhase {
|
|
|
|
private text: string;
|
2023-04-12 00:08:03 +01:00
|
|
|
private callbackDelay: integer;
|
2023-04-10 19:12:01 +01:00
|
|
|
private prompt: boolean;
|
|
|
|
|
2023-04-12 00:08:03 +01:00
|
|
|
constructor(scene: BattleScene, text: string, callbackDelay?: integer, prompt?: boolean) {
|
2023-04-10 19:12:01 +01:00
|
|
|
super(scene);
|
|
|
|
|
|
|
|
this.text = text;
|
2023-04-12 00:08:03 +01:00
|
|
|
this.callbackDelay = callbackDelay;
|
2023-04-10 19:12:01 +01:00
|
|
|
this.prompt = prompt;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
2023-04-18 03:44:41 +01:00
|
|
|
this.scene.ui.showText(this.text, null, () => this.end(), this.callbackDelay || (this.prompt ? 0 : 1500), this.prompt);
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-15 06:32:16 +01:00
|
|
|
export class DamagePhase extends PokemonPhase {
|
|
|
|
private damageResult: DamageResult;
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, player: boolean, damageResult?: DamageResult) {
|
|
|
|
super(scene, player);
|
|
|
|
|
|
|
|
this.damageResult = damageResult || MoveResult.EFFECTIVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
switch (this.damageResult) {
|
|
|
|
case MoveResult.EFFECTIVE:
|
|
|
|
this.scene.sound.play('hit');
|
|
|
|
break;
|
|
|
|
case MoveResult.SUPER_EFFECTIVE:
|
|
|
|
this.scene.sound.play('hit_strong');
|
|
|
|
break;
|
|
|
|
case MoveResult.NOT_VERY_EFFECTIVE:
|
|
|
|
this.scene.sound.play('hit_weak');
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-04-18 17:30:47 +01:00
|
|
|
if (this.damageResult !== MoveResult.OTHER) {
|
|
|
|
const flashTimer = this.scene.time.addEvent({
|
|
|
|
delay: 100,
|
|
|
|
repeat: 5,
|
|
|
|
startAt: 200,
|
|
|
|
callback: () => {
|
|
|
|
this.getPokemon().getSprite().setVisible(flashTimer.repeatCount % 2 === 0);
|
|
|
|
if (!flashTimer.repeatCount)
|
|
|
|
this.getPokemon().updateInfo().then(() => this.end());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else
|
|
|
|
this.getPokemon().updateInfo().then(() => this.end());
|
2023-04-15 06:32:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
export class FaintPhase extends PokemonPhase {
|
|
|
|
constructor(scene: BattleScene, player: boolean) {
|
|
|
|
super(scene, player);
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
2023-04-15 06:32:16 +01:00
|
|
|
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(this.getPokemon(), ' fainted!'), null, true));
|
|
|
|
|
|
|
|
if (this.player)
|
2023-04-10 19:12:01 +01:00
|
|
|
this.scene.unshiftPhase(new SwitchPhase(this.scene, true, false));
|
2023-04-15 06:32:16 +01:00
|
|
|
else
|
2023-04-10 19:12:01 +01:00
|
|
|
this.scene.unshiftPhase(new VictoryPhase(this.scene));
|
|
|
|
|
|
|
|
const pokemon = this.getPokemon();
|
2023-04-14 04:04:51 +01:00
|
|
|
|
2023-04-13 17:16:36 +01:00
|
|
|
pokemon.lapseTags(BattleTagLapseType.FAINT);
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
pokemon.faintCry(() => {
|
|
|
|
pokemon.hideInfo();
|
|
|
|
this.scene.sound.play('faint');
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: pokemon,
|
|
|
|
duration: 500,
|
|
|
|
y: pokemon.y + 150,
|
|
|
|
ease: 'Sine.easeIn',
|
|
|
|
onComplete: () => {
|
|
|
|
pokemon.setVisible(false);
|
|
|
|
pokemon.y -= 150;
|
2023-04-16 23:40:32 +01:00
|
|
|
pokemon.trySetStatus(StatusEffect.FAINT);
|
2023-04-15 06:32:16 +01:00
|
|
|
if (pokemon.isPlayer())
|
2023-04-10 19:12:01 +01:00
|
|
|
this.scene.currentBattle.removeFaintedParticipant(pokemon as PlayerPokemon);
|
|
|
|
this.scene.field.remove(pokemon);
|
|
|
|
this.end();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class VictoryPhase extends PokemonPhase {
|
|
|
|
constructor(scene: BattleScene) {
|
|
|
|
super(scene, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
const participantIds = this.scene.currentBattle.playerParticipantIds;
|
|
|
|
const party = this.scene.getParty();
|
|
|
|
const expShareModifier = this.scene.getModifier(ExpShareModifier) as ExpShareModifier;
|
2023-04-16 03:12:59 +01:00
|
|
|
const expValue = this.scene.getEnemyPokemon().getExpValue();
|
2023-04-10 19:12:01 +01:00
|
|
|
for (let pm = 0; pm < party.length; pm++) {
|
|
|
|
const pokemon = party[pm];
|
|
|
|
if (!pokemon.hp)
|
|
|
|
continue;
|
|
|
|
const pId = pokemon.id;
|
|
|
|
const participated = participantIds.has(pId);
|
|
|
|
if (!participated && !expShareModifier)
|
|
|
|
continue;
|
|
|
|
if (pokemon.level < 100) {
|
|
|
|
let expMultiplier = 0;
|
|
|
|
if (participated)
|
|
|
|
expMultiplier += (1 / participantIds.size);
|
|
|
|
if (expShareModifier)
|
|
|
|
expMultiplier += expShareModifier.stackCount * 0.1;
|
|
|
|
console.log(pokemon.species.name, expMultiplier)
|
|
|
|
this.scene.unshiftPhase(new ExpPhase(this.scene, pm, expValue * expMultiplier));
|
|
|
|
}
|
|
|
|
}
|
2023-04-18 18:36:11 +01:00
|
|
|
this.scene.pushPhase(new SelectModifierPhase(this.scene));
|
2023-04-10 19:12:01 +01:00
|
|
|
this.scene.newBattle();
|
|
|
|
|
|
|
|
this.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class SwitchPhase extends BattlePhase {
|
|
|
|
private isModal: boolean;
|
|
|
|
private doReturn: boolean;
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, isModal: boolean, doReturn: boolean) {
|
|
|
|
super(scene);
|
|
|
|
|
|
|
|
this.isModal = isModal;
|
|
|
|
this.doReturn = doReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
2023-04-11 16:04:39 +01:00
|
|
|
this.scene.ui.setMode(Mode.PARTY, this.isModal ? PartyUiMode.FAINT_SWITCH : PartyUiMode.POST_BATTLE_SWITCH, (slotIndex: integer, _option: PartyOption) => {
|
2023-04-10 19:12:01 +01:00
|
|
|
if (slotIndex && slotIndex < 6)
|
|
|
|
this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, slotIndex, this.doReturn));
|
|
|
|
this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end());
|
|
|
|
}, PartyUiHandler.FilterNonFainted);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class ExpPhase extends PartyMemberPokemonPhase {
|
|
|
|
private expValue: number;
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, partyMemberIndex: integer, expValue: number) {
|
|
|
|
super(scene, partyMemberIndex);
|
|
|
|
|
|
|
|
this.expValue = expValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
const pokemon = this.getPokemon();
|
|
|
|
let exp = new Utils.NumberHolder(this.expValue);
|
|
|
|
this.scene.applyModifiers(ExpBoosterModifier, exp);
|
|
|
|
exp.value = Math.floor(exp.value);
|
|
|
|
this.scene.ui.showText(`${pokemon.name} gained\n${exp.value} EXP. Points!`, null, () => {
|
|
|
|
const lastLevel = pokemon.level;
|
|
|
|
let newLevel: integer;
|
|
|
|
pokemon.addExp(exp.value);
|
|
|
|
newLevel = pokemon.level;
|
|
|
|
if (newLevel > lastLevel)
|
|
|
|
this.scene.unshiftPhase(new LevelUpPhase(this.scene, this.partyMemberIndex, lastLevel, newLevel));
|
|
|
|
pokemon.updateInfo().then(() => this.end());
|
|
|
|
}, null, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
export class LevelUpPhase extends PartyMemberPokemonPhase {
|
|
|
|
private lastLevel: integer;
|
|
|
|
private level: integer;
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, partyMemberIndex: integer, lastLevel: integer, level: integer) {
|
|
|
|
super(scene, partyMemberIndex);
|
|
|
|
|
|
|
|
this.lastLevel = lastLevel;
|
|
|
|
this.level = level;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
const pokemon = this.getPokemon();
|
|
|
|
const prevStats = pokemon.stats.slice(0);
|
|
|
|
pokemon.calculateStats();
|
|
|
|
pokemon.updateInfo();
|
2023-04-18 06:32:26 +01:00
|
|
|
this.scene.playSoundWithoutBgm('level_up_fanfare', 1500);
|
2023-04-10 19:12:01 +01:00
|
|
|
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 = 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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class LearnMovePhase extends PartyMemberPokemonPhase {
|
|
|
|
private moveId: Moves;
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, partyMemberIndex: integer, moveId: Moves) {
|
|
|
|
super(scene, partyMemberIndex);
|
|
|
|
|
|
|
|
this.moveId = moveId;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
const pokemon = this.getPokemon();
|
|
|
|
const move = allMoves[this.moveId - 1];
|
|
|
|
|
|
|
|
const existingMoveIndex = pokemon.moveset.findIndex(m => m?.moveId === move.id);
|
|
|
|
|
|
|
|
if (existingMoveIndex > -1) {
|
|
|
|
this.end();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const emptyMoveIndex = pokemon.moveset.length < 4
|
|
|
|
? 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);
|
2023-04-12 00:08:03 +01:00
|
|
|
initMoveAnim(this.moveId).then(() => {
|
2023-04-10 19:12:01 +01:00
|
|
|
loadMoveAnimAssets(this.scene, [ this.moveId ], true)
|
|
|
|
.then(() => {
|
|
|
|
this.scene.ui.setMode(messageMode).then(() => {
|
2023-04-18 06:32:26 +01:00
|
|
|
this.scene.playSoundWithoutBgm('level_up_fanfare', 1500);
|
2023-04-10 19:12:01 +01:00
|
|
|
this.scene.ui.showText(`${pokemon.name} learned\n${Utils.toPokemonUpperCase(move.name)}!`, null, () => this.end(), messageMode === Mode.EVOLUTION_SCENE ? 1000 : null, true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
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(messageMode).then(() => {
|
|
|
|
this.scene.ui.showText(`Stop trying to teach\n${move.name}?`, null, () => {
|
|
|
|
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.setModeWithoutClear(Mode.CONFIRM, () => {
|
|
|
|
this.scene.ui.setMode(messageMode);
|
|
|
|
this.scene.ui.showText('Which move should be forgotten?', null, () => {
|
|
|
|
this.scene.ui.setModeWithoutClear(Mode.SUMMARY, this.getPokemon(), SummaryUiMode.LEARN_MOVE, move, (moveIndex: integer) => {
|
|
|
|
if (moveIndex === 4) {
|
|
|
|
noHandler();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId));
|
|
|
|
this.end();
|
|
|
|
}, null, true);
|
|
|
|
}, null, true);
|
|
|
|
}, null, true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}, null, true);
|
|
|
|
}, noHandler);
|
|
|
|
});
|
|
|
|
}, null, true);
|
|
|
|
}, null, true);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-14 06:08:44 +01:00
|
|
|
export class PokemonHealPhase extends CommonAnimPhase {
|
|
|
|
private hpHealed: integer;
|
2023-04-16 05:29:55 +01:00
|
|
|
private message: string;
|
|
|
|
private showFullHpMessage: boolean;
|
|
|
|
private skipAnim: boolean;
|
2023-04-14 06:08:44 +01:00
|
|
|
|
2023-04-16 05:29:55 +01:00
|
|
|
constructor(scene: BattleScene, player: boolean, hpHealed: integer, message: string, showFullHpMessage: boolean, skipAnim?: boolean) {
|
2023-04-14 06:08:44 +01:00
|
|
|
super(scene, player, CommonAnim.HEALTH_UP);
|
|
|
|
|
|
|
|
this.hpHealed = hpHealed;
|
2023-04-16 05:29:55 +01:00
|
|
|
this.message = message;
|
|
|
|
this.showFullHpMessage = showFullHpMessage;
|
|
|
|
this.skipAnim = !!skipAnim;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
2023-04-16 23:40:32 +01:00
|
|
|
if (!this.skipAnim && this.getPokemon().getHpRatio() < 1)
|
2023-04-16 05:29:55 +01:00
|
|
|
super.start();
|
|
|
|
else
|
|
|
|
this.end();
|
2023-04-14 06:08:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
end() {
|
|
|
|
const pokemon = this.getPokemon();
|
|
|
|
|
2023-04-16 05:29:55 +01:00
|
|
|
const fullHp = pokemon.getHpRatio() >= 1;
|
|
|
|
|
|
|
|
if (!fullHp) {
|
|
|
|
pokemon.hp = Math.min(pokemon.hp + this.hpHealed, pokemon.getMaxHp());
|
|
|
|
pokemon.updateInfo().then(() => super.end());
|
|
|
|
} else if (this.showFullHpMessage)
|
|
|
|
this.message = getPokemonMessage(pokemon, `'s\nHP is full!`);
|
|
|
|
|
|
|
|
if (this.message)
|
|
|
|
this.scene.unshiftPhase(new MessagePhase(this.scene, this.message));
|
|
|
|
|
|
|
|
if (fullHp)
|
|
|
|
super.end();
|
2023-04-14 06:08:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 19:12:01 +01:00
|
|
|
export class AttemptCapturePhase extends BattlePhase {
|
|
|
|
private pokeballType: PokeballType;
|
|
|
|
private pokeball: Phaser.GameObjects.Sprite;
|
|
|
|
private originalY: number;
|
|
|
|
|
|
|
|
constructor(scene: BattleScene, pokeballType: PokeballType) {
|
|
|
|
super(scene);
|
|
|
|
|
|
|
|
this.pokeballType = pokeballType;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
this.scene.pokeballCounts[this.pokeballType]--;
|
|
|
|
|
|
|
|
const pokemon = this.scene.getEnemyPokemon();
|
|
|
|
this.originalY = pokemon.y;
|
|
|
|
|
|
|
|
const _3m = 3 * pokemon.getMaxHp();
|
|
|
|
const _2h = 2 * pokemon.hp;
|
|
|
|
const catchRate = pokemon.species.catchRate;
|
|
|
|
const pokeballMultiplier = getPokeballCatchMultiplier(this.pokeballType);
|
|
|
|
const statusMultiplier = 1;
|
|
|
|
const x = Math.round((((_3m - _2h) * catchRate * pokeballMultiplier) / _3m) * statusMultiplier);
|
|
|
|
const y = Math.round(65536 / Math.sqrt(Math.sqrt(255 / x)));
|
|
|
|
|
|
|
|
const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType);
|
|
|
|
this.pokeball = this.scene.add.sprite(16, 80, 'pb', pokeballAtlasKey);
|
|
|
|
this.pokeball.setOrigin(0.5, 0.625);
|
|
|
|
this.scene.field.add(this.pokeball);
|
|
|
|
|
|
|
|
this.scene.sound.play('pb_throw');
|
|
|
|
this.scene.time.delayedCall(300, () => {
|
|
|
|
this.scene.field.moveBelow(this.pokeball, pokemon);
|
|
|
|
});
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: this.pokeball,
|
|
|
|
x: { value: 236, ease: 'Linear' },
|
|
|
|
y: { value: 16, ease: 'Cubic.easeOut' },
|
|
|
|
duration: 500,
|
|
|
|
onComplete: () => {
|
|
|
|
this.pokeball.setTexture('pb', `${pokeballAtlasKey}_opening`);
|
|
|
|
this.scene.time.delayedCall(17, () => this.pokeball.setTexture('pb', `${pokeballAtlasKey}_open`));
|
|
|
|
this.scene.sound.play('pb_rel');
|
|
|
|
pokemon.tint(getPokeballTintColor(this.pokeballType));
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: pokemon,
|
|
|
|
duration: 250,
|
|
|
|
ease: 'Sine.easeIn',
|
|
|
|
scale: 0.25,
|
|
|
|
y: 20,
|
|
|
|
onComplete: () => {
|
|
|
|
this.pokeball.setTexture('pb', `${pokeballAtlasKey}_opening`);
|
|
|
|
pokemon.setVisible(false);
|
|
|
|
this.scene.sound.play('pb_catch');
|
|
|
|
this.scene.time.delayedCall(17, () => this.pokeball.setTexture('pb', `${pokeballAtlasKey}`));
|
|
|
|
|
|
|
|
const doShake = this.pokeballType !== PokeballType.MASTER_BALL ? () => {
|
|
|
|
let shakeCount = 0;
|
|
|
|
const pbX = this.pokeball.x;
|
|
|
|
const shakeCounter = this.scene.tweens.addCounter({
|
|
|
|
from: 0,
|
|
|
|
to: 1,
|
|
|
|
repeat: 4,
|
|
|
|
yoyo: true,
|
|
|
|
ease: 'Cubic.easeOut',
|
|
|
|
duration: 250,
|
|
|
|
repeatDelay: 500,
|
|
|
|
onUpdate: t => {
|
|
|
|
if (shakeCount && shakeCount < 4) {
|
|
|
|
const value = t.getValue();
|
|
|
|
const directionMultiplier = shakeCount % 2 === 1 ? 1 : -1;
|
|
|
|
this.pokeball.setX(pbX + value * 4 * directionMultiplier);
|
|
|
|
this.pokeball.setAngle(value * 27.5 * directionMultiplier);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
onRepeat: () => {
|
|
|
|
if (shakeCount++ < 3) {
|
|
|
|
if (Utils.randInt(65536) < y)
|
|
|
|
this.scene.sound.play('pb_move');
|
|
|
|
else {
|
|
|
|
shakeCounter.stop();
|
2023-04-11 06:31:18 +01:00
|
|
|
this.failCatch(shakeCount);
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
this.scene.sound.play('pb_lock')
|
|
|
|
},
|
|
|
|
onComplete: () => {
|
|
|
|
this.catch();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} : () => this.catch();
|
|
|
|
|
|
|
|
this.scene.time.delayedCall(250, () => doPokeballBounceAnim(this.scene, this.pokeball, 16, 72, 350, doShake));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-04-11 06:31:18 +01:00
|
|
|
failCatch(shakeCount: integer) {
|
2023-04-10 19:12:01 +01:00
|
|
|
const pokemon = this.scene.getEnemyPokemon();
|
|
|
|
|
|
|
|
this.scene.sound.play('pb_rel');
|
|
|
|
pokemon.setY(this.originalY);
|
|
|
|
pokemon.cry();
|
|
|
|
pokemon.tint(getPokeballTintColor(this.pokeballType));
|
|
|
|
pokemon.setVisible(true);
|
|
|
|
pokemon.untint(250, 'Sine.easeOut');
|
|
|
|
|
|
|
|
const pokeballAtlasKey = getPokeballAtlasKey(this.pokeballType);
|
|
|
|
this.pokeball.setTexture('pb', `${pokeballAtlasKey}_opening`);
|
|
|
|
this.scene.time.delayedCall(17, () => this.pokeball.setTexture('pb', `${pokeballAtlasKey}_open`));
|
|
|
|
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: pokemon,
|
|
|
|
duration: 250,
|
|
|
|
ease: 'Sine.easeOut',
|
|
|
|
scale: 1
|
|
|
|
});
|
|
|
|
|
|
|
|
this.removePb();
|
|
|
|
this.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
catch() {
|
|
|
|
const pokemon = this.scene.getEnemyPokemon();
|
|
|
|
this.scene.unshiftPhase(new VictoryPhase(this.scene));
|
|
|
|
this.scene.ui.showText(`${pokemon.name} was caught!`, null, () => {
|
2023-04-10 21:52:27 +01:00
|
|
|
const end = () => {
|
2023-04-10 19:12:01 +01:00
|
|
|
this.removePb();
|
|
|
|
this.end();
|
2023-04-10 21:52:27 +01:00
|
|
|
};
|
2023-04-11 06:31:18 +01:00
|
|
|
const addToParty = () => {
|
|
|
|
const newPokemon = pokemon.addToParty();
|
|
|
|
pokemon.hp = 0;
|
|
|
|
this.scene.field.remove(pokemon, true);
|
|
|
|
if (newPokemon)
|
|
|
|
newPokemon.loadAssets().then(end);
|
|
|
|
else
|
|
|
|
end();
|
|
|
|
};
|
2023-04-18 18:36:11 +01:00
|
|
|
Promise.all([ pokemon.hideInfo(), this.scene.gameData.setPokemonCaught(pokemon) ]).then(() => {
|
2023-04-18 06:32:26 +01:00
|
|
|
if (this.scene.getParty().length === 6) {
|
|
|
|
const promptRelease = () => {
|
|
|
|
this.scene.ui.showText(`Your party is full.\nRelease a POKéMON to make room for ${pokemon.name}?`, null, () => {
|
|
|
|
this.scene.ui.setMode(Mode.CONFIRM, () => {
|
|
|
|
this.scene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, (slotIndex: integer, _option: PartyOption) => {
|
|
|
|
this.scene.ui.setMode(Mode.MESSAGE).then(() => {
|
|
|
|
if (slotIndex < 6)
|
|
|
|
addToParty();
|
|
|
|
else
|
|
|
|
promptRelease();
|
|
|
|
});
|
2023-04-11 06:31:18 +01:00
|
|
|
});
|
2023-04-18 06:32:26 +01:00
|
|
|
}, () => {
|
|
|
|
this.scene.ui.setMode(Mode.MESSAGE);
|
|
|
|
pokemon.hp = 0;
|
|
|
|
end();
|
2023-04-11 06:31:18 +01:00
|
|
|
});
|
|
|
|
});
|
2023-04-18 06:32:26 +01:00
|
|
|
};
|
|
|
|
promptRelease();
|
|
|
|
} else
|
|
|
|
addToParty();
|
|
|
|
});
|
2023-04-10 19:12:01 +01:00
|
|
|
}, 0, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
removePb() {
|
|
|
|
this.scene.tweens.add({
|
|
|
|
targets: this.pokeball,
|
|
|
|
duration: 250,
|
|
|
|
delay: 250,
|
|
|
|
ease: 'Sine.easeIn',
|
|
|
|
alpha: 0,
|
|
|
|
onComplete: () => this.pokeball.destroy()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class SelectModifierPhase extends BattlePhase {
|
|
|
|
constructor(scene: BattleScene) {
|
|
|
|
super(scene);
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
const party = this.scene.getParty();
|
|
|
|
regenerateModifierPoolThresholds(party);
|
|
|
|
const modifierCount = new Utils.IntegerHolder(3);
|
|
|
|
this.scene.applyModifiers(ExtraModifierModifier, modifierCount);
|
2023-04-12 17:56:37 +01:00
|
|
|
const typeOptions: Array<ModifierTypeOption> = getModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex - 1, modifierCount.value, party);
|
2023-04-10 19:12:01 +01:00
|
|
|
|
|
|
|
const modifierSelectCallback = (cursor: integer) => {
|
|
|
|
if (cursor < 0) {
|
|
|
|
this.scene.ui.setMode(Mode.MESSAGE);
|
|
|
|
super.end();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-04-12 17:56:37 +01:00
|
|
|
const modifierType = typeOptions[cursor].type;
|
2023-04-10 19:12:01 +01:00
|
|
|
if (modifierType instanceof PokemonModifierType) {
|
|
|
|
const pokemonModifierType = modifierType as PokemonModifierType;
|
2023-04-11 16:04:39 +01:00
|
|
|
const isMoveModifier = modifierType instanceof PokemonMoveModifierType;
|
|
|
|
this.scene.ui.setModeWithoutClear(Mode.PARTY, !isMoveModifier ? PartyUiMode.MODIFIER : PartyUiMode.MOVE_MODIFIER, (slotIndex: integer, option: PartyOption) => {
|
2023-04-10 19:12:01 +01:00
|
|
|
if (slotIndex < 6) {
|
|
|
|
this.scene.ui.setMode(Mode.MODIFIER_SELECT);
|
2023-04-12 17:56:37 +01:00
|
|
|
const modifierType = typeOptions[cursor].type;
|
2023-04-11 16:04:39 +01:00
|
|
|
const modifier = !isMoveModifier
|
2023-04-12 17:56:37 +01:00
|
|
|
? modifierType.newModifier(party[slotIndex])
|
|
|
|
: modifierType.newModifier(party[slotIndex], option - PartyOption.MOVE_1);
|
2023-04-11 16:04:39 +01:00
|
|
|
this.scene.addModifier(modifier).then(() => super.end());
|
2023-04-10 19:12:01 +01:00
|
|
|
this.scene.ui.clearText();
|
|
|
|
this.scene.ui.setMode(Mode.MESSAGE);
|
|
|
|
} else
|
2023-04-12 17:56:37 +01:00
|
|
|
this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback);
|
2023-04-11 16:04:39 +01:00
|
|
|
}, pokemonModifierType.selectFilter, modifierType instanceof PokemonMoveModifierType ? (modifierType as PokemonMoveModifierType).moveSelectFilter : undefined);
|
2023-04-10 19:12:01 +01:00
|
|
|
} else {
|
2023-04-12 17:56:37 +01:00
|
|
|
this.scene.addModifier(typeOptions[cursor].type.newModifier()).then(() => super.end());
|
2023-04-10 19:12:01 +01:00
|
|
|
this.scene.ui.clearText();
|
|
|
|
this.scene.ui.setMode(Mode.MESSAGE);
|
|
|
|
}
|
|
|
|
};
|
2023-04-12 17:56:37 +01:00
|
|
|
this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback);
|
2023-04-10 19:12:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class ShinySparklePhase extends PokemonPhase {
|
|
|
|
constructor(scene: BattleScene, player: boolean) {
|
|
|
|
super(scene, player);
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
super.start();
|
|
|
|
|
|
|
|
this.getPokemon().sparkle();
|
|
|
|
this.scene.time.delayedCall(1000, () => this.end());
|
|
|
|
}
|
|
|
|
}
|