pokerogue/src/battle-scene.ts

435 lines
12 KiB
TypeScript
Raw Normal View History

2023-03-28 19:54:52 +01:00
import Phaser from 'phaser';
import { ArenaType, Arena } from './arena';
import UI from './ui/ui';
import { BattlePhase, EncounterPhase, SummonPhase, CommandPhase } from './battle-phase';
import { PlayerPokemon, EnemyPokemon } from './pokemon';
import PokemonSpecies, { default as Pokemon, allSpecies } from './pokemon-species';
import * as Utils from './utils';
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonModifierType, PokemonModifier } from './modifier';
import { Stat } from './pokemon-stat';
import { PokeballType } from './pokeball';
export default class BattleScene extends Phaser.Scene {
private phaseQueue: Array<BattlePhase>;
private phaseQueuePrepend: Array<BattlePhase>;
private currentPhase: BattlePhase;
private arena: Arena;
public field: Phaser.GameObjects.Container;
public fieldUI: Phaser.GameObjects.Container;
public arenaBg: Phaser.GameObjects.Image;
public arenaPlayer: Phaser.GameObjects.Image;
public arenaEnemy: Phaser.GameObjects.Image;
public arenaEnemy2: Phaser.GameObjects.Image;
public trainer: Phaser.GameObjects.Sprite;
public waveIndex: integer;
public pokeballCounts = Object.fromEntries(Utils.getEnumValues(PokeballType).map(t => [ t, 0 ]));
private party: PlayerPokemon[];
private modifierBar: ModifierBar;
private modifiers: Modifier[];
private enemyPokemon: EnemyPokemon;
public uiContainer: Phaser.GameObjects.Container;
public ui: UI;
private bgm: Phaser.Sound.BaseSound;
private upKey: Phaser.Input.Keyboard.Key;
private downKey: Phaser.Input.Keyboard.Key;
private leftKey: Phaser.Input.Keyboard.Key;
private rightKey: Phaser.Input.Keyboard.Key;
private actionKey: Phaser.Input.Keyboard.Key;
private cancelKey: Phaser.Input.Keyboard.Key;
private blockInput: boolean;
public trainerId: integer = Utils.randInt(65536);
public secretId: integer = Utils.randInt(65536);
constructor() {
super('battle');
this.phaseQueue = [];
this.phaseQueuePrepend = [];
}
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 += '/';
this.load.atlas(key, `images/${folder}${filenameRoot}.png`, `images/${folder}/${filenameRoot}.json`)
}
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');
this.loadImage('level_up_stats', 'ui');
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.loadAtlas('party_cancel', 'ui');
// Load arena images
for (let a = 1; a <= 15; a++) {
const arenaId = Utils.padInt(a, 2);
this.loadImage(`arena_${arenaId}`, 'arenas', `${arenaId}.png`);
this.loadImage(`arena_${arenaId}a`, 'arenas', `${arenaId}a.png`);
this.loadImage(`arena_${arenaId}b`, 'arenas', `${arenaId}b.png`);
}
// 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');
this.loadAtlas('shiny', 'effects');
this.loadAtlas('pb', '');
this.loadAtlas('items', '');
for (let i = 0; i < 6; i++)
this.loadAtlas(`pokemon_icons_${i}`, 'ui');
this.loadSe('select');
this.loadSe('menu_open');
this.loadSe('hit');
this.loadSe('hit_strong');
this.loadSe('hit_weak');
this.loadSe('faint');
this.loadSe('flee');
this.loadSe('exp');
this.loadSe('level_up');
this.loadSe('shiny');
this.loadSe('restore');
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');
this.loadSe('m_bubble');
this.loadSe('m_bubble3');
this.loadSe('m_crabhammer');
this.loadBgm('level_up_fanfare');
}
create() {
this.load.setBaseURL();
const field = this.add.container(0, 0);
field.setScale(6);
this.field = field;
// Init arena
const arenas = [
new Arena(this, ArenaType.PLAINS, 'battle'),
new Arena(this, ArenaType.GRASS, 'grass'),
new Arena(this, ArenaType.FOREST, 'forest'),
new Arena(this, ArenaType.WATER, 'water'),
new Arena(this, ArenaType.SWAMP, 'swamp'),
new Arena(this, ArenaType.SEA, 'sea'),
new Arena(this, ArenaType.MOUNTAIN, 'mountain'),
new Arena(this, ArenaType.LAND, 'land'),
new Arena(this, ArenaType.CAVE, 'cave'),
new Arena(this, ArenaType.DESERT, 'desert'),
new Arena(this, ArenaType.ARENA_BROWN, 'elite_1'),
new Arena(this, ArenaType.ARENA_BLUE, 'elite_2'),
new Arena(this, ArenaType.ARENA_PURPLE, 'elite_3'),
new Arena(this, ArenaType.ARENA_PINK, 'elite_4'),
new Arena(this, ArenaType.ARENA_ORANGE, 'elite_5')
];
const arena = arenas[Utils.randInt(15)];
this.arena = arena;
this.arenaBg = this.add.image(0, 0, `arena_${Utils.padInt(arena.type, 2)}`);
this.arenaPlayer = this.add.image(340, 20, `arena_${Utils.padInt(arena.type, 2)}a`);
this.arenaEnemy = this.add.image(-240, 13, `arena_${Utils.padInt(arena.type, 2)}b`);
this.arenaEnemy2 = this.add.image(-240, 13, `arena_${Utils.padInt(arena.type, 2)}b`);
[this.arenaBg, this.arenaPlayer, this.arenaEnemy, this.arenaEnemy2].forEach(a => {
a.setOrigin(0, 0);
field.add(a);
});
//arena.playBgm();
const fieldUI = this.add.container(0, this.game.canvas.height);
fieldUI.setScale(6);
this.fieldUI = fieldUI;
const uiContainer = this.add.container(0, 0);
uiContainer.setScale(6);
this.uiContainer = uiContainer;
this.modifiers = [];
this.modifierBar = new ModifierBar(this);
this.add.existing(this.modifierBar);
uiContainer.add(this.modifierBar);
this.waveIndex = 1;
this.party = [];
for (let s = 0; s < 3; s++) {
const playerSpecies = this.randomSpecies();
const playerPokemon = new PlayerPokemon(this, playerSpecies, 5);
playerPokemon.setVisible(false);
this.party.push(playerPokemon);
}
const enemySpecies = arena.randomSpecies(1);
console.log(enemySpecies.name);
const enemyPokemon = new EnemyPokemon(this, enemySpecies, this.getLevelForWave());
this.add.existing(enemyPokemon);
this.enemyPokemon = enemyPokemon;
field.add(enemyPokemon);
console.log(this.getPlayerPokemon().species.name, this.getPlayerPokemon().species.speciesId, this.getPlayerPokemon().stats);
console.log(enemyPokemon.species.name, enemyPokemon.species.speciesId, enemyPokemon.stats);
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(406, 132, 'trainer_m');
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
});
const ui = new UI(this);
this.uiContainer.add(ui);
this.ui = ui;
ui.setup();
this.phaseQueue.push(new EncounterPhase(this), new SummonPhase(this));
this.shiftPhase();
this.upKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP);
this.downKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN);
this.leftKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT);
this.rightKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT);
this.actionKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z);
this.cancelKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.X);
}
update() {
this.checkInput();
}
getParty(): PlayerPokemon[] {
return this.party;
}
getPlayerPokemon(): PlayerPokemon {
return this.getParty()[0];
}
getEnemyPokemon(): EnemyPokemon {
return this.enemyPokemon;
}
setEnemyPokemon(enemyPokemon: EnemyPokemon) {
this.enemyPokemon = enemyPokemon;
}
randomSpecies(fromArenaPool?: boolean): PokemonSpecies {
return fromArenaPool
? this.arena.randomSpecies(1)
: allSpecies[(Utils.randInt(allSpecies.length)) - 1];
}
getLevelForWave() {
if (this.waveIndex === 1)
return 5;
let averageLevel = 5 + this.waveIndex * 0.5;
if (this.waveIndex % 10 === 0)
return averageLevel + 5;
const deviation = 10 / this.waveIndex;
return averageLevel + Math.round(Utils.randGauss(deviation));
}
checkInput(): boolean {
if (this.blockInput)
return;
if (this.upKey.isDown)
this.ui.processInput(this.upKey.keyCode);
else if (this.downKey.isDown)
this.ui.processInput(this.downKey.keyCode);
else if (this.leftKey.isDown)
this.ui.processInput(this.leftKey.keyCode);
else if (this.rightKey.isDown)
this.ui.processInput(this.rightKey.keyCode);
else if (this.actionKey.isDown)
this.ui.processInput(this.actionKey.keyCode);
else if (this.cancelKey.isDown)
this.ui.processInput(this.cancelKey.keyCode);
else
return;
this.blockInput = true;
this.time.delayedCall(250, () => {
this.blockInput = false;
});
}
playBgm(bgmName: string): void {
if (this.bgm) {
this.bgm.stop();
this.bgm.destroy();
}
this.bgm = this.sound.add(bgmName, { loop: true });
this.bgm.play();
}
pauseBgm(): void {
if (this.bgm)
this.bgm.pause();
}
resumeBgm(): void {
if (this.bgm && this.bgm.isPaused)
this.bgm.resume();
}
getCurrentPhase(): BattlePhase {
return this.currentPhase;
}
pushPhase(phase: BattlePhase): void {
this.phaseQueue.push(phase);
}
unshiftPhase(phase: BattlePhase): void {
this.phaseQueuePrepend.push(phase);
}
clearPhaseQueue(): void {
this.phaseQueue.splice(0, this.phaseQueue.length);
}
shiftPhase(): void {
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();
}
populatePhaseQueue(): void {
this.phaseQueue.push(new CommandPhase(this));
}
addModifier(modifier: Modifier): void {
if (modifier.add(this.modifierBar, this.modifiers))
this.sound.play('restore');
if (modifier instanceof ConsumableModifier) {
const args = [ this ];
if (modifier.shouldApply(args))
modifier.apply(args);
return;
}
if (modifier instanceof PokemonModifier) {
for (let p in this.party) {
const pokemon = this.party[p];
if (modifier instanceof ConsumablePokemonModifier) {
const args = [ pokemon ];
if (modifier.shouldApply(args))
modifier.apply(args);
}
pokemon.calculateStats();
pokemon.updateInfo();
}
}
}
applyModifiers(modifierType: { new(...args: any[]): Modifier }, ...args: any[]): void {
const modifiers = this.modifiers.filter(m => m instanceof modifierType && m.shouldApply(args));
for (let modifier of modifiers) {
if (modifier.apply(args))
console.log('Applied', modifier.type.name);
}
}
}