pokerogue/src/battle-scene.ts

917 lines
29 KiB
TypeScript
Raw Normal View History

2023-03-28 19:54:52 +01:00
import Phaser from 'phaser';
2023-04-20 20:46:05 +01:00
import { Biome } from './data/biome';
2023-04-25 03:32:12 +01:00
import UI from './ui/ui';
import { EncounterPhase, SummonPhase, CommandPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, CheckLoadPhase } from './battle-phases';
2023-04-21 00:44:56 +01:00
import Pokemon, { PlayerPokemon, EnemyPokemon } from './pokemon';
import PokemonSpecies, { allSpecies, getPokemonSpecies, initSpecies } from './data/pokemon-species';
2023-03-28 19:54:52 +01:00
import * as Utils from './utils';
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate } from './modifier/modifier';
2023-04-20 20:46:05 +01:00
import { PokeballType } from './data/pokeball';
import { Species } from './data/species';
import { initAutoPlay } from './system/auto-play';
2023-03-31 04:02:35 +01:00
import { Battle } from './battle';
2023-04-20 20:46:05 +01:00
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from './data/battle-anims';
2023-04-10 19:12:01 +01:00
import { BattlePhase } from './battle-phase';
2023-04-20 20:46:05 +01:00
import { initGameSpeed } from './system/game-speed';
import { Arena } from './arena';
2023-04-20 20:46:05 +01:00
import { GameData } from './system/game-data';
import StarterSelectUiHandler from './ui/starter-select-ui-handler';
2023-04-20 20:46:05 +01:00
import { TextStyle, addTextObject } from './ui/text';
import { Moves, initMoves } from './data/move';
2023-04-21 02:32:48 +01:00
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave } from './modifier/modifier-type';
2023-04-27 06:14:15 +01:00
import AbilityBar from './ui/ability-bar';
import { initAbilities } from './data/ability';
2023-03-28 19:54:52 +01:00
2023-04-09 05:22:14 +01:00
const enableAuto = true;
2023-04-21 19:05:16 +01:00
const quickStart = false;
export const startingLevel = 5;
export const startingWave = 1;
export const startingBiome = Biome.TOWN;
2023-04-09 05:22:14 +01:00
export enum Button {
UP,
DOWN,
LEFT,
RIGHT,
ACTION,
CANCEL,
CYCLE_SHINY,
CYCLE_FORM,
CYCLE_GENDER,
2023-04-26 17:50:21 +01:00
CYCLE_ABILITY,
2023-04-13 00:09:15 +01:00
QUICK_START,
AUTO,
SPEED_UP,
SLOW_DOWN
}
2023-04-28 20:03:42 +01:00
export interface PokeballCounts {
[pb: string]: integer;
}
2023-03-28 19:54:52 +01:00
export default class BattleScene extends Phaser.Scene {
2023-04-09 05:22:14 +01:00
public auto: boolean;
2023-04-12 16:30:47 +01:00
public gameSpeed: integer = 1;
2023-04-21 19:05:16 +01:00
public quickStart: boolean = quickStart;
public finalWave: integer = 200;
2023-03-29 05:31:25 +01:00
2023-04-18 06:32:26 +01:00
public gameData: GameData;
2023-04-10 19:12:01 +01:00
private phaseQueue: BattlePhase[];
private phaseQueuePrepend: BattlePhase[];
private phaseQueuePrependSpliceIndex: integer;
2023-03-28 19:54:52 +01:00
private currentPhase: BattlePhase;
public field: Phaser.GameObjects.Container;
public fieldUI: Phaser.GameObjects.Container;
2023-04-27 06:14:15 +01:00
public abilityBar: AbilityBar;
2023-03-28 19:54:52 +01:00
public arenaBg: Phaser.GameObjects.Image;
2023-03-31 21:04:39 +01:00
public arenaBgTransition: Phaser.GameObjects.Image;
2023-03-28 19:54:52 +01:00
public arenaPlayer: Phaser.GameObjects.Image;
2023-03-31 21:04:39 +01:00
public arenaPlayerTransition: Phaser.GameObjects.Image;
2023-03-28 19:54:52 +01:00
public arenaEnemy: Phaser.GameObjects.Image;
2023-03-31 21:04:39 +01:00
public arenaEnemyTransition: Phaser.GameObjects.Image;
public arenaNextEnemy: Phaser.GameObjects.Image;
public arena: Arena;
2023-03-28 19:54:52 +01:00
public trainer: Phaser.GameObjects.Sprite;
2023-03-31 04:02:35 +01:00
public currentBattle: Battle;
public pokeballCounts: PokeballCounts;
2023-03-28 19:54:52 +01:00
private party: PlayerPokemon[];
2023-04-19 04:54:07 +01:00
private waveCountText: Phaser.GameObjects.Text;
2023-03-28 19:54:52 +01:00
private modifierBar: ModifierBar;
2023-04-21 00:44:56 +01:00
private enemyModifierBar: ModifierBar;
2023-04-10 00:15:21 +01:00
private modifiers: PersistentModifier[];
2023-04-21 00:44:56 +01:00
private enemyModifiers: PokemonHeldItemModifier[];
2023-03-28 19:54:52 +01:00
public uiContainer: Phaser.GameObjects.Container;
public ui: UI;
2023-04-04 01:47:41 +01:00
//public spritePipeline: SpritePipeline;
2023-03-28 19:54:52 +01:00
private bgm: Phaser.Sound.BaseSound;
2023-04-18 06:32:26 +01:00
private bgmResumeTimer: Phaser.Time.TimerEvent;
2023-03-28 19:54:52 +01:00
private buttonKeys: Phaser.Input.Keyboard.Key[][];
2023-03-28 19:54:52 +01:00
private blockInput: boolean;
constructor() {
super('battle');
initSpecies();
initMoves();
initAbilities();
2023-04-18 06:32:26 +01:00
this.gameData = new GameData(this);
2023-03-28 19:54:52 +01:00
this.phaseQueue = [];
this.phaseQueuePrepend = [];
this.phaseQueuePrependSpliceIndex = -1;
2023-03-28 19:54:52 +01:00
}
loadImage(key: string, folder: string, filename?: string) {
if (!filename)
filename = `${key}.png`;
this.load.image(key, `images/${folder}/${filename}`);
}
loadAtlas(key: string, folder: string, filenameRoot?: string) {
if (!filenameRoot)
filenameRoot = key;
if (folder)
folder += '/';
2023-04-10 21:17:25 +01:00
this.load.atlas(key, `images/${folder}${filenameRoot}.png`, `images/${folder}/${filenameRoot}.json`);
2023-03-28 19:54:52 +01:00
}
2023-04-04 01:47:41 +01:00
loadSpritesheet(key: string, folder: string, size: integer, filename?: string) {
if (!filename)
filename = `${key}.png`;
this.load.spritesheet(key, `images/${folder}/${filename}`, { frameWidth: size, frameHeight: size });
}
2023-03-28 19:54:52 +01:00
loadSe(key: string, folder?: string, filenames?: string | string[]) {
if (!filenames)
filenames = `${key}.wav`;
if (!folder)
folder = '';
else
folder += '/';
if (!Array.isArray(filenames))
filenames = [ filenames ];
for (let f of filenames as string[]) {
this.load.audio(key, `audio/se/${folder}${f}`);
}
}
loadBgm(key: string, filename?: string) {
if (!filename)
filename = `${key}.mp3`;
this.load.audio(key, `audio/bgm/${filename}`);
}
preload() {
// Load menu images
this.loadImage('bg', 'ui');
this.loadImage('bg_command', 'ui');
this.loadImage('bg_fight', 'ui');
this.loadAtlas('prompt', 'ui');
this.loadImage('cursor', 'ui');
this.loadImage('pbinfo_player', 'ui');
this.loadImage('pbinfo_enemy', 'ui');
this.loadImage('overlay_lv', 'ui');
this.loadAtlas('numbers', 'ui');
this.loadAtlas('overlay_hp', 'ui');
this.loadImage('overlay_exp', 'ui');
2023-04-18 06:32:26 +01:00
this.loadImage('icon_owned', 'ui');
2023-03-28 19:54:52 +01:00
this.loadImage('level_up_stats', 'ui');
2023-04-27 06:14:15 +01:00
this.loadImage('ability_bar', 'ui');
2023-04-02 03:59:07 +01:00
this.loadImage('ball_window', 'ui');
2023-03-28 19:54:52 +01:00
this.loadImage('boolean_window', 'ui');
this.loadImage('party_bg', 'ui');
this.loadAtlas('party_slot_main', 'ui');
this.loadAtlas('party_slot', 'ui');
this.loadImage('party_slot_overlay_lv', 'ui');
this.loadImage('party_slot_hp_bar', 'ui');
this.loadAtlas('party_slot_hp_overlay', 'ui');
this.loadAtlas('party_pb', 'ui');
this.loadImage('party_message', 'ui');
this.loadImage('party_message_large', 'ui');
this.loadImage('party_message_options', 'ui');
2023-04-21 19:05:16 +01:00
this.loadImage('party_message_options_wide', 'ui');
this.loadImage('party_options_top', 'ui');
this.loadImage('party_options_center', 'ui');
this.loadImage('party_options_bottom', 'ui');
2023-04-21 19:05:16 +01:00
this.loadImage('party_options_wide_top', 'ui');
this.loadImage('party_options_wide_center', 'ui');
this.loadImage('party_options_wide_bottom', 'ui');
2023-03-28 19:54:52 +01:00
this.loadAtlas('party_cancel', 'ui');
this.loadImage('summary_bg', 'ui');
2023-04-06 15:05:12 +01:00
this.loadImage('summary_overlay_shiny', 'ui');
this.loadImage('summary_profile', 'ui');
2023-04-23 21:36:03 +01:00
this.loadImage('summary_stats', 'ui');
this.loadImage('summary_stats_overlay_exp', 'ui');
2023-04-06 15:05:12 +01:00
this.loadImage('summary_moves', 'ui');
this.loadImage('summary_moves_effect', 'ui');
this.loadImage('summary_moves_overlay_row', 'ui');
2023-04-09 01:35:45 +01:00
this.loadImage('summary_moves_overlay_pp', 'ui');
this.loadAtlas('summary_moves_cursor', 'ui');
2023-04-23 23:40:21 +01:00
for (let t = 1; t <= 3; t++)
this.loadImage(`summary_tabs_${t}`, 'ui');
2023-04-21 19:05:16 +01:00
for (let o = 1; o <= 3; o++)
this.loadImage(`option_select_window_${o}`, 'ui');
2023-04-12 05:37:56 +01:00
2023-04-13 00:09:15 +01:00
this.loadImage('starter_select_bg', 'ui');
this.loadImage('starter_select_message', 'ui');
this.loadImage('starter_select_cursor', 'ui');
this.loadImage('starter_select_cursor_highlight', 'ui');
this.loadImage('starter_select_gen_cursor', 'ui');
this.loadImage('starter_select_gen_cursor_highlight', 'ui');
2023-03-28 19:54:52 +01:00
// Load arena images
2023-03-31 04:02:35 +01:00
Utils.getEnumValues(Biome).map(at => {
const atKey = Biome[at].toLowerCase();
2023-03-29 23:55:41 +01:00
this.loadImage(`${atKey}_bg`, 'arenas', `${atKey}_bg.png`);
this.loadImage(`${atKey}_a`, 'arenas', `${atKey}_a.png`);
this.loadImage(`${atKey}_b`, 'arenas', `${atKey}_b.png`);
});
2023-03-28 19:54:52 +01:00
// Load trainer images
this.loadImage('trainer_m', 'trainer');
this.loadAtlas('trainer_m_pb', 'trainer');
// Load pokemon-related images
this.loadImage(`pkmn__back__sub`, 'pokemon/back', 'sub.png');
this.loadImage(`pkmn__sub`, 'pokemon', 'sub.png');
2023-04-11 04:15:06 +01:00
this.loadAtlas('battle_stats', 'effects');
2023-04-10 21:52:27 +01:00
this.loadAtlas('shiny', 'effects');
2023-04-10 12:59:00 +01:00
this.loadImage('evo_sparkle', 'effects');
this.load.video('evo_bg', 'images/effects/evo_bg.mp4', null, false, true);
2023-03-28 19:54:52 +01:00
this.loadAtlas('pb', '');
this.loadAtlas('items', '');
2023-04-01 01:19:57 +01:00
this.loadAtlas('types', '');
this.loadAtlas('statuses', '');
2023-04-09 01:35:45 +01:00
this.loadAtlas('categories', '');
2023-03-28 19:54:52 +01:00
2023-04-26 21:07:29 +01:00
for (let i = 0; i < 7; i++)
2023-03-28 19:54:52 +01:00
this.loadAtlas(`pokemon_icons_${i}`, 'ui');
this.loadSe('select');
this.loadSe('menu_open');
this.loadSe('hit');
this.loadSe('hit_strong');
this.loadSe('hit_weak');
2023-04-11 04:15:06 +01:00
this.loadSe('stat_up');
this.loadSe('stat_down');
2023-03-28 19:54:52 +01:00
this.loadSe('faint');
this.loadSe('flee');
2023-04-12 17:48:02 +01:00
this.loadSe('low_hp');
2023-03-28 19:54:52 +01:00
this.loadSe('exp');
this.loadSe('level_up');
2023-04-10 18:54:06 +01:00
this.loadSe('sparkle');
2023-03-28 19:54:52 +01:00
this.loadSe('restore');
2023-04-10 18:54:06 +01:00
this.loadSe('shine');
this.loadSe('charge');
this.loadSe('beam');
2023-04-12 17:48:02 +01:00
this.loadSe('upgrade');
2023-03-28 19:54:52 +01:00
this.loadSe('error');
this.loadSe('pb');
this.loadSe('pb_rel');
this.loadSe('pb_throw');
this.loadSe('pb_bounce_1');
this.loadSe('pb_bounce_2');
this.loadSe('pb_move');
this.loadSe('pb_catch');
this.loadSe('pb_lock');
2023-04-13 00:09:15 +01:00
this.loadBgm('menu');
2023-03-28 19:54:52 +01:00
this.loadBgm('level_up_fanfare');
2023-04-10 12:59:00 +01:00
this.loadBgm('evolution');
this.loadBgm('evolution_fanfare');
2023-04-04 01:47:41 +01:00
2023-04-06 16:30:22 +01:00
//this.load.glsl('sprite', 'shaders/sprite.frag');
2023-04-04 01:47:41 +01:00
populateAnims();
2023-03-28 19:54:52 +01:00
}
create() {
2023-04-12 16:30:47 +01:00
initGameSpeed.apply(this);
this.setupControls();
2023-03-28 19:54:52 +01:00
this.load.setBaseURL();
2023-04-04 01:47:41 +01:00
//this.spritePipeline = (this.renderer as Phaser.Renderer.WebGL.WebGLRenderer).pipelines.get('Sprite') as SpritePipeline;
this.time.delayedCall(20, () => this.launchBattle());
}
update() {
this.checkInput();
}
launchBattle() {
2023-03-28 19:54:52 +01:00
const field = this.add.container(0, 0);
field.setScale(6);
this.field = field;
const fieldUI = this.add.container(0, this.game.canvas.height);
2023-04-11 04:15:06 +01:00
fieldUI.setDepth(1);
2023-03-28 19:54:52 +01:00
fieldUI.setScale(6);
this.fieldUI = fieldUI;
const uiContainer = this.add.container(0, 0);
2023-04-11 04:15:06 +01:00
uiContainer.setDepth(2);
2023-03-28 19:54:52 +01:00
uiContainer.setScale(6);
this.uiContainer = uiContainer;
this.modifiers = [];
2023-04-21 00:44:56 +01:00
this.enemyModifiers = [];
2023-03-28 19:54:52 +01:00
this.modifierBar = new ModifierBar(this);
2023-03-29 05:31:25 +01:00
this.add.existing(this.modifierBar);
uiContainer.add(this.modifierBar);
2023-03-28 19:54:52 +01:00
2023-04-21 00:44:56 +01:00
this.enemyModifierBar = new ModifierBar(this, true);
this.add.existing(this.enemyModifierBar);
uiContainer.add(this.enemyModifierBar);
2023-04-27 06:14:15 +01:00
this.abilityBar = new AbilityBar(this);
this.abilityBar.setup();
this.fieldUI.add(this.abilityBar);
this.waveCountText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, startingWave.toString(), TextStyle.BATTLE_INFO);
2023-04-19 04:54:07 +01:00
this.waveCountText.setOrigin(1, 0);
2023-04-21 00:44:56 +01:00
this.updateWaveCountPosition();
2023-04-19 04:54:07 +01:00
this.fieldUI.add(this.waveCountText);
2023-03-28 19:54:52 +01:00
this.party = [];
2023-03-29 05:31:25 +01:00
let loadPokemonAssets = [];
this.quickStart = this.quickStart || this.isButtonPressed(Button.QUICK_START);
this.arenaBg = this.add.sprite(0, 0, 'plains_bg');
this.arenaBgTransition = this.add.sprite(0, 0, `plains_bg`);
this.arenaPlayer = this.add.sprite(0, 0, `plains_a`);
this.arenaPlayerTransition = this.add.sprite(0, 0, `plains_a`);
this.arenaEnemy = this.add.sprite(0, 0, `plains_b`);
this.arenaNextEnemy = this.add.sprite(0, 0, `plains_b`);
2023-04-12 05:37:56 +01:00
this.arenaBgTransition.setVisible(false);
this.arenaPlayerTransition.setVisible(false);
[this.arenaBg, this.arenaBgTransition, this.arenaPlayer, this.arenaPlayerTransition, this.arenaEnemy, this.arenaNextEnemy].forEach(a => {
a.setOrigin(0, 0);
field.add(a);
});
2023-04-13 00:09:15 +01:00
if (this.quickStart) {
for (let s = 0; s < 3; s++) {
const playerSpecies = getPokemonSpecies((getPokemonSpecies(s === 0 ? Species.TORCHIC : s === 1 ? Species.TREECKO : Species.MUDKIP)).getSpeciesForLevel(startingLevel, true));
2023-04-23 06:03:09 +01:00
const playerPokemon = new PlayerPokemon(this, playerSpecies, startingLevel, 0, 0);
2023-04-13 00:09:15 +01:00
playerPokemon.setVisible(false);
loadPokemonAssets.push(playerPokemon.loadAssets());
2023-03-28 19:54:52 +01:00
2023-04-13 00:09:15 +01:00
this.party.push(playerPokemon);
}
2023-03-28 19:54:52 +01:00
}
const trainerPbFrameNames = this.anims.generateFrameNames('trainer_m_pb', { zeroPad: 2, start: 1, end: 12 });
this.anims.create({
key: 'trainer_m_pb',
frames: trainerPbFrameNames,
frameRate: 16
});
const trainer = this.add.sprite(0, 0, 'trainer_m');
2023-03-28 19:54:52 +01:00
trainer.setOrigin(0.5, 1);
field.add(trainer);
this.trainer = trainer;
this.anims.create({
key: 'prompt',
frames: this.anims.generateFrameNumbers('prompt', { start: 1, end: 4 }),
frameRate: 6,
repeat: -1,
showOnStart: true
});
this.reset();
2023-03-28 19:54:52 +01:00
const ui = new UI(this);
this.uiContainer.add(ui);
this.ui = ui;
ui.setup();
2023-04-19 23:19:55 +01:00
Promise.all([
Promise.all(loadPokemonAssets),
initCommonAnims().then(() => loadCommonAnimAssets(this, true)),
initMoveAnim(Moves.STRUGGLE).then(() => loadMoveAnimAssets(this, [ Moves.STRUGGLE ], true))
]).then(() => {
2023-04-09 05:22:14 +01:00
if (enableAuto)
initAutoPlay.apply(this);
2023-03-29 05:31:25 +01:00
2023-03-31 21:04:39 +01:00
this.newBattle();
2023-03-29 05:31:25 +01:00
this.shiftPhase();
});
2023-03-28 19:54:52 +01:00
}
setupControls() {
const keyCodes = Phaser.Input.Keyboard.KeyCodes;
const keyConfig = {
[Button.UP]: [keyCodes.UP, keyCodes.W],
[Button.DOWN]: [keyCodes.DOWN, keyCodes.S],
[Button.LEFT]: [keyCodes.LEFT, keyCodes.A],
[Button.RIGHT]: [keyCodes.RIGHT, keyCodes.D],
[Button.ACTION]: [keyCodes.ENTER, keyCodes.SPACE, keyCodes.Z],
[Button.CANCEL]: [keyCodes.BACKSPACE, keyCodes.ESC, keyCodes.X],
[Button.CYCLE_SHINY]: [keyCodes.R],
[Button.CYCLE_FORM]: [keyCodes.F],
[Button.CYCLE_GENDER]: [keyCodes.G],
2023-04-26 17:50:21 +01:00
[Button.CYCLE_ABILITY]: [keyCodes.E],
2023-04-13 00:09:15 +01:00
[Button.QUICK_START]: [keyCodes.Q],
[Button.AUTO]: [keyCodes.F2],
[Button.SPEED_UP]: [keyCodes.PLUS],
[Button.SLOW_DOWN]: [keyCodes.MINUS]
};
this.buttonKeys = [];
for (let b of Utils.getEnumValues(Button)) {
const keys: Phaser.Input.Keyboard.Key[] = [];
if (keyConfig.hasOwnProperty(b)) {
for (let k of keyConfig[b])
keys.push(this.input.keyboard.addKey(k));
}
this.buttonKeys[b] = keys;
}
2023-03-28 19:54:52 +01:00
}
getParty(): PlayerPokemon[] {
return this.party;
}
2023-04-21 00:44:56 +01:00
getEnemyParty(): EnemyPokemon[] {
return this.getEnemyPokemon() ? [ this.getEnemyPokemon() ] : [];
}
2023-03-28 19:54:52 +01:00
getPlayerPokemon(): PlayerPokemon {
return this.getParty()[0];
}
getEnemyPokemon(): EnemyPokemon {
return this.currentBattle?.enemyPokemon;
}
2023-04-23 03:14:53 +01:00
getPokemonById(pokemonId: integer): Pokemon {
const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId);
return findInParty(this.getParty()) || findInParty(this.getEnemyParty());
}
reset(): void {
this.pokeballCounts = Object.fromEntries(Utils.getEnumValues(PokeballType).filter(p => p <= PokeballType.MASTER_BALL).map(t => [ t, 0 ]));
2023-04-25 03:32:12 +01:00
this.pokeballCounts[PokeballType.POKEBALL] += 5;
this.modifiers = [];
this.enemyModifiers = [];
this.modifierBar.removeAll(true);
this.enemyModifierBar.removeAll(true);
for (let p of this.getParty())
p.destroy();
this.party = [];
for (let p of this.getEnemyParty())
p.destroy();
this.currentBattle = null;
this.waveCountText.setText(startingWave.toString());
2023-04-28 20:03:42 +01:00
this.waveCountText.setVisible(false);
2023-04-28 20:03:42 +01:00
this.newArena(startingBiome, true);
this.arenaBgTransition.setPosition(0, 0);
this.arenaPlayer.setPosition(300, 0);
2023-04-30 16:38:46 +01:00
this.arenaPlayerTransition.setPosition(0, 0);
this.arenaEnemy.setPosition(-280, 0);
this.arenaNextEnemy.setPosition(-280, 0);
this.trainer.setTexture('trainer_m');
this.trainer.setPosition(406, 132);
2023-03-28 19:54:52 +01:00
}
2023-04-28 20:03:42 +01:00
newBattle(waveIndex?: integer): Battle {
if (!waveIndex) {
if (this.currentBattle) {
this.getEnemyPokemon().destroy();
if (this.currentBattle.waveIndex % 10)
this.pushPhase(new NextEncounterPhase(this));
else {
this.pushPhase(new SelectBiomePhase(this));
this.pushPhase(new NewBiomeEncounterPhase(this));
}
} else {
if (!this.quickStart)
this.pushPhase(new CheckLoadPhase(this));
else {
this.arena.playBgm();
this.pushPhase(new EncounterPhase(this));
this.pushPhase(new SummonPhase(this));
}
2023-03-31 21:04:39 +01:00
}
}
2023-04-28 20:03:42 +01:00
this.currentBattle = new Battle(waveIndex || ((this.currentBattle?.waveIndex || (startingWave - 1)) + 1));
2023-04-19 04:54:07 +01:00
2023-03-31 04:02:35 +01:00
return this.currentBattle;
2023-03-28 19:54:52 +01:00
}
2023-04-28 20:03:42 +01:00
newArena(biome: Biome, init?: boolean): Arena {
this.arena = new Arena(this, biome, Biome[biome].toLowerCase());
2023-04-28 20:03:42 +01:00
if (init) {
const biomeKey = this.arena.getBiomeKey();
this.arenaBg.setTexture(`${biomeKey}_bg`);
this.arenaBgTransition.setTexture(`${biomeKey}_bg`);
this.arenaPlayer.setTexture(`${biomeKey}_a`);
this.arenaPlayerTransition.setTexture(`${biomeKey}_a`);
this.arenaEnemy.setTexture(`${biomeKey}_b`);
this.arenaNextEnemy.setTexture(`${biomeKey}_b`);
}
2023-03-31 21:04:39 +01:00
return this.arena;
2023-03-28 19:54:52 +01:00
}
2023-04-21 00:44:56 +01:00
updateWaveCountText(): void {
2023-04-19 04:54:07 +01:00
const isBoss = !(this.currentBattle.waveIndex % 10);
this.waveCountText.setText(this.currentBattle.waveIndex.toString());
this.waveCountText.setColor(!isBoss ? '#404040' : '#f89890');
this.waveCountText.setShadowColor(!isBoss ? '#ded6b5' : '#984038');
2023-04-28 20:03:42 +01:00
this.waveCountText.setVisible(true);
2023-04-19 04:54:07 +01:00
}
2023-04-21 00:44:56 +01:00
updateWaveCountPosition(): void {
this.waveCountText.setY(-(this.game.canvas.height / 6) + (this.enemyModifiers.length ? 15 : 0));
}
randomSpecies(waveIndex: integer, level: integer, fromArenaPool?: boolean): PokemonSpecies {
2023-03-31 21:04:39 +01:00
return fromArenaPool
? this.arena.randomSpecies(waveIndex, level)
: getPokemonSpecies(allSpecies[(Utils.randInt(allSpecies.length)) - 1].getSpeciesForLevel(level));
2023-03-28 19:54:52 +01:00
}
checkInput(): boolean {
if (this.blockInput)
return;
if (this.isButtonPressed(Button.UP))
this.ui.processInput(Button.UP);
else if (this.isButtonPressed(Button.DOWN))
this.ui.processInput(Button.DOWN);
else if (this.isButtonPressed(Button.LEFT))
this.ui.processInput(Button.LEFT);
else if (this.isButtonPressed(Button.RIGHT))
this.ui.processInput(Button.RIGHT);
else if (this.isButtonPressed(Button.ACTION))
this.ui.processInput(Button.ACTION);
else if (this.isButtonPressed(Button.CANCEL))
this.ui.processInput(Button.CANCEL);
else if (this.ui?.getHandler() instanceof StarterSelectUiHandler) {
if (this.isButtonPressed(Button.CYCLE_SHINY))
this.ui.processInput(Button.CYCLE_SHINY);
else if (this.isButtonPressed(Button.CYCLE_FORM))
this.ui.processInput(Button.CYCLE_FORM);
else if (this.isButtonPressed(Button.CYCLE_GENDER))
this.ui.processInput(Button.CYCLE_GENDER);
2023-04-26 17:50:21 +01:00
else if (this.isButtonPressed(Button.CYCLE_ABILITY))
this.ui.processInput(Button.CYCLE_ABILITY);
else
return;
}
2023-04-12 16:30:47 +01:00
else if (this.isButtonPressed(Button.SPEED_UP)) {
if (!this.auto) {
if (this.gameSpeed < 2.5)
2023-04-12 16:30:47 +01:00
this.gameSpeed += 0.25;
} else if (this.gameSpeed < 20)
this.gameSpeed++;
} else if (this.isButtonPressed(Button.SLOW_DOWN)) {
if (this.gameSpeed > 1) {
if (!this.auto)
this.gameSpeed -= 0.25;
else
this.gameSpeed--;
2023-04-09 05:22:14 +01:00
}
2023-04-12 16:30:47 +01:00
} else if (enableAuto) {
if (this.isButtonPressed(Button.AUTO)) {
this.auto = !this.auto;
if (this.auto)
this.gameSpeed = Math.floor(this.gameSpeed);
else if (this.gameSpeed > 2.5)
this.gameSpeed = 2.5;
2023-04-12 16:30:47 +01:00
} else
return;
2023-04-09 05:22:14 +01:00
} else
2023-03-28 19:54:52 +01:00
return;
this.blockInput = true;
2023-04-12 16:30:47 +01:00
this.time.delayedCall(new Utils.FixedInt(250) as unknown as integer, () => this.blockInput = false);
2023-03-28 19:54:52 +01:00
}
isButtonPressed(button: Button): boolean {
return this.buttonKeys[button].filter(k => k.isDown).length >= 1;
}
2023-04-26 04:56:38 +01:00
playBgm(bgmName?: string, loopPoint?: number): void {
2023-04-10 18:54:06 +01:00
if (!bgmName && this.bgm) {
this.bgm.play({
volume: 1
});
return;
}
2023-03-31 21:04:39 +01:00
if (this.bgm && this.bgm.isPlaying)
2023-03-28 19:54:52 +01:00
this.bgm.stop();
this.bgm = this.sound.add(bgmName, { loop: true });
this.bgm.play();
2023-04-26 04:56:38 +01:00
if (loopPoint)
this.bgm.on('looped', () => this.bgm.play({ seek: loopPoint }));
2023-03-28 19:54:52 +01:00
}
pauseBgm(): void {
if (this.bgm)
this.bgm.pause();
}
resumeBgm(): void {
if (this.bgm && this.bgm.isPaused)
this.bgm.resume();
}
fadeOutBgm(duration?: integer, destroy?: boolean): void {
this.arena.fadeOutBgm(duration || 500, destroy);
2023-04-10 18:54:06 +01:00
}
2023-04-18 06:32:26 +01:00
playSoundWithoutBgm(soundName: string, pauseDuration?: integer): void {
this.pauseBgm();
this.sound.play(soundName);
const sound = this.sound.get(soundName);
if (this.bgmResumeTimer)
this.bgmResumeTimer.destroy();
this.bgmResumeTimer = this.time.delayedCall((pauseDuration || (sound.totalDuration * 1000)), () => {
this.resumeBgm();
this.bgmResumeTimer = null;
});
}
2023-03-28 19:54:52 +01:00
getCurrentPhase(): BattlePhase {
return this.currentPhase;
}
pushPhase(phase: BattlePhase): void {
this.phaseQueue.push(phase);
}
unshiftPhase(phase: BattlePhase): void {
if (this.phaseQueuePrependSpliceIndex === -1)
this.phaseQueuePrepend.push(phase);
else
this.phaseQueuePrepend.splice(this.phaseQueuePrependSpliceIndex, 0, phase);
2023-03-28 19:54:52 +01:00
}
clearPhaseQueue(): void {
this.phaseQueue.splice(0, this.phaseQueue.length);
}
setPhaseQueueSplice(): void {
this.phaseQueuePrependSpliceIndex = this.phaseQueuePrepend.length;
}
clearPhaseQueueSplice(): void {
this.phaseQueuePrependSpliceIndex = -1;
}
2023-03-28 19:54:52 +01:00
shiftPhase(): void {
if (this.phaseQueuePrependSpliceIndex > -1)
this.clearPhaseQueueSplice();
2023-03-28 19:54:52 +01:00
if (this.phaseQueuePrepend.length) {
while (this.phaseQueuePrepend.length)
this.phaseQueue.unshift(this.phaseQueuePrepend.pop());
}
if (!this.phaseQueue.length)
this.populatePhaseQueue();
this.currentPhase = this.phaseQueue.shift();
this.currentPhase.start();
}
2023-04-22 00:30:04 +01:00
queueMessage(message: string, callbackDelay?: integer, prompt?: boolean) {
this.unshiftPhase(new MessagePhase(this, message, callbackDelay, prompt));
}
2023-03-28 19:54:52 +01:00
populatePhaseQueue(): void {
this.phaseQueue.push(new CommandPhase(this));
}
2023-04-21 00:44:56 +01:00
addModifier(modifier: Modifier, playSound?: boolean, virtual?: boolean): Promise<void> {
2023-04-04 23:28:21 +01:00
return new Promise(resolve => {
const soundName = modifier.type.soundName;
2023-04-10 00:15:21 +01:00
if (modifier instanceof PersistentModifier) {
if ((modifier as PersistentModifier).add(this.modifiers, !!virtual)) {
2023-04-21 00:44:56 +01:00
if (playSound && !this.sound.get(soundName))
this.sound.play(soundName);
2023-04-20 20:46:05 +01:00
} else if (!virtual) {
const defaultModifierType = getDefaultModifierTypeForTier(modifier.type.tier);
2023-04-21 00:44:56 +01:00
this.addModifier(defaultModifierType.newModifier(), playSound).then(() => resolve());
2023-04-22 00:30:04 +01:00
this.queueMessage(`The stack for this item is full.\n You will receive ${defaultModifierType.name} instead.`, null, true);
return;
}
2023-03-28 19:54:52 +01:00
2023-04-10 00:15:21 +01:00
if (!virtual)
this.updateModifiers().then(() => resolve());
} else if (modifier instanceof ConsumableModifier) {
2023-04-21 00:44:56 +01:00
if (playSound && !this.sound.get(soundName))
this.sound.play(soundName);
2023-03-28 19:54:52 +01:00
2023-04-10 00:15:21 +01:00
if (modifier instanceof ConsumablePokemonModifier) {
for (let p in this.party) {
const pokemon = this.party[p];
2023-04-04 23:28:21 +01:00
2023-04-10 00:15:21 +01:00
const args: any[] = [ pokemon ];
if (modifier instanceof PokemonHpRestoreModifier) {
2023-04-20 20:46:05 +01:00
if (!(modifier as PokemonHpRestoreModifier).fainted) {
const hpRestoreMultiplier = new Utils.IntegerHolder(1);
2023-04-21 00:44:56 +01:00
this.applyModifiers(HealingBoosterModifier, true, hpRestoreMultiplier);
2023-04-20 20:46:05 +01:00
args.push(hpRestoreMultiplier.value);
} else
args.push(1);
2023-04-10 00:15:21 +01:00
}
2023-04-11 16:04:39 +01:00
2023-04-04 23:28:21 +01:00
if (modifier.shouldApply(args))
modifier.apply(args);
}
2023-04-10 00:15:21 +01:00
Promise.allSettled(this.party.map(p => p.updateInfo())).then(() => resolve());
} else {
const args = [ this ];
if (modifier.shouldApply(args))
modifier.apply(args);
resolve();
}
}
});
}
2023-03-28 19:54:52 +01:00
2023-04-23 15:24:22 +01:00
addEnemyModifier(itemModifier: PokemonHeldItemModifier): Promise<void> {
return new Promise(resolve => {
itemModifier.add(this.enemyModifiers, false);
this.updateModifiers(false).then(() => resolve());
});
}
tryTransferHeldItemModifier(itemModifier: PokemonHeldItemModifier, target: Pokemon, transferStack: boolean, playSound: boolean): Promise<boolean> {
2023-04-21 20:45:48 +01:00
return new Promise(resolve => {
const newItemModifier = itemModifier.clone() as PokemonHeldItemModifier;
2023-04-23 15:24:22 +01:00
const source = itemModifier.getPokemon(target.scene);
2023-04-21 20:45:48 +01:00
newItemModifier.pokemonId = target.id;
const matchingModifier = target.scene.findModifier(m => m instanceof PokemonHeldItemModifier
2023-04-23 15:24:22 +01:00
&& (m as PokemonHeldItemModifier).matchType(itemModifier), target.isPlayer()) as PokemonHeldItemModifier;
2023-04-21 20:45:48 +01:00
let removeOld = true;
if (matchingModifier) {
const maxStackCount = matchingModifier.getMaxStackCount();
if (matchingModifier.stackCount >= maxStackCount) {
resolve(false);
return;
}
2023-04-23 15:24:22 +01:00
const newStackCount = matchingModifier.stackCount + (transferStack ? itemModifier.stackCount : 1);
2023-04-21 20:45:48 +01:00
if (newStackCount > maxStackCount) {
itemModifier.stackCount = newStackCount - maxStackCount;
newItemModifier.stackCount = maxStackCount;
removeOld = !itemModifier.stackCount;
}
2023-04-23 15:24:22 +01:00
} else if (!transferStack)
removeOld = !(--itemModifier.stackCount);
if (!removeOld || this.removeModifier(itemModifier, !source.isPlayer())) {
const addModifier = () => {
if (target.isPlayer())
this.addModifier(newItemModifier, playSound).then(() => resolve(true));
else
this.addEnemyModifier(newItemModifier).then(() => resolve(true));
};
if (source.isPlayer() !== target.isPlayer())
this.updateModifiers(source.isPlayer()).then(() => addModifier());
else
addModifier();
2023-04-21 20:45:48 +01:00
return;
}
resolve(false);
});
}
2023-04-11 14:41:11 +01:00
removePartyMemberModifiers(partyMemberIndex: integer): Promise<void> {
2023-04-10 00:15:21 +01:00
return new Promise(resolve => {
2023-04-11 14:41:11 +01:00
const pokemonId = this.getParty()[partyMemberIndex].id;
const modifiersToRemove = this.modifiers.filter(m => (m instanceof PokemonHeldItemModifier) && (m as PokemonHeldItemModifier).pokemonId === pokemonId);
for (let m of modifiersToRemove)
this.modifiers.splice(this.modifiers.indexOf(m), 1);
this.updateModifiers().then(() => resolve());
2023-04-10 00:15:21 +01:00
});
}
2023-04-04 23:28:21 +01:00
2023-04-21 00:44:56 +01:00
generateEnemyModifiers(): Promise<void> {
2023-04-10 00:15:21 +01:00
return new Promise(resolve => {
2023-04-21 00:44:56 +01:00
const waveIndex = this.currentBattle.waveIndex;
2023-04-21 03:26:38 +01:00
const chances = Math.ceil(waveIndex / 10);
2023-04-23 15:42:00 +01:00
const isBoss = !(waveIndex % 10);
2023-04-21 00:44:56 +01:00
let count = 0;
for (let c = 0; c < chances; c++) {
2023-04-21 03:26:38 +01:00
if (!Utils.randInt(!isBoss ? 12 : 4))
2023-04-21 00:44:56 +01:00
count++;
if (count === 12)
break;
}
if (isBoss)
2023-04-21 03:26:38 +01:00
count = Math.max(count, Math.floor(chances / 2));
2023-04-21 00:44:56 +01:00
getEnemyModifierTypesForWave(waveIndex, count, this.getEnemyParty()).map(mt => mt.newModifier(this.getEnemyPokemon()).add(this.enemyModifiers, false));
this.updateModifiers(false).then(() => resolve());
});
}
clearEnemyModifiers(): void {
this.enemyModifiers.splice(0, this.enemyModifiers.length);
this.updateModifiers(false).then(() => this.updateWaveCountPosition());
}
updateModifiers(player?: boolean): Promise<void> {
if (player === undefined)
player = true;
return new Promise(resolve => {
2023-04-24 05:38:28 +01:00
const modifiers = player ? this.modifiers : this.enemyModifiers as PersistentModifier[];
2023-04-21 00:44:56 +01:00
for (let modifier of modifiers) {
2023-04-10 00:15:21 +01:00
if (modifier instanceof PersistentModifier)
(modifier as PersistentModifier).virtualStackCount = 0;
}
2023-04-21 00:44:56 +01:00
const modifiersClone = modifiers.slice(0);
for (let modifier of modifiersClone) {
2023-04-10 00:15:21 +01:00
if (!modifier.getStackCount())
2023-04-21 00:44:56 +01:00
modifiers.splice(modifiers.indexOf(modifier), 1);
2023-03-28 19:54:52 +01:00
}
2023-04-04 23:28:21 +01:00
2023-04-21 00:44:56 +01:00
this.updatePartyForModifiers(player ? this.getParty() : this.getEnemyParty()).then(() => {
(player ? this.modifierBar : this.enemyModifierBar).updateModifiers(modifiers);
if (!player)
this.updateWaveCountPosition();
2023-04-04 23:28:21 +01:00
resolve();
2023-04-10 00:15:21 +01:00
});
2023-04-04 23:28:21 +01:00
});
2023-03-28 19:54:52 +01:00
}
2023-04-21 00:44:56 +01:00
updatePartyForModifiers(party: Pokemon[]): Promise<void> {
2023-04-11 14:41:11 +01:00
return new Promise(resolve => {
2023-04-21 00:44:56 +01:00
Promise.allSettled(party.map(p => {
2023-04-11 14:41:11 +01:00
p.calculateStats();
return p.updateInfo();
})).then(() => resolve());
});
}
2023-04-21 00:44:56 +01:00
removeModifier(modifier: PersistentModifier, enemy?: boolean): boolean {
const modifiers = !enemy ? this.modifiers : this.enemyModifiers;
const modifierIndex = modifiers.indexOf(modifier);
if (modifierIndex > -1) {
2023-04-21 00:44:56 +01:00
modifiers.splice(modifierIndex, 1);
return true;
}
return false;
}
2023-04-28 20:03:42 +01:00
getModifiers(modifierType: { new(...args: any[]): Modifier }, player?: boolean): PersistentModifier[] {
2023-04-21 00:44:56 +01:00
if (player === undefined)
player = true;
return (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType);
}
2023-04-28 20:03:42 +01:00
findModifiers(modifierFilter: ModifierPredicate, player?: boolean): PersistentModifier[] {
2023-04-21 00:44:56 +01:00
if (player === undefined)
player = true;
return (player ? this.modifiers : this.enemyModifiers).filter(m => (modifierFilter as ModifierPredicate)(m));
2023-03-31 04:02:35 +01:00
}
2023-04-28 20:03:42 +01:00
findModifier(modifierFilter: ModifierPredicate, player?: boolean): PersistentModifier {
2023-04-21 00:44:56 +01:00
if (player === undefined)
player = true;
return (player ? this.modifiers : this.enemyModifiers).find(m => (modifierFilter as ModifierPredicate)(m));
2023-04-14 23:21:33 +01:00
}
2023-04-21 00:44:56 +01:00
applyModifiers(modifierType: { new(...args: any[]): Modifier }, player?: boolean, ...args: any[]): void {
if (player === undefined)
player = true;
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args));
2023-03-28 19:54:52 +01:00
for (let modifier of modifiers) {
if (modifier.apply(args))
2023-04-21 00:44:56 +01:00
console.log('Applied', modifier.type.name, !player ? '(enemy)' : '');
2023-03-28 19:54:52 +01:00
}
}
2023-04-20 20:46:05 +01:00
2023-04-21 00:44:56 +01:00
applyModifier(modifierType: { new(...args: any[]): Modifier }, player?: boolean, ...args: any[]): PersistentModifier {
if (player === undefined)
player = true;
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args));
2023-04-20 20:46:05 +01:00
for (let modifier of modifiers) {
if (modifier.apply(args)) {
2023-04-21 00:44:56 +01:00
console.log('Applied', modifier.type.name, !player ? '(enemy)' : '');
2023-04-20 20:46:05 +01:00
return modifier;
}
}
return null;
}
2023-03-28 19:54:52 +01:00
}