diff --git a/src/arena.ts b/src/arena.ts index 93de85afd7d..fd5fa24945a 100644 --- a/src/arena.ts +++ b/src/arena.ts @@ -161,7 +161,7 @@ export class Arena { this.weather = weather ? new Weather(weather, viaMove ? 5 : 0) : null; if (this.weather) { - this.scene.unshiftPhase(new CommonAnimPhase(this.scene, true, CommonAnim.SUNNY + (weather - 1))); + this.scene.unshiftPhase(new CommonAnimPhase(this.scene, true, 0, 0, CommonAnim.SUNNY + (weather - 1))); this.scene.queueMessage(getWeatherStartMessage(weather)); } else this.scene.queueMessage(getWeatherClearMessage(oldWeatherType)); diff --git a/src/battle-phases.ts b/src/battle-phases.ts index 65edb62ee68..18b5b692ce7 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -28,6 +28,7 @@ import { ArenaTagType, ArenaTrapTag, TrickRoomTag } from "./data/arena-tag"; import { CheckTrappedAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, SuppressWeatherEffectAbAttr, applyCheckTrappedAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability"; import { Unlockables, getUnlockableName } from "./system/unlockables"; import { getBiomeKey } from "./arena"; +import { BattleTarget } from "./battle"; export class CheckLoadPhase extends BattlePhase { private loaded: boolean; @@ -73,7 +74,9 @@ export class CheckLoadPhase extends BattlePhase { this.scene.arena.playBgm(); this.scene.pushPhase(new EncounterPhase(this.scene, this.loaded)); - this.scene.pushPhase(new SummonPhase(this.scene)); + this.scene.pushPhase(new SummonPhase(this.scene, 0)); + if (this.scene.currentBattle.double) + this.scene.pushPhase(new SummonPhase(this.scene, 1)); super.end(); } @@ -113,6 +116,65 @@ export class SelectStarterPhase extends BattlePhase { } } +type PokemonFunc = (pokemon: Pokemon) => void; + +export abstract class FieldPhase extends BattlePhase { + getOrder(): BattleTarget[] { + const playerField = this.scene.getPlayerField() as Pokemon[]; + const enemyField = this.scene.getEnemyField() as Pokemon[]; + + let orderedTargets: Pokemon[] = playerField.concat(enemyField).sort((a: Pokemon, b: Pokemon) => { + const aSpeed = a?.getBattleStat(Stat.SPD) || 0; + const bSpeed = b?.getBattleStat(Stat.SPD) || 0; + + return aSpeed < bSpeed ? 1 : aSpeed > bSpeed ? -1 : !Utils.randInt(2) ? -1 : 1; + }); + + const speedReversed = new Utils.BooleanHolder(false); + this.scene.arena.applyTags(TrickRoomTag, speedReversed); + + if (speedReversed.value) + orderedTargets = orderedTargets.reverse(); + + return orderedTargets.map(t => t.getFieldIndex() + (!t.isPlayer() ? BattleTarget.ENEMY : 0)); + } + + executeForAll(func: PokemonFunc): void { + const field = this.scene.getField().filter(p => p); + field.forEach(pokemon => func(pokemon)); + } +} + +export abstract class PokemonPhase extends FieldPhase { + protected player: boolean; + protected fieldIndex: integer; + + constructor(scene: BattleScene, player: boolean, fieldIndex: integer) { + super(scene); + + this.player = player; + this.fieldIndex = fieldIndex; + } + + getPokemon() { + return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex || 0]; + } +} + +export abstract class PartyMemberPokemonPhase extends PokemonPhase { + protected partyMemberIndex: integer; + + constructor(scene: BattleScene, partyMemberIndex: integer) { + super(scene, true, partyMemberIndex); + + this.partyMemberIndex = partyMemberIndex; + } + + getPokemon() { + return this.scene.getParty()[this.partyMemberIndex]; + } +} + export class EncounterPhase extends BattlePhase { private loaded: boolean; @@ -127,25 +189,34 @@ export class EncounterPhase extends BattlePhase { this.scene.updateWaveCountText(); + const loadEnemyAssets = []; + const battle = this.scene.currentBattle; - const enemySpecies = this.scene.randomSpecies(battle.waveIndex, battle.enemyLevel, true); - if (!this.loaded) - battle.enemyPokemon = new EnemyPokemon(this.scene, enemySpecies, battle.enemyLevel); - const enemyPokemon = this.scene.getEnemyPokemon(); - enemyPokemon.resetSummonData(); - this.scene.gameData.setPokemonSeen(enemyPokemon); + battle.enemyLevels.forEach((_, e) => { + const enemySpecies = this.scene.randomSpecies(battle.waveIndex, battle.enemyLevels[e], true); + if (!this.loaded) + battle.enemyField[e] = new EnemyPokemon(this.scene, enemySpecies, battle.enemyLevels[e]); + const enemyPokemon = this.scene.getEnemyField()[e]; + enemyPokemon.resetSummonData(); + + this.scene.gameData.setPokemonSeen(enemyPokemon); - console.log(enemyPokemon.species.name, enemyPokemon.species.speciesId, enemyPokemon.stats); + loadEnemyAssets.push(enemyPokemon.loadAssets()); + + 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); + Promise.all(loadEnemyAssets).then(() => { + battle.enemyField.forEach(enemyPokemon => { + this.scene.field.add(enemyPokemon); + if (this.scene.getPlayerPokemon().visible) + this.scene.field.moveBelow(enemyPokemon, this.scene.getPlayerPokemon()); + enemyPokemon.tint(0, 0.5); + }); if (!this.loaded) { - regenerateModifierPoolThresholds(this.scene.getEnemyParty(), false); + regenerateModifierPoolThresholds(this.scene.getEnemyField(), false); this.scene.generateEnemyModifiers(); } @@ -165,27 +236,36 @@ export class EncounterPhase extends BattlePhase { this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena.biomeType), false); - const enemyPokemon = this.scene.getEnemyPokemon(); + const enemyField = this.scene.getEnemyField(); 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, + targets: [ this.scene.arenaEnemy, enemyField, this.scene.arenaPlayer, this.scene.trainer ].flat(), + x: (_target, _key, value, fieldIndex: integer) => fieldIndex < 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); + enemyField.forEach(enemyPokemon => { + enemyPokemon.untint(100, 'Sine.easeOut'); + enemyPokemon.cry(); + enemyPokemon.showInfo(); + }); + const text = enemyField.length === 1 + ? `A wild ${enemyField[0].name} appeared!` + : `A wild ${enemyField[0].name}\nand ${enemyField[1].name} appeared!`; + this.scene.ui.showText(text, null, () => this.end(), 1500); } }); } end() { - if (this.scene.getEnemyPokemon().shiny) - this.scene.unshiftPhase(new ShinySparklePhase(this.scene, false)); + const enemyField = this.scene.getEnemyField(); - this.scene.arena.applyTags(ArenaTrapTag, this.scene.getEnemyPokemon()); + enemyField.forEach((enemyPokemon, e) => { + if (enemyPokemon.shiny) + this.scene.unshiftPhase(new ShinySparklePhase(this.scene, false, e)); + }); - applyPostSummonAbAttrs(PostSummonAbAttr, this.scene.getEnemyPokemon()); + enemyField.forEach(enemyPokemon => this.scene.arena.applyTags(ArenaTrapTag, enemyPokemon)); + + enemyField.forEach(enemyPokemon => applyPostSummonAbAttrs(PostSummonAbAttr, enemyPokemon)); // TODO: Remove //this.scene.unshiftPhase(new SelectModifierPhase(this.scene)); @@ -200,28 +280,37 @@ export class NextEncounterPhase extends EncounterPhase { } doEncounter(): void { - const enemyPokemon = this.scene.getEnemyPokemon(); + const enemyField = this.scene.getEnemyField(); this.scene.tweens.add({ - targets: [ this.scene.arenaEnemy, this.scene.arenaNextEnemy, enemyPokemon ], + targets: [ this.scene.arenaEnemy, this.scene.arenaNextEnemy, enemyField ].flat(), x: '+=300', duration: 2000, onComplete: () => { this.scene.arenaEnemy.setX(this.scene.arenaNextEnemy.x); this.scene.arenaEnemy.setAlpha(1); 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); + enemyField.forEach(enemyPokemon => { + enemyPokemon.untint(100, 'Sine.easeOut'); + enemyPokemon.cry(); + enemyPokemon.showInfo(); + }); + const text = enemyField.length === 1 + ? `A wild ${enemyField[0].name} appeared!` + : `A wild ${enemyField[0].name}\nand ${enemyField[1].name} appeared!`; + this.scene.ui.showText(text, null, () => this.end(), 1500); } }); } end() { - if (this.scene.getEnemyPokemon().shiny) - this.scene.unshiftPhase(new ShinySparklePhase(this.scene, false)); + this.scene.getEnemyField().forEach((enemyPokemon, e) => { + if (enemyPokemon.shiny) + this.scene.unshiftPhase(new ShinySparklePhase(this.scene, false, e)); + }); - this.scene.unshiftPhase(new CheckSwitchPhase(this.scene)); + this.scene.unshiftPhase(new CheckSwitchPhase(this.scene, 0)); + if (this.scene.currentBattle.double) + this.scene.unshiftPhase(new CheckSwitchPhase(this.scene, 1)); super.end(); } @@ -235,16 +324,21 @@ export class NewBiomeEncounterPhase extends NextEncounterPhase { doEncounter(): void { this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena.biomeType), false); - const enemyPokemon = this.scene.getEnemyPokemon(); + const enemyField = this.scene.getEnemyField(); this.scene.tweens.add({ - targets: [ this.scene.arenaEnemy, enemyPokemon ], - x: (_target, _key, value, targetIndex: integer) => targetIndex < 2 ? value + 300 : value - 300, + targets: [ this.scene.arenaEnemy, enemyField ].flat(), + x: (_target, _key, value, fieldIndex: integer) => fieldIndex < 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); + enemyField.forEach(enemyPokemon => { + enemyPokemon.untint(100, 'Sine.easeOut'); + enemyPokemon.cry(); + enemyPokemon.showInfo(); + }); + const text = enemyField.length === 1 + ? `A wild ${enemyField[0].name} appeared!` + : `A wild ${enemyField[0].name}\nand ${enemyField[1].name} appeared!`; + this.scene.ui.showText(text, null, () => this.end(), 1500); } }); } @@ -339,9 +433,9 @@ export class SwitchBiomePhase extends BattlePhase { } } -export class SummonPhase extends BattlePhase { - constructor(scene: BattleScene) { - super(scene); +export class SummonPhase extends PartyMemberPokemonPhase { + constructor(scene: BattleScene, fieldIndex: integer) { + super(scene, fieldIndex); } start() { @@ -351,7 +445,7 @@ export class SummonPhase extends BattlePhase { } preSummon(): void { - this.scene.ui.showText(`Go! ${this.scene.getPlayerPokemon().name}!`); + this.scene.ui.showText(`Go! ${this.getPokemon().name}!`); this.scene.trainer.play('trainer_m_pb'); this.scene.tweens.add({ targets: this.scene.trainer, @@ -367,7 +461,7 @@ export class SummonPhase extends BattlePhase { pokeball.setOrigin(0.5, 0.625); this.scene.field.add(pokeball); - const playerPokemon = this.scene.getPlayerPokemon(); + const playerPokemon = this.getPokemon(); pokeball.setVisible(true); this.scene.tweens.add({ @@ -414,16 +508,18 @@ export class SummonPhase extends BattlePhase { } end() { - const pokemon = this.scene.getPlayerPokemon(); + const playerField = this.scene.getPlayerField(); - if (pokemon.shiny) - this.scene.unshiftPhase(new ShinySparklePhase(this.scene, true)); + playerField.forEach((pokemon, p) => { + if (pokemon.shiny) + this.scene.unshiftPhase(new ShinySparklePhase(this.scene, true, p)); + }); - pokemon.resetTurnData(); + playerField.forEach(pokemon => pokemon.resetTurnData()); - this.scene.arena.applyTags(ArenaTrapTag, pokemon); + playerField.forEach(pokemon => this.scene.arena.applyTags(ArenaTrapTag, pokemon)); - applyPostSummonAbAttrs(PostSummonAbAttr, pokemon); + playerField.forEach(pokemon => applyPostSummonAbAttrs(PostSummonAbAttr, pokemon)); super.end(); } @@ -436,8 +532,8 @@ export class SwitchSummonPhase extends SummonPhase { private lastPokemon: PlayerPokemon; - constructor(scene: BattleScene, slotIndex: integer, doReturn: boolean, batonPass: boolean) { - super(scene); + constructor(scene: BattleScene, fieldIndex: integer, slotIndex: integer, doReturn: boolean, batonPass: boolean) { + super(scene, fieldIndex); this.slotIndex = slotIndex; this.doReturn = doReturn; @@ -454,12 +550,12 @@ export class SwitchSummonPhase extends SummonPhase { return; } - const playerPokemon = this.scene.getPlayerPokemon(); + const playerPokemon = this.getPokemon(); if (!this.batonPass) - this.scene.getEnemyPokemon()?.removeTagsBySourceId(playerPokemon.id); + this.scene.getEnemyField().forEach(enemyPokemon => enemyPokemon.removeTagsBySourceId(playerPokemon.id)); - this.scene.ui.showText(`Come back, ${this.scene.getPlayerPokemon().name}!`); + this.scene.ui.showText(`Come back, ${playerPokemon.name}!`); this.scene.sound.play('pb_rel'); playerPokemon.hideInfo(); playerPokemon.tint(getPokeballTintColor(playerPokemon.pokeball), 1, 250, 'Sine.easeIn'); @@ -479,9 +575,9 @@ export class SwitchSummonPhase extends SummonPhase { switchAndSummon() { const party = this.scene.getParty(); const switchedPokemon = party[this.slotIndex]; - this.lastPokemon = this.scene.getPlayerPokemon(); + this.lastPokemon = this.getPokemon(); if (this.batonPass) { - this.scene.getEnemyPokemon()?.transferTagsBySourceId(this.lastPokemon.id, switchedPokemon.id); + this.scene.getEnemyField().forEach(enemyPokemon => enemyPokemon.transferTagsBySourceId(this.lastPokemon.id, switchedPokemon.id)); if (!this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === switchedPokemon.id)) { const batonPassModifier = this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === this.lastPokemon.id) as SwitchEffectTransferModifier; @@ -496,7 +592,7 @@ export class SwitchSummonPhase extends SummonPhase { end() { if (this.batonPass) - this.scene.getPlayerPokemon().transferSummon(this.lastPokemon); + this.getPokemon().transferSummon(this.lastPokemon); this.lastPokemon.resetSummonData(); @@ -505,15 +601,21 @@ export class SwitchSummonPhase extends SummonPhase { } export class CheckSwitchPhase extends BattlePhase { - constructor(scene: BattleScene) { - super(scene) + protected fieldIndex: integer; + + constructor(scene: BattleScene, fieldIndex: integer) { + super(scene); + + this.fieldIndex = fieldIndex; } start() { super.start(); - if (this.scene.field.getAll().indexOf(this.scene.getPlayerPokemon()) === -1) { - this.scene.unshiftPhase(new SummonMissingPhase(this.scene)); + const pokemon = this.scene.getPlayerField()[this.fieldIndex]; + + if (this.scene.field.getAll().indexOf(pokemon) === -1) { + this.scene.unshiftPhase(new SummonMissingPhase(this.scene, this.fieldIndex)); super.end(); return; } @@ -523,7 +625,7 @@ export class CheckSwitchPhase extends BattlePhase { return; } - if (this.scene.getPlayerPokemon().getTag(BattlerTagType.FRENZY)) { + if (pokemon.getTag(BattlerTagType.FRENZY)) { super.end(); return; } @@ -531,7 +633,7 @@ export class CheckSwitchPhase extends BattlePhase { this.scene.ui.showText('Will you switch\nPOKéMON?', null, () => { this.scene.ui.setMode(Mode.CONFIRM, () => { this.scene.ui.setMode(Mode.MESSAGE); - this.scene.unshiftPhase(new SwitchPhase(this.scene, false, true)); + this.scene.unshiftPhase(new SwitchPhase(this.scene, this.fieldIndex, false, true)); this.end(); }, () => { this.scene.ui.setMode(Mode.MESSAGE); @@ -542,59 +644,59 @@ export class CheckSwitchPhase extends BattlePhase { } export class SummonMissingPhase extends SummonPhase { - constructor(scene: BattleScene) { - super(scene); + constructor(scene: BattleScene, fieldIndex: integer) { + super(scene, fieldIndex); } preSummon(): void { - this.scene.ui.showText(`Go! ${this.scene.getPlayerPokemon().name}!`); + this.scene.ui.showText(`Go! ${this.getPokemon().name}!`); this.scene.time.delayedCall(250, () => this.summon()); } } -type PokemonFunc = (pokemon: Pokemon) => void; - -export abstract class FieldPhase extends BattlePhase { - isPlayerDelayed(): boolean { - const playerPokemon = this.scene.getPlayerPokemon(); - const enemyPokemon = this.scene.getEnemyPokemon(); - - const playerSpeed = playerPokemon?.getBattleStat(Stat.SPD) || 0; - const enemySpeed = enemyPokemon?.getBattleStat(Stat.SPD) || 0; - - const speedDelayed = new Utils.BooleanHolder(playerSpeed < enemySpeed); - this.scene.arena.applyTags(TrickRoomTag, speedDelayed); - - return speedDelayed.value || (playerSpeed === enemySpeed && Utils.randInt(2) === 1); - } - - executeForBoth(func: PokemonFunc): void { - const playerPokemon = this.scene.getPlayerPokemon(); - const enemyPokemon = this.scene.getEnemyPokemon(); - const delayed = this.isPlayerDelayed(); - if (!delayed && playerPokemon) - func(playerPokemon); - if (enemyPokemon) - func(enemyPokemon); - if (delayed && playerPokemon) - func(playerPokemon); - } -} - -export class CommandPhase extends FieldPhase { +export class TurnInitPhase extends FieldPhase { constructor(scene: BattleScene) { - super(scene) + super(scene); } start() { super.start(); - const playerPokemon = this.scene.getPlayerPokemon(); + this.scene.getPlayerField().forEach(playerPokemon => { + this.scene.currentBattle.addParticipant(playerPokemon); - this.scene.currentBattle.addParticipant(playerPokemon); + playerPokemon.resetTurnData(); + }); - playerPokemon.resetTurnData(); - this.scene.getEnemyPokemon().resetTurnData(); + this.scene.getEnemyField().forEach(enemyPokemon => enemyPokemon.resetTurnData()); + + const order = this.getOrder(); + for (let o of order) { + if (o < BattleTarget.ENEMY) + this.scene.pushPhase(new CommandPhase(this.scene, o)); + else + this.scene.pushPhase(new EnemyCommandPhase(this.scene, o - BattleTarget.ENEMY)); + } + + this.scene.pushPhase(new TurnStartPhase(this.scene)); + + this.end(); + } +} + +export class CommandPhase extends FieldPhase { + protected fieldIndex: integer; + + constructor(scene: BattleScene, fieldIndex: integer) { + super(scene); + + this.fieldIndex = fieldIndex; + } + + start() { + super.start(); + + const playerPokemon = this.scene.getPlayerField()[this.fieldIndex]; const moveQueue = playerPokemon.getMoveQueue(); @@ -617,43 +719,17 @@ export class CommandPhase extends FieldPhase { } handleCommand(command: Command, cursor: integer, ...args: any[]): boolean { - const playerPokemon = this.scene.getPlayerPokemon(); - const enemyPokemon = this.scene.getEnemyPokemon(); + const playerPokemon = this.scene.getPlayerField()[this.fieldIndex]; + const enemyField = this.scene.getEnemyField(); let success: boolean; - let isDelayed = (command: Command, playerMove: PokemonMove, enemyMove: PokemonMove) => { - switch (command) { - case Command.FIGHT: - if (playerMove || enemyMove) { - const playerMovePriority = playerMove?.getMove()?.priority || 0; - const enemyMovePriority = enemyMove?.getMove()?.priority || 0; - if (playerMovePriority !== enemyMovePriority) - return playerMovePriority < enemyMovePriority; - } - break; - case Command.BALL: - case Command.POKEMON: - case Command.RUN: - return false; - } - - return this.isPlayerDelayed(); - }; - - let playerMove: PokemonMove; - switch (command) { case Command.FIGHT: - if (cursor == -1) { - this.scene.pushPhase(new PlayerMovePhase(this.scene, playerPokemon, new PokemonMove(Moves.NONE))); - success = true; - break; - } + const targetIndex = Utils.randInt(enemyField.length); // TODO: Let user select this - if (playerPokemon.trySelectMove(cursor, args[0] as boolean)) { - playerMove = playerPokemon.getMoveset()[cursor]; - const playerPhase = new PlayerMovePhase(this.scene, playerPokemon, playerMove, false, args[0] as boolean); - this.scene.pushPhase(playerPhase); + if (cursor === -1 || playerPokemon.trySelectMove(cursor, args[0] as boolean)) { + this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.FIGHT, cursor: cursor, + move: cursor > -1 ? { move: playerPokemon.moveset[cursor].moveId } : null, args: args }; // TODO: Struggle logic success = true; } else if (cursor < playerPokemon.getMoveset().length) { const move = playerPokemon.getMoveset()[cursor]; @@ -676,7 +752,8 @@ export class CommandPhase extends FieldPhase { this.scene.ui.setMode(Mode.COMMAND); }, null, true); } else if (cursor < 4) { - this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, cursor as PokeballType)); + const targetIndex = Utils.randInt(enemyField.length); // TODO: Let user select this + this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor, targetIndex: targetIndex }; success = true; } break; @@ -687,12 +764,11 @@ export class CommandPhase extends FieldPhase { const trapped = new Utils.BooleanHolder(false); const batonPass = isSwitch && args[0] as boolean; if (!batonPass) - applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped); + enemyField.forEach(enemyPokemon => applyCheckTrappedAbAttrs(CheckTrappedAbAttr, enemyPokemon, trapped)); if (batonPass || (!trapTag && !trapped.value)) { - if (isSwitch) - this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, cursor, true, args[0] as boolean)); - else - this.scene.unshiftPhase(new AttemptRunPhase(this.scene)); + this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch + ? { command: Command.POKEMON, cursor: cursor, args: args } + : { command: Command.RUN }; success = true; } else if (trapTag) this.scene.ui.showText(`${this.scene.getPokemonById(trapTag.sourceId).name}'s ${trapTag.getMoveName()}\nprevents ${isSwitch ? 'switching' : 'fleeing'}!`, null, () => { @@ -701,47 +777,129 @@ export class CommandPhase extends FieldPhase { break; } - if (success) { - if (this.scene.arena.weather) - this.scene.unshiftPhase(new WeatherEffectPhase(this.scene, this.scene.arena.weather)); - - const enemyNextMove = enemyPokemon.getNextMove(); - let enemyMove: PokemonMove; - if (enemyNextMove.move) { - enemyMove = enemyPokemon.getMoveset().find(m => m.moveId === enemyNextMove.move) || new PokemonMove(enemyNextMove.move, 0, 0); - const enemyPhase = new EnemyMovePhase(this.scene, enemyPokemon, enemyMove, false, enemyNextMove.ignorePP); - if (isDelayed(command, playerMove, enemyMove)) - this.scene.unshiftPhase(enemyPhase); - else - this.scene.pushPhase(enemyPhase); - } - - 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); - if (this.isPlayerDelayed()) - statusEffectPhases.unshift(enemyStatusEffectPhase); - else - statusEffectPhases.push(enemyStatusEffectPhase); - } - for (let sef of statusEffectPhases) - this.scene.pushPhase(sef); - - this.scene.pushPhase(new TurnEndPhase(this.scene)); - + if (success) this.end(); - } return success; } + getPokemon(): PlayerPokemon { + return this.scene.getPlayerField()[this.fieldIndex]; + } + end() { this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); } } +export class EnemyCommandPhase extends FieldPhase { + protected fieldIndex: integer; + + constructor(scene: BattleScene, fieldIndex: integer) { + super(scene); + + this.fieldIndex = fieldIndex; + } + + start() { + super.start(); + + const enemyPokemon = this.scene.getEnemyField()[this.fieldIndex]; + const playerField = this.scene.getPlayerField(); + + this.scene.currentBattle.turnCommands[this.fieldIndex + BattleTarget.ENEMY] = { command: Command.FIGHT, move: enemyPokemon.getNextMove(), targetIndex: Utils.randInt(playerField.length) } + + this.end(); + } +} + +export class TurnStartPhase extends FieldPhase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + if (this.scene.arena.weather) + this.scene.unshiftPhase(new WeatherEffectPhase(this.scene, this.scene.arena.weather)); + + const field = this.scene.getField(); + const order = this.getOrder(); + + const moveOrder = order.slice(0); + + moveOrder.sort((a, b) => { + const aCommand = this.scene.currentBattle.turnCommands[a]; + const bCommand = this.scene.currentBattle.turnCommands[b]; + + if (aCommand.command !== bCommand.command) { + if (aCommand.command === Command.FIGHT) + return 1; + else if (bCommand.command === Command.FIGHT) + return -1; + } else if (aCommand.command === Command.FIGHT) { + const aPriority = allMoves[aCommand.move.move].priority; + const bPriority = allMoves[bCommand.move.move].priority; + + if (aPriority !== bPriority) + return aPriority < bPriority ? 1 : -1; + } + + const aIndex = order.indexOf(a); + const bIndex = order.indexOf(b); + + return aIndex < bIndex ? -1 : aIndex > bIndex ? 1 : 0; + }); + + for (let o of moveOrder) { + + const pokemon = field[o]; + const turnCommand = this.scene.currentBattle.turnCommands[o]; + + console.log(pokemon, field, o); + + switch (turnCommand.command) { + case Command.FIGHT: + const queuedMove = turnCommand.move; + if (!queuedMove) + continue; + const move = pokemon.getMoveset().find(m => m.moveId === queuedMove.move) || new PokemonMove(queuedMove.move); + if (pokemon.isPlayer()) { + if (turnCommand.cursor === -1) + this.scene.pushPhase(new PlayerMovePhase(this.scene, pokemon as PlayerPokemon, turnCommand.targetIndex, move)); + else { + const playerPhase = new PlayerMovePhase(this.scene, pokemon as PlayerPokemon, turnCommand.targetIndex, move, false, queuedMove.ignorePP); + this.scene.pushPhase(playerPhase); + } + } else + this.scene.pushPhase(new EnemyMovePhase(this.scene, pokemon as EnemyPokemon, turnCommand.targetIndex, move, false, queuedMove.ignorePP)); + break; + case Command.BALL: + this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targetIndex, turnCommand.cursor)); + break; + case Command.POKEMON: + case Command.RUN: + const isSwitch = turnCommand.command === Command.POKEMON; + if (isSwitch) + this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, pokemon.getFieldIndex(), turnCommand.cursor, true, turnCommand.args[0] as boolean)); + else + this.scene.unshiftPhase(new AttemptRunPhase(this.scene, pokemon.getFieldIndex())); + break; + } + } + + for (let o of order) { + if (field[o].status && field[o].status.isPostTurn()) + this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, !Math.floor(o / 2), o % 2)); + } + + this.scene.pushPhase(new TurnEndPhase(this.scene)); + + this.end(); + } +} + export class TurnEndPhase extends FieldPhase { constructor(scene: BattleScene) { super(scene); @@ -766,18 +924,18 @@ export class TurnEndPhase extends FieldPhase { const hasUsableBerry = !!this.scene.findModifier(m => m instanceof BerryModifier && m.shouldApply([ pokemon ]), pokemon.isPlayer()); if (hasUsableBerry) - this.scene.pushPhase(new BerryPhase(this.scene, pokemon.isPlayer())); + this.scene.pushPhase(new BerryPhase(this.scene, pokemon.isPlayer(), pokemon.getFieldIndex())); this.scene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon); - this.executeForBoth((pokemon: Pokemon) => applyPostTurnAbAttrs(PostTurnAbAttr, pokemon)); + this.executeForAll((pokemon: Pokemon) => applyPostTurnAbAttrs(PostTurnAbAttr, pokemon)); this.scene.applyModifiers(TurnHeldItemTransferModifier, pokemon.isPlayer(), pokemon); pokemon.battleSummonData.turnCount++; }; - this.executeForBoth(handlePokemon); + this.executeForAll(handlePokemon); this.scene.arena.lapseTags(); @@ -796,6 +954,11 @@ export class BattleEndPhase extends BattlePhase { start() { super.start(); + for (let pokemon of this.scene.getField()) { + if (pokemon) + pokemon.resetBattleSummonData(); + } + this.scene.clearEnemyModifiers(); const tempBattleStatBoosterModifiers = this.scene.getModifiers(TempBattleStatBoosterModifier) as TempBattleStatBoosterModifier[]; @@ -808,45 +971,19 @@ export class BattleEndPhase extends BattlePhase { } } -export abstract class PokemonPhase extends FieldPhase { - 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]; - } -} - export class CommonAnimPhase extends PokemonPhase { private anim: CommonAnim; + private targetIndex: integer; - constructor(scene: BattleScene, player: boolean, anim: CommonAnim) { - super(scene, player); + constructor(scene: BattleScene, player: boolean, fieldIndex: integer, targetIndex: integer, anim: CommonAnim) { + super(scene, player, fieldIndex); this.anim = anim; + this.targetIndex = targetIndex; } start() { - new CommonBattleAnim(this.anim, this.getPokemon(), this.player ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon()).play(this.scene, () => { + new CommonBattleAnim(this.anim, this.getPokemon(), (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField())[this.targetIndex]).play(this.scene, () => { this.end(); }); } @@ -854,15 +991,17 @@ export class CommonAnimPhase extends PokemonPhase { export abstract class MovePhase extends BattlePhase { protected pokemon: Pokemon; + protected targetIndex: integer; protected move: PokemonMove; protected followUp: boolean; protected ignorePp: boolean; protected cancelled: boolean; - constructor(scene: BattleScene, pokemon: Pokemon, move: PokemonMove, followUp?: boolean, ignorePp?: boolean) { + constructor(scene: BattleScene, pokemon: Pokemon, targetIndex: integer, move: PokemonMove, followUp?: boolean, ignorePp?: boolean) { super(scene); this.pokemon = pokemon; + this.targetIndex = targetIndex; this.move = move; this.followUp = !!followUp; this.ignorePp = !!ignorePp; @@ -891,7 +1030,7 @@ export abstract class MovePhase extends BattlePhase { return; } - const target = this.pokemon.getOpponent(); + const target = this.pokemon.getOpponent(this.targetIndex); if (!this.followUp && this.canMove()) this.pokemon.lapseTags(BattlerTagLapseType.MOVE); @@ -953,7 +1092,7 @@ export abstract class MovePhase extends BattlePhase { } if (activated) { this.scene.queueMessage(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))); + this.scene.unshiftPhase(new CommonAnimPhase(this.scene, this.pokemon.isPlayer(), this.pokemon.getFieldIndex(), this.targetIndex, CommonAnim.POISON + (this.pokemon.status.effect - 1))); doMove(); } else { if (healed) { @@ -969,39 +1108,41 @@ export abstract class MovePhase extends BattlePhase { end() { if (!this.followUp && this.canMove()) - this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.isPlayer())); + this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.isPlayer(), this.pokemon.getFieldIndex())); super.end(); } } export class PlayerMovePhase extends MovePhase { - constructor(scene: BattleScene, pokemon: PlayerPokemon, move: PokemonMove, followUp?: boolean, ignorePp?: boolean) { - super(scene, pokemon, move, followUp, ignorePp); + constructor(scene: BattleScene, pokemon: PlayerPokemon, targetIndex: integer, move: PokemonMove, followUp?: boolean, ignorePp?: boolean) { + super(scene, pokemon, targetIndex, move, followUp, ignorePp); } getEffectPhase(): MoveEffectPhase { - return new PlayerMoveEffectPhase(this.scene, this.move); + return new PlayerMoveEffectPhase(this.scene, this.pokemon.getFieldIndex(), this.targetIndex, this.move); } } export class EnemyMovePhase extends MovePhase { - constructor(scene: BattleScene, pokemon: EnemyPokemon, move: PokemonMove, followUp?: boolean, ignorePp?: boolean) { - super(scene, pokemon, move, followUp, ignorePp); + constructor(scene: BattleScene, pokemon: EnemyPokemon, targetIndex: integer, move: PokemonMove, followUp?: boolean, ignorePp?: boolean) { + super(scene, pokemon, targetIndex, move, followUp, ignorePp); } getEffectPhase(): MoveEffectPhase { - return new EnemyMoveEffectPhase(this.scene, this.move); + return new EnemyMoveEffectPhase(this.scene, this.pokemon.getFieldIndex(), this.targetIndex, this.move); } } abstract class MoveEffectPhase extends PokemonPhase { protected move: PokemonMove; + protected targetIndex: integer; - constructor(scene: BattleScene, player: boolean, move: PokemonMove) { - super(scene, player); + constructor(scene: BattleScene, player: boolean, fieldIndex: integer, targetIndex: integer, move: PokemonMove) { + super(scene, player, fieldIndex); this.move = move; + this.targetIndex = targetIndex; } start() { @@ -1038,7 +1179,7 @@ abstract class MoveEffectPhase extends PokemonPhase { const isProtected = !this.move.getMove().hasFlag(MoveFlags.IGNORE_PROTECT) && target.lapseTag(BattlerTagType.PROTECTED); - new MoveAnim(this.move.getMove().id as Moves, user).play(this.scene, () => { + new MoveAnim(this.move.getMove().id as Moves, user, this.targetIndex).play(this.scene, () => { const result = !isProtected ? target.apply(user, this.move) : MoveResult.NO_EFFECT; user.pushMoveHistory({ move: this.move.moveId, result: result, virtual: this.move.virtual }); if (result !== MoveResult.NO_EFFECT && result !== MoveResult.FAILED) { @@ -1118,54 +1259,44 @@ abstract class MoveEffectPhase extends PokemonPhase { abstract getUserPokemon(): Pokemon; - abstract getTargetPokemon(): Pokemon; + getTargetPokemon(): Pokemon { + return this.getUserPokemon().getOpponent(this.targetIndex); + } abstract getNewHitPhase(): MoveEffectPhase; } export class PlayerMoveEffectPhase extends MoveEffectPhase { - constructor(scene: BattleScene, move: PokemonMove) { - super(scene, true, move); + constructor(scene: BattleScene, fieldIndex: integer, targetIndex: integer, move: PokemonMove) { + super(scene, true, fieldIndex, targetIndex, move); } getUserPokemon(): Pokemon { - return this.scene.getPlayerPokemon(); - } - - getTargetPokemon(): Pokemon { - /*if (this.move.getMove().category === MoveCategory.STATUS) - return this.getUserPokemon();*/ - return this.scene.getEnemyPokemon(); + return this.scene.getPlayerField()[this.fieldIndex]; } getNewHitPhase() { - return new PlayerMoveEffectPhase(this.scene, this.move); + return new PlayerMoveEffectPhase(this.scene, this.fieldIndex, this.targetIndex, this.move); } } export class EnemyMoveEffectPhase extends MoveEffectPhase { - constructor(scene: BattleScene, move: PokemonMove) { - super(scene, false, move); + constructor(scene: BattleScene, fieldIndex: integer, targetIndex: integer, move: PokemonMove) { + super(scene, false, fieldIndex, targetIndex, move); } getUserPokemon(): Pokemon { - return this.scene.getEnemyPokemon(); - } - - getTargetPokemon(): Pokemon { - /*if (this.move.getMove().category === MoveCategory.STATUS) - return this.getUserPokemon();*/ - return this.scene.getPlayerPokemon(); + return this.scene.getEnemyField()[this.fieldIndex]; } getNewHitPhase() { - return new EnemyMoveEffectPhase(this.scene, this.move); + return new EnemyMoveEffectPhase(this.scene, this.fieldIndex, this.targetIndex, this.move); } } export class MoveEndPhase extends PokemonPhase { - constructor(scene: BattleScene, player: boolean) { - super(scene, player); + constructor(scene: BattleScene, player: boolean, fieldIndex: integer) { + super(scene, player, fieldIndex); } start() { @@ -1202,7 +1333,7 @@ export class MoveAnimTestPhase extends BattlePhase { initMoveAnim(moveId).then(() => { loadMoveAnimAssets(this.scene, [ moveId ], true) .then(() => { - new MoveAnim(moveId, player ? this.scene.getPlayerPokemon() : this.scene.getEnemyPokemon()).play(this.scene, () => { + new MoveAnim(moveId, player ? this.scene.getPlayerPokemon() : this.scene.getEnemyPokemon(), 0).play(this.scene, () => { if (player) this.playMoveAnim(moveQueue, false); else @@ -1214,8 +1345,8 @@ export class MoveAnimTestPhase extends BattlePhase { } export class ShowAbilityPhase extends PokemonPhase { - constructor(scene: BattleScene, player: boolean) { - super(scene, player); + constructor(scene: BattleScene, player: boolean, fieldIndex: integer) { + super(scene, player, fieldIndex); } start() { @@ -1230,8 +1361,8 @@ export class StatChangePhase extends PokemonPhase { private selfTarget: boolean; private levels: integer; - constructor(scene: BattleScene, player: boolean, selfTarget: boolean, stats: BattleStat[], levels: integer) { - super(scene, player); + constructor(scene: BattleScene, player: boolean, fieldIndex: integer, selfTarget: boolean, stats: BattleStat[], levels: integer) { + super(scene, player, fieldIndex); const allStats = Utils.getEnumValues(BattleStat); this.selfTarget = selfTarget; @@ -1319,7 +1450,7 @@ export class WeatherEffectPhase extends CommonAnimPhase { private weather: Weather; constructor(scene: BattleScene, weather: Weather) { - super(scene, true, CommonAnim.SUNNY + (weather.weatherType - 1)); + super(scene, true, 0, 0, CommonAnim.SUNNY + (weather.weatherType - 1)); this.weather = weather; } @@ -1328,7 +1459,7 @@ export class WeatherEffectPhase extends CommonAnimPhase { const cancelled = new Utils.BooleanHolder(false); - this.executeForBoth((pokemon: Pokemon) => applyPreWeatherEffectAbAttrs(SuppressWeatherEffectAbAttr, pokemon, this.weather, cancelled)); + this.executeForAll((pokemon: Pokemon) => applyPreWeatherEffectAbAttrs(SuppressWeatherEffectAbAttr, pokemon, this.weather, cancelled)); if (!cancelled.value) { const inflictDamage = (pokemon: Pokemon) => { @@ -1340,11 +1471,11 @@ export class WeatherEffectPhase extends CommonAnimPhase { return; this.scene.queueMessage(getWeatherDamageMessage(this.weather.weatherType, pokemon)); - this.scene.unshiftPhase(new DamagePhase(this.scene, pokemon.isPlayer())); + this.scene.unshiftPhase(new DamagePhase(this.scene, pokemon.isPlayer(), pokemon.getFieldIndex())); pokemon.damage(Math.ceil(pokemon.getMaxHp() / 16)); }; - this.executeForBoth((pokemon: Pokemon) => { + this.executeForAll((pokemon: Pokemon) => { const immune = !pokemon || !!pokemon.getTypes().filter(t => this.weather.isTypeDamageImmune(t)).length; if (!immune) inflictDamage(pokemon); @@ -1353,7 +1484,7 @@ export class WeatherEffectPhase extends CommonAnimPhase { } this.scene.ui.showText(getWeatherLapseMessage(this.weather.weatherType), null, () => { - this.executeForBoth((pokemon: Pokemon) => applyPostWeatherLapseAbAttrs(PostWeatherLapseAbAttr, pokemon, this.weather)); + this.executeForAll((pokemon: Pokemon) => applyPostWeatherLapseAbAttrs(PostWeatherLapseAbAttr, pokemon, this.weather)); super.start(); }); @@ -1365,8 +1496,8 @@ export class ObtainStatusEffectPhase extends PokemonPhase { private cureTurn: integer; private sourceText: string; - constructor(scene: BattleScene, player: boolean, statusEffect: StatusEffect, cureTurn?: integer, sourceText?: string) { - super(scene, player); + constructor(scene: BattleScene, player: boolean, fieldIndex: integer, statusEffect: StatusEffect, cureTurn?: integer, sourceText?: string) { + super(scene, player, fieldIndex); this.statusEffect = statusEffect; this.cureTurn = cureTurn; @@ -1383,7 +1514,7 @@ export class ObtainStatusEffectPhase extends PokemonPhase { new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect - 1), pokemon).play(this.scene, () => { this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectObtainText(this.statusEffect, this.sourceText))); if (pokemon.status.isPostTurn()) - this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, this.player)); + this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, this.player, this.fieldIndex)); this.end(); }); return; @@ -1395,8 +1526,8 @@ export class ObtainStatusEffectPhase extends PokemonPhase { } export class PostTurnStatusEffectPhase extends PokemonPhase { - constructor(scene: BattleScene, player: boolean) { - super(scene, player); + constructor(scene: BattleScene, player: boolean, fieldIndex: integer) { + super(scene, player, fieldIndex); } start() { @@ -1453,8 +1584,8 @@ export class MessagePhase extends BattlePhase { export class DamagePhase extends PokemonPhase { private damageResult: DamageResult; - constructor(scene: BattleScene, player: boolean, damageResult?: DamageResult) { - super(scene, player); + constructor(scene: BattleScene, player: boolean, fieldIndex: integer, damageResult?: DamageResult) { + super(scene, player, fieldIndex); this.damageResult = damageResult || MoveResult.EFFECTIVE; } @@ -1491,8 +1622,8 @@ export class DamagePhase extends PokemonPhase { } export class FaintPhase extends PokemonPhase { - constructor(scene: BattleScene, player: boolean) { - super(scene, player); + constructor(scene: BattleScene, player: boolean, fieldIndex: integer) { + super(scene, player, fieldIndex); } start() { @@ -1501,17 +1632,14 @@ export class FaintPhase extends PokemonPhase { this.scene.queueMessage(getPokemonMessage(this.getPokemon(), ' fainted!'), null, true); if (this.player) { - this.scene.unshiftPhase(this.scene.getParty().filter(p => p.hp).length ? new SwitchPhase(this.scene, true, false) : new GameOverPhase(this.scene)); + this.scene.unshiftPhase(this.scene.getParty().filter(p => p.hp).length ? new SwitchPhase(this.scene, this.fieldIndex, true, false) : new GameOverPhase(this.scene)); } else - this.scene.unshiftPhase(new VictoryPhase(this.scene)); + this.scene.unshiftPhase(new VictoryPhase(this.scene, this.fieldIndex)); const pokemon = this.getPokemon(); pokemon.lapseTags(BattlerTagLapseType.FAINT); - if (pokemon.isPlayer()) - this.scene.getEnemyPokemon()?.removeTagsBySourceId(pokemon.id); - else - this.scene.getPlayerPokemon()?.removeTagsBySourceId(pokemon.id); + this.scene.getField().filter(p => p && p !== pokemon).forEach(p => p.removeTagsBySourceId(pokemon.id)); pokemon.faintCry(() => { pokemon.hideInfo(); @@ -1536,8 +1664,8 @@ export class FaintPhase extends PokemonPhase { } export class VictoryPhase extends PokemonPhase { - constructor(scene: BattleScene) { - super(scene, true); + constructor(scene: BattleScene, targetIndex: integer) { + super(scene, true, targetIndex); } start() { @@ -1548,7 +1676,7 @@ export class VictoryPhase extends PokemonPhase { const expShareModifier = this.scene.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier; const expBalanceModifier = this.scene.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier; const multipleParticipantExpBonusModifier = this.scene.findModifier(m => m instanceof MultipleParticipantExpBonusModifier) as MultipleParticipantExpBonusModifier; - const expValue = this.scene.getEnemyPokemon().getExpValue(); + const expValue = this.getPokemon().getExpValue(); const expPartyMembers = party.filter(p => p.hp && p.level < maxExpLevel); const partyMemberExp = []; for (let partyMember of expPartyMembers) { @@ -1603,13 +1731,15 @@ export class VictoryPhase extends PokemonPhase { this.scene.unshiftPhase(new ExpPhase(this.scene, partyMemberIndex, exp)); } } - - this.scene.pushPhase(new BattleEndPhase(this.scene)); - if (this.scene.currentBattle.waveIndex < this.scene.finalWave) { - this.scene.pushPhase(new SelectModifierPhase(this.scene)); - this.scene.newBattle(); - } else - this.scene.pushPhase(new GameOverPhase(this.scene, true)); + + if (!this.scene.currentBattle.enemyField.filter(p => p?.hp).length) { + this.scene.pushPhase(new BattleEndPhase(this.scene)); + if (this.scene.currentBattle.waveIndex < this.scene.finalWave) { + this.scene.pushPhase(new SelectModifierPhase(this.scene)); + this.scene.newBattle(); + } else + this.scene.pushPhase(new GameOverPhase(this.scene, true)); + } this.end(); } @@ -1677,12 +1807,14 @@ export class UnlockPhase extends BattlePhase { } export class SwitchPhase extends BattlePhase { + protected fieldIndex: integer; private isModal: boolean; private doReturn: boolean; - constructor(scene: BattleScene, isModal: boolean, doReturn: boolean) { + constructor(scene: BattleScene, fieldIndex: integer, isModal: boolean, doReturn: boolean) { super(scene); + this.fieldIndex = fieldIndex; this.isModal = isModal; this.doReturn = doReturn; } @@ -1690,9 +1822,9 @@ export class SwitchPhase extends BattlePhase { start() { super.start(); - this.scene.ui.setMode(Mode.PARTY, this.isModal ? PartyUiMode.FAINT_SWITCH : PartyUiMode.POST_BATTLE_SWITCH, (slotIndex: integer, option: PartyOption) => { + this.scene.ui.setMode(Mode.PARTY, this.isModal ? PartyUiMode.FAINT_SWITCH : PartyUiMode.POST_BATTLE_SWITCH, this.fieldIndex, (slotIndex: integer, option: PartyOption) => { if (slotIndex && slotIndex < 6) - this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, slotIndex, this.doReturn, option === PartyOption.PASS_BATON)); + this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, this.fieldIndex, slotIndex, this.doReturn, option === PartyOption.PASS_BATON)); this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); }, PartyUiHandler.FilterNonFainted); } @@ -1850,8 +1982,8 @@ export class LearnMovePhase extends PartyMemberPokemonPhase { } export class BerryPhase extends CommonAnimPhase { - constructor(scene: BattleScene, player: boolean) { - super(scene, player, CommonAnim.USE_ITEM); + constructor(scene: BattleScene, player: boolean, fieldIndex: integer) { + super(scene, player, fieldIndex, 0, CommonAnim.USE_ITEM); } start() { @@ -1879,8 +2011,8 @@ export class PokemonHealPhase extends CommonAnimPhase { private showFullHpMessage: boolean; private skipAnim: boolean; - constructor(scene: BattleScene, player: boolean, hpHealed: integer, message: string, showFullHpMessage: boolean, skipAnim?: boolean) { - super(scene, player, CommonAnim.HEALTH_UP); + constructor(scene: BattleScene, player: boolean, fieldIndex: integer, hpHealed: integer, message: string, showFullHpMessage: boolean, skipAnim?: boolean) { + super(scene, player, fieldIndex, 0, CommonAnim.HEALTH_UP); this.hpHealed = hpHealed; this.message = message; @@ -1921,13 +2053,13 @@ export class PokemonHealPhase extends CommonAnimPhase { } } -export class AttemptCapturePhase extends BattlePhase { +export class AttemptCapturePhase extends PokemonPhase { private pokeballType: PokeballType; private pokeball: Phaser.GameObjects.Sprite; private originalY: number; - constructor(scene: BattleScene, pokeballType: PokeballType) { - super(scene); + constructor(scene: BattleScene, targetIndex: integer, pokeballType: PokeballType) { + super(scene, false, targetIndex); this.pokeballType = pokeballType; } @@ -1937,7 +2069,7 @@ export class AttemptCapturePhase extends BattlePhase { this.scene.pokeballCounts[this.pokeballType]--; - const pokemon = this.scene.getEnemyPokemon(); + const pokemon = this.getPokemon(); this.originalY = pokemon.y; const _3m = 3 * pokemon.getMaxHp(); @@ -2024,7 +2156,7 @@ export class AttemptCapturePhase extends BattlePhase { } failCatch(shakeCount: integer) { - const pokemon = this.scene.getEnemyPokemon(); + const pokemon = this.getPokemon(); this.scene.sound.play('pb_rel'); pokemon.setY(this.originalY); @@ -2049,8 +2181,8 @@ export class AttemptCapturePhase extends BattlePhase { } catch() { - const pokemon = this.scene.getEnemyPokemon(); - this.scene.unshiftPhase(new VictoryPhase(this.scene)); + const pokemon = this.getPokemon() as EnemyPokemon; + this.scene.unshiftPhase(new VictoryPhase(this.scene, this.fieldIndex)); this.scene.ui.showText(`${pokemon.name} was caught!`, null, () => { const end = () => { this.removePb(); @@ -2060,7 +2192,7 @@ export class AttemptCapturePhase extends BattlePhase { const newPokemon = pokemon.addToParty(); const modifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false); Promise.all(modifiers.map(m => this.scene.addModifier(m))).then(() => { - this.scene.getPlayerPokemon().removeTagsBySourceId(pokemon.id); + this.scene.getPlayerField().forEach(playerPokemon => playerPokemon.removeTagsBySourceId(pokemon.id)); pokemon.hp = 0; this.scene.clearEnemyModifiers(); this.scene.field.remove(pokemon, true); @@ -2075,7 +2207,7 @@ export class AttemptCapturePhase extends BattlePhase { 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.PARTY, PartyUiMode.RELEASE, this.fieldIndex, (slotIndex: integer, _option: PartyOption) => { this.scene.ui.setMode(Mode.MESSAGE).then(() => { if (slotIndex < 6) addToParty(); @@ -2109,31 +2241,33 @@ export class AttemptCapturePhase extends BattlePhase { } } -export class AttemptRunPhase extends BattlePhase { - constructor(scene: BattleScene) { - super(scene); +export class AttemptRunPhase extends PokemonPhase { + constructor(scene: BattleScene, fieldIndex: integer) { + super(scene, true, fieldIndex); } start() { super.start(); - const playerPokemon = this.scene.getPlayerPokemon(); - const enemyPokemon = this.scene.getEnemyPokemon(); + const playerPokemon = this.getPokemon(); + const enemyField = this.scene.getEnemyField(); - const escapeChance = (((playerPokemon.stats[Stat.SPD] * 128) / enemyPokemon.stats[Stat.SPD]) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256; + const enemySpeed = enemyField.reduce((total: integer, enemyPokemon: Pokemon) => total + enemyPokemon.stats[Stat.SPD], 0) / enemyField.length; + + const escapeChance = (((playerPokemon.stats[Stat.SPD] * 128) / enemySpeed) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256; if (Utils.randInt(256) < escapeChance) { this.scene.sound.play('flee'); this.scene.queueMessage('You got away safely!', null, true, 500); this.scene.tweens.add({ - targets: [ this.scene.arenaEnemy, enemyPokemon ], + targets: [ this.scene.arenaEnemy, enemyField ].flat(), alpha: 0, duration: 250, ease: 'Sine.easeIn' }); - enemyPokemon.hp = 0; + enemyField.forEach(enemyPokemon => enemyPokemon.hp = 0); this.scene.pushPhase(new BattleEndPhase(this.scene)); this.scene.newBattle(); @@ -2164,7 +2298,7 @@ export class SelectModifierPhase extends BattlePhase { super.end(); return; } else if (cursor >= typeOptions.length) { - this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, (fromSlotIndex: integer, itemIndex: integer, toSlotIndex: integer) => { + this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, toSlotIndex: integer) => { if (toSlotIndex !== undefined && fromSlotIndex < 6 && toSlotIndex < 6 && fromSlotIndex !== toSlotIndex && itemIndex > -1) { this.scene.ui.setMode(Mode.MODIFIER_SELECT).then(() => { const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier @@ -2189,7 +2323,7 @@ export class SelectModifierPhase extends BattlePhase { if (modifierType instanceof PokemonModifierType) { const pokemonModifierType = modifierType as PokemonModifierType; const isMoveModifier = modifierType instanceof PokemonMoveModifierType; - this.scene.ui.setModeWithoutClear(Mode.PARTY, !isMoveModifier ? PartyUiMode.MODIFIER : PartyUiMode.MOVE_MODIFIER, (slotIndex: integer, option: PartyOption) => { + this.scene.ui.setModeWithoutClear(Mode.PARTY, !isMoveModifier ? PartyUiMode.MODIFIER : PartyUiMode.MOVE_MODIFIER, -1, (slotIndex: integer, option: PartyOption) => { if (slotIndex < 6) { this.scene.ui.setMode(Mode.MODIFIER_SELECT).then(() => { const modifierType = typeOptions[cursor].type; @@ -2214,8 +2348,8 @@ export class SelectModifierPhase extends BattlePhase { } export class ShinySparklePhase extends PokemonPhase { - constructor(scene: BattleScene, player: boolean) { - super(scene, player); + constructor(scene: BattleScene, player: boolean, fieldIndex: integer) { + super(scene, player, fieldIndex); } start() { diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 8a8557a47be..7458cff259a 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1,7 +1,7 @@ import Phaser from 'phaser'; import { Biome } from './data/biome'; import UI from './ui/ui'; -import { EncounterPhase, SummonPhase, CommandPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, CheckLoadPhase } from './battle-phases'; +import { EncounterPhase, SummonPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, CheckLoadPhase, TurnInitPhase } from './battle-phases'; import Pokemon, { PlayerPokemon, EnemyPokemon } from './pokemon'; import PokemonSpecies, { allSpecies, getPokemonSpecies, initSpecies } from './data/pokemon-species'; import * as Utils from './utils'; @@ -9,7 +9,6 @@ import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, P import { PokeballType } from './data/pokeball'; import { Species } from './data/species'; import { initAutoPlay } from './system/auto-play'; -import { Battle } from './battle'; import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from './data/battle-anims'; import { BattlePhase } from './battle-phase'; import { initGameSpeed } from './system/game-speed'; @@ -21,6 +20,7 @@ import { Moves, initMoves } from './data/move'; import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave } from './modifier/modifier-type'; import AbilityBar from './ui/ability-bar'; import { BlockItemTheftAbAttr, applyAbAttrs, initAbilities } from './data/ability'; +import Battle from './battle'; const enableAuto = true; const quickStart = false; @@ -446,21 +446,35 @@ export default class BattleScene extends Phaser.Scene { return this.party; } - getEnemyParty(): EnemyPokemon[] { - return this.getEnemyPokemon() ? [ this.getEnemyPokemon() ] : []; - } - getPlayerPokemon(): PlayerPokemon { return this.getParty()[0]; } + getPlayerField(): PlayerPokemon[] { + const party = this.getParty(); + return party.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1)); + } + getEnemyPokemon(): EnemyPokemon { - return this.currentBattle?.enemyPokemon; + return this.currentBattle?.enemyField[0]; + } + + getEnemyField(): EnemyPokemon[] { + return this.currentBattle?.enemyField || []; + } + + getField(): Pokemon[] { + const ret = new Array(4).fill(null); + const playerField = this.getPlayerField(); + const enemyField = this.getEnemyField(); + ret.splice(0, playerField.length, ...playerField); + ret.splice(2, enemyField.length, ...enemyField); + return ret; } getPokemonById(pokemonId: integer): Pokemon { const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId); - return findInParty(this.getParty()) || findInParty(this.getEnemyParty()); + return findInParty(this.getParty()) || findInParty(this.getEnemyField()); } reset(): void { @@ -475,7 +489,7 @@ export default class BattleScene extends Phaser.Scene { for (let p of this.getParty()) p.destroy(); this.party = []; - for (let p of this.getEnemyParty()) + for (let p of this.getEnemyField()) p.destroy(); this.currentBattle = null; @@ -493,10 +507,10 @@ export default class BattleScene extends Phaser.Scene { this.trainer.setPosition(406, 132); } - newBattle(waveIndex?: integer): Battle { + newBattle(waveIndex?: integer, double?: boolean): Battle { if (!waveIndex) { if (this.currentBattle) { - this.getEnemyPokemon().destroy(); + this.getEnemyField().forEach(enemyPokemon => enemyPokemon.destroy()); if (this.currentBattle.waveIndex % 10) this.pushPhase(new NextEncounterPhase(this)); else { @@ -509,12 +523,14 @@ export default class BattleScene extends Phaser.Scene { else { this.arena.playBgm(); this.pushPhase(new EncounterPhase(this)); - this.pushPhase(new SummonPhase(this)); + this.pushPhase(new SummonPhase(this, 0)); + if (double === undefined || double) + this.pushPhase(new SummonPhase(this, 1)); } } } - this.currentBattle = new Battle(waveIndex || ((this.currentBattle?.waveIndex || (startingWave - 1)) + 1)); + this.currentBattle = new Battle(waveIndex || ((this.currentBattle?.waveIndex || (startingWave - 1)) + 1), double === undefined || double); return this.currentBattle; } @@ -699,7 +715,7 @@ export default class BattleScene extends Phaser.Scene { } populatePhaseQueue(): void { - this.phaseQueue.push(new CommandPhase(this)); + this.phaseQueue.push(new TurnInitPhase(this)); } addModifier(modifier: Modifier, playSound?: boolean, virtual?: boolean): Promise { @@ -828,7 +844,9 @@ export default class BattleScene extends Phaser.Scene { } if (isBoss) count = Math.max(count, Math.floor(chances / 2)); - getEnemyModifierTypesForWave(waveIndex, count, this.getEnemyParty()).map(mt => mt.newModifier(this.getEnemyPokemon()).add(this.enemyModifiers, false)); + const enemyField = this.getEnemyField(); + getEnemyModifierTypesForWave(waveIndex, count, this.getEnemyField()) + .map(mt => mt.newModifier(enemyField[enemyField.length === 1 ? 0 : Utils.randInt(enemyField.length)]).add(this.enemyModifiers, false)); this.updateModifiers(false).then(() => resolve()); }); @@ -855,7 +873,7 @@ export default class BattleScene extends Phaser.Scene { modifiers.splice(modifiers.indexOf(modifier), 1); } - this.updatePartyForModifiers(player ? this.getParty() : this.getEnemyParty()).then(() => { + this.updatePartyForModifiers(player ? this.getParty() : this.getEnemyField()).then(() => { (player ? this.modifierBar : this.enemyModifierBar).updateModifiers(modifiers); if (!player) this.updateWaveCountPosition(); diff --git a/src/battle.ts b/src/battle.ts index 474b7a249f4..c1ebc4f7ac0 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -1,18 +1,44 @@ -import { EnemyPokemon, PlayerPokemon } from "./pokemon"; +import { EnemyPokemon, PlayerPokemon, QueuedMove } from "./pokemon"; +import { Command } from "./ui/command-ui-handler"; import * as Utils from "./utils"; -export class Battle { +export enum BattleTarget { + PLAYER, + PLAYER_2, + ENEMY, + ENEMY_2 +} + +interface TurnCommand { + command: Command; + cursor?: integer; + move?: QueuedMove; + targetIndex?: integer; + args?: any[]; +}; + +interface TurnCommands { + [key: integer]: TurnCommand +} + +export default class Battle { public waveIndex: integer; - public enemyLevel: integer; - public enemyPokemon: EnemyPokemon; + public enemyLevels: integer[]; + public enemyField: EnemyPokemon[]; + public double: boolean; public turn: integer; + public turnCommands: TurnCommands; public playerParticipantIds: Set = new Set(); public escapeAttempts: integer = 0; - constructor(waveIndex: integer) { + constructor(waveIndex: integer, double: boolean) { this.waveIndex = waveIndex; - this.enemyLevel = this.getLevelForWave(); - this.turn = 1; + this.enemyLevels = new Array(double ? 2 : 1).fill(null).map(() => this.getLevelForWave()); + this.enemyField = []; + this.double = double; + this.turn = 0; + + this.incrementTurn(); } private getLevelForWave(): number { @@ -31,6 +57,7 @@ export class Battle { incrementTurn() { this.turn++; + this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattleTarget).map(bt => [ bt, null ])); } addParticipant(playerPokemon: PlayerPokemon): void { diff --git a/src/data/ability.ts b/src/data/ability.ts index 3421a3d2a41..67df80c0a60 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -157,7 +157,7 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { if (ret && pokemon.getHpRatio() < 1) { const scene = pokemon.scene; - scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.isPlayer(), Math.max(Math.floor(pokemon.getMaxHp() / 4), 1), getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nrestored its HP a little!`), true)); + scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.isPlayer(), pokemon.getFieldIndex(), Math.max(Math.floor(pokemon.getMaxHp() / 4), 1), getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nrestored its HP a little!`), true)); return true; } @@ -181,7 +181,7 @@ class TypeImmunityStatChangeAbAttr extends TypeImmunityAbAttr { if (ret) { cancelled.value = true; - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), true, [ this.stat ], this.levels)); + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), true, [ this.stat ], this.levels)); } return ret; @@ -406,9 +406,9 @@ export class PostSummonStatChangeAbAttr extends PostSummonAbAttr { } applyPostSummon(pokemon: Pokemon, args: any[]): boolean { - const statChangePhase = new StatChangePhase(pokemon.scene, pokemon.isPlayer() === this.selfTarget, this.selfTarget, this.stats, this.levels); + const statChangePhase = new StatChangePhase(pokemon.scene, pokemon.isPlayer() === this.selfTarget, pokemon.getFieldIndex(), this.selfTarget, this.stats, this.levels); - if (!this.selfTarget && !pokemon.getOpponent()?.summonData) + if (!this.selfTarget && !pokemon.getOpponent(0)?.summonData) pokemon.scene.pushPhase(statChangePhase); // TODO: This causes the ability bar to be shown at the wrong time else pokemon.scene.unshiftPhase(statChangePhase); @@ -576,7 +576,7 @@ export class PostTurnAbAttr extends AbAttr { export class PostTurnSpeedBoostAbAttr extends PostTurnAbAttr { applyPostTurn(pokemon: Pokemon, args: any[]): boolean { - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), true, [ BattleStat.SPD ], 1)); + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), true, [ BattleStat.SPD ], 1)); return true; } } @@ -594,7 +594,8 @@ export class PostTurnHealAbAttr extends PostTurnAbAttr { applyPostTurn(pokemon: Pokemon, args: any[]): boolean { if (pokemon.getHpRatio() < 1) { const scene = pokemon.scene; - scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.isPlayer(), Math.max(Math.floor(pokemon.getMaxHp() / 16), 1), getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nrestored its HP a little!`), true)); + scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.isPlayer(), pokemon.getFieldIndex(), + Math.max(Math.floor(pokemon.getMaxHp() / 16), 1), getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nrestored its HP a little!`), true)); return true; } @@ -632,7 +633,7 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr { applyPostWeatherLapse(pokemon: Pokemon, weather: Weather, args: any[]): boolean { if (pokemon.getHpRatio() < 1) { const scene = pokemon.scene; - scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.isPlayer(), Math.max(Math.floor(pokemon.getMaxHp() / (16 / this.healFactor)), 1), getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nrestored its HP a little!`), true)); + scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.isPlayer(), pokemon.getFieldIndex(), Math.max(Math.floor(pokemon.getMaxHp() / (16 / this.healFactor)), 1), getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nrestored its HP a little!`), true)); return true; } @@ -988,7 +989,7 @@ function canApplyAttr(pokemon: Pokemon, attr: AbAttr): boolean { } function queueShowAbility(pokemon: Pokemon): void { - pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.isPlayer())); + pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex())); pokemon.scene.clearPhaseQueueSplice(); } @@ -1232,8 +1233,8 @@ export function initAbilities() { new Ability(Abilities.MAGMA_ARMOR, "Magma Armor", "Prevents the POKéMON from becoming frozen.", 3) .attr(StatusEffectImmunityAbAttr, StatusEffect.FREEZE), new Ability(Abilities.MAGNET_PULL, "Magnet Pull", "Prevents STEEL-type POKéMON from escaping.", 3) - .attr(ArenaTrapAbAttr) - .condition((pokemon: Pokemon) => pokemon.getOpponent()?.isOfType(Type.STEEL)), + /*.attr(ArenaTrapAbAttr) + .condition((pokemon: Pokemon) => pokemon.getOpponent()?.isOfType(Type.STEEL))*/, // TODO: Rework new Ability(Abilities.MARVEL_SCALE, "Marvel Scale (N)", "Ups DEFENSE if there is a status problem.", 3), new Ability(Abilities.MINUS, "Minus (N)", "Ups SP. ATK if another POKéMON has PLUS or MINUS.", 3), new Ability(Abilities.NATURAL_CURE, "Natural Cure (N)", "All status problems heal when it switches out.", 3), diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index de0fa347abf..ff6f8b262bd 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -143,8 +143,7 @@ class SpikesTag extends ArenaTrapTag { super.onAdd(arena); const source = arena.scene.getPokemonById(this.sourceId); - const target = source.getOpponent(); - arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${target.name}'s feet!`); + arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${source.getOpponentDescriptor()}'s feet!`); } activateTrap(pokemon: Pokemon): boolean { @@ -170,15 +169,14 @@ class ToxicSpikesTag extends ArenaTrapTag { super.onAdd(arena); const source = arena.scene.getPokemonById(this.sourceId); - const target = source.getOpponent(); - arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${target.name}'s feet!`); + arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${source.getOpponentDescriptor()}'s feet!`); } activateTrap(pokemon: Pokemon): boolean { if (!pokemon.status && (!pokemon.isOfType(Type.FLYING) || pokemon.getTag(BattlerTagType.IGNORE_FLYING) || pokemon.scene.arena.getTag(ArenaTagType.GRAVITY))) { const toxic = this.layers > 1; - pokemon.scene.unshiftPhase(new ObtainStatusEffectPhase(pokemon.scene, pokemon.isPlayer(), + pokemon.scene.unshiftPhase(new ObtainStatusEffectPhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), !toxic ? StatusEffect.POISON : StatusEffect.TOXIC, null, `the ${this.getMoveName()}`)); return true; } @@ -196,8 +194,7 @@ class StealthRockTag extends ArenaTrapTag { super.onAdd(arena); const source = arena.scene.getPokemonById(this.sourceId); - const target = source.getOpponent(); - arena.scene.queueMessage(`Pointed stones float in the air\naround ${target.name}!`); + arena.scene.queueMessage(`Pointed stones float in the air\naround ${source.getOpponentDescriptor()}!`); } activateTrap(pokemon: Pokemon): boolean { @@ -242,8 +239,8 @@ export class TrickRoomTag extends ArenaTag { } apply(args: any[]): boolean { - const speedDelayed = args[0] as Utils.BooleanHolder; - speedDelayed.value = !speedDelayed.value; + const speedReversed = args[0] as Utils.BooleanHolder; + speedReversed.value = !speedReversed.value; return true; } diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 6275fec7cce..5e245f7cb2f 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -831,8 +831,8 @@ export class CommonBattleAnim extends BattleAnim { export class MoveAnim extends BattleAnim { public move: Moves; - constructor(move: Moves, user: Pokemon) { - super(user, getMoveTarget(user, move)); + constructor(move: Moves, user: Pokemon, targetIndex: integer) { + super(user, getMoveTarget(user, targetIndex, move)); this.move = move; } @@ -852,7 +852,7 @@ export class MoveChargeAnim extends MoveAnim { private chargeAnim: ChargeAnim; constructor(chargeAnim: ChargeAnim, move: Moves, user: Pokemon) { - super(move, user); + super(move, user, 0); this.chargeAnim = chargeAnim; } diff --git a/src/data/battler-tag.ts b/src/data/battler-tag.ts index 595125d97fb..d593bd571c0 100644 --- a/src/data/battler-tag.ts +++ b/src/data/battler-tag.ts @@ -177,7 +177,7 @@ export class ConfusedTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.CONFUSION)); + pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), 0, CommonAnim.CONFUSION)); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' became\nconfused!')); } @@ -198,14 +198,14 @@ export class ConfusedTag extends BattlerTag { if (ret) { pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nconfused!')); - pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.CONFUSION)); + pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), 0, CommonAnim.CONFUSION)); if (Utils.randInt(2)) { const atk = pokemon.getBattleStat(Stat.ATK); const def = pokemon.getBattleStat(Stat.DEF); const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * ((Utils.randInt(15) + 85) / 100)); pokemon.scene.queueMessage('It hurt itself in its\nconfusion!'); - pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer())); + pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex())); pokemon.damage(damage); (pokemon.scene.getCurrentPhase() as MovePhase).cancel(); } @@ -245,7 +245,7 @@ export class InfatuatedTag extends BattlerTag { if (ret) { pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is in love\nwith ${pokemon.scene.getPokemonById(this.sourceId).name}!`)); - pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.ATTRACT)); + pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), 0, CommonAnim.ATTRACT)); if (Utils.randInt(2)) { pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nimmobilized by love!')); @@ -282,12 +282,13 @@ export class SeedTag extends BattlerTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) { - pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, !pokemon.isPlayer(), CommonAnim.LEECH_SEED)); + const source = pokemon.scene.getPokemonById(this.sourceId); + pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.isPlayer(), source.getFieldIndex(), pokemon.getFieldIndex(), CommonAnim.LEECH_SEED)); const damage = Math.max(Math.floor(pokemon.getMaxHp() / 8), 1); - pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer())); + pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex())); pokemon.damage(damage); - pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, !pokemon.isPlayer(), damage, getPokemonMessage(pokemon, '\'s health is\nsapped by LEECH SEED!'), false, true)); + pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, !source.isPlayer(), source.getFieldIndex(), damage, getPokemonMessage(pokemon, '\'s health is\nsapped by LEECH SEED!'), false, true)); } return ret; @@ -320,10 +321,10 @@ export class NightmareTag extends BattlerTag { if (ret) { pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is locked\nin a NIGHTMARE!')); - pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.CURSE)); // TODO: Update animation type + pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), 0, CommonAnim.CURSE)); // TODO: Update animation type const damage = Math.ceil(pokemon.getMaxHp() / 4); - pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer())); + pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex())); pokemon.damage(damage); } @@ -344,7 +345,7 @@ export class IngrainTag extends TrappedTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) - pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), Math.floor(pokemon.getMaxHp() / 16), + pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), Math.floor(pokemon.getMaxHp() / 16), getPokemonMessage(pokemon, ` absorbed\nnutrients with its roots!`), true)); return ret; @@ -374,7 +375,8 @@ export class AquaRingTag extends BattlerTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) - pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), Math.floor(pokemon.getMaxHp() / 16), `${this.getMoveName()} restored\n${pokemon.name}\'s HP!`, true)); + pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), + Math.floor(pokemon.getMaxHp() / 16), `${this.getMoveName()} restored\n${pokemon.name}\'s HP!`, true)); return ret; } @@ -393,7 +395,7 @@ export class DrowsyTag extends BattlerTag { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { if (!super.lapse(pokemon, lapseType)) { - pokemon.scene.unshiftPhase(new ObtainStatusEffectPhase(pokemon.scene, pokemon.isPlayer(), StatusEffect.SLEEP)); + pokemon.scene.unshiftPhase(new ObtainStatusEffectPhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), StatusEffect.SLEEP)); return false; } @@ -423,10 +425,10 @@ export abstract class DamagingTrapTag extends TrappedTag { if (ret) { pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is hurt\nby ${this.getMoveName()}!`)); - pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), this.commonAnim)); + pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), 0, this.commonAnim)); const damage = Math.ceil(pokemon.getMaxHp() / 16); - pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer())); + pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex())); pokemon.damage(damage); } @@ -541,7 +543,7 @@ export class TruantTag extends BattlerTag { if (lastMove && lastMove.move !== Moves.NONE) { (pokemon.scene.getCurrentPhase() as MovePhase).cancel(); - pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.isPlayer())); + pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex())); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is\nloafing around!')); } diff --git a/src/data/berry.ts b/src/data/berry.ts index b5f2fc30aad..c21e5bbbd0e 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -54,12 +54,7 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate { case BerryType.LUM: return (pokemon: Pokemon) => !!pokemon.status || !!pokemon.getTag(BattlerTagType.CONFUSED); case BerryType.ENIGMA: - return (pokemon: Pokemon) => { - const opponent = pokemon.getOpponent(); - const opponentLastMove = opponent ? opponent.getLastXMoves(1).find(() => true) : null; // TODO: Update so this works even if opponent has fainted - - return opponentLastMove && opponentLastMove.turn === pokemon.scene.currentBattle?.turn - 1 && opponentLastMove.result === MoveResult.SUPER_EFFECTIVE; - }; + return (pokemon: Pokemon) => !!pokemon.turnData.attacksReceived.filter(a => a.result === MoveResult.SUPER_EFFECTIVE).length; case BerryType.LIECHI: case BerryType.GANLON: case BerryType.SALAC: @@ -83,7 +78,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { case BerryType.SITRUS: case BerryType.ENIGMA: return (pokemon: Pokemon) => { - pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), Math.floor(pokemon.getMaxHp() / 4), getPokemonMessage(pokemon, `'s ${getBerryName(berryType)}\nrestored its HP!`), true)); + pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), Math.floor(pokemon.getMaxHp() / 4), getPokemonMessage(pokemon, `'s ${getBerryName(berryType)}\nrestored its HP!`), true)); }; case BerryType.LUM: return (pokemon: Pokemon) => { @@ -101,13 +96,13 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { case BerryType.APICOT: return (pokemon: Pokemon) => { const battleStat = (berryType - BerryType.LIECHI) as BattleStat; - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), true, [ battleStat ], 1)); + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), true, [ battleStat ], 1)); }; case BerryType.LANSAT: return (pokemon: Pokemon) => { pokemon.addTag(BattlerTagType.CRIT_BOOST); }; case BerryType.STARF: - return (pokemon: Pokemon) => pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), true, [ BattleStat.RAND ], 2)); + return (pokemon: Pokemon) => pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), true, [ BattleStat.RAND ], 2)); } } \ No newline at end of file diff --git a/src/data/move.ts b/src/data/move.ts index 67fe9335c00..4c53e1efe5a 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -936,7 +936,8 @@ export class HealAttr extends MoveEffectAttr { } addHealPhase(user: Pokemon, healRatio: number) { - user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.isPlayer(), Math.max(Math.floor(user.getMaxHp() * healRatio), 1), getPokemonMessage(user, ' regained\nhealth!'), true, !this.showAnim)); + user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.isPlayer(), user.getFieldIndex(), + Math.max(Math.floor(user.getMaxHp() * healRatio), 1), getPokemonMessage(user, ' regained\nhealth!'), true, !this.showAnim)); } } @@ -977,7 +978,8 @@ export class HitHealAttr extends MoveHitEffectAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.isPlayer(), Math.max(Math.floor(user.turnData.damageDealt * this.healRatio), 1), getPokemonMessage(target, ` had its\nenergy drained!`), false, true)); + user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.isPlayer(), user.getFieldIndex(), + Math.max(Math.floor(user.turnData.damageDealt * this.healRatio), 1), getPokemonMessage(target, ` had its\nenergy drained!`), false, true)); return true; } } @@ -1214,7 +1216,7 @@ export class StatChangeAttr extends MoveEffectAttr { if (move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance) { const levels = this.getLevels(user); - user.scene.unshiftPhase(new StatChangePhase(user.scene, user.isPlayer() === this.selfTarget, this.selfTarget, this.stats, levels)); + user.scene.unshiftPhase(new StatChangePhase(user.scene, user.isPlayer() === this.selfTarget, user.getFieldIndex(), this.selfTarget, this.stats, levels)); return true; } @@ -1770,8 +1772,8 @@ export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr { const moveIndex = moveset.findIndex(m => m.moveId === move.moveId); user.getMoveQueue().push({ move: move.moveId, ignorePP: this.enemyMoveset }); user.scene.unshiftPhase(user.isPlayer() - ? new PlayerMovePhase(user.scene, user as PlayerPokemon, moveset[moveIndex], true) - : new EnemyMovePhase(user.scene, user as EnemyPokemon, moveset[moveIndex], true)); + ? new PlayerMovePhase(user.scene, user as PlayerPokemon, target.getFieldIndex(), moveset[moveIndex], true) + : new EnemyMovePhase(user.scene, user as EnemyPokemon, target.getFieldIndex(), moveset[moveIndex], true)); return true; } @@ -1786,8 +1788,8 @@ export class RandomMoveAttr extends OverrideMoveEffectAttr { const moveId = moveIds[Utils.randInt(moveIds.length)]; user.getMoveQueue().push({ move: moveId, ignorePP: true }); user.scene.unshiftPhase(user.isPlayer() - ? new PlayerMovePhase(user.scene, user as PlayerPokemon, new PokemonMove(moveId, 0, 0, true), true) - : new EnemyMovePhase(user.scene, user as EnemyPokemon, new PokemonMove(moveId, 0, 0, true), true)); + ? new PlayerMovePhase(user.scene, user as PlayerPokemon, target.getFieldIndex(), new PokemonMove(moveId, 0, 0, true), true) + : new EnemyMovePhase(user.scene, user as EnemyPokemon, target.getFieldIndex(), new PokemonMove(moveId, 0, 0, true), true)); initMoveAnim(moveId).then(() => { loadMoveAnimAssets(user.scene, [ moveId ], true) .then(() => resolve(true)); @@ -1824,8 +1826,8 @@ export class CopyMoveAttr extends OverrideMoveEffectAttr { user.getMoveQueue().push({ move: copiedMove.move, ignorePP: true }); user.scene.unshiftPhase(user.isPlayer() - ? new PlayerMovePhase(user.scene, user as PlayerPokemon, new PokemonMove(copiedMove.move, 0, 0, true), true) - : new EnemyMovePhase(user.scene, user as EnemyPokemon, new PokemonMove(copiedMove.move, 0, 0, true), true)); + ? new PlayerMovePhase(user.scene, user as PlayerPokemon, target.getFieldIndex(), new PokemonMove(copiedMove.move, 0, 0, true), true) + : new EnemyMovePhase(user.scene, user as EnemyPokemon, target.getFieldIndex(), new PokemonMove(copiedMove.move, 0, 0, true), true)); return true; } @@ -1932,10 +1934,10 @@ export function applyFilteredMoveAttrs(attrFilter: MoveAttrFilter, user: Pokemon return applyMoveAttrsInternal(attrFilter, user, target, move, args); } -export function getMoveTarget(user: Pokemon, move: Moves): Pokemon { +export function getMoveTarget(user: Pokemon, targetIndex: integer, move: Moves): Pokemon { const moveTarget = allMoves[move].moveTarget; - const other = user.getOpponent(); + const other = user.getOpponent(targetIndex); switch (moveTarget) { case MoveTarget.USER: diff --git a/src/data/weather.ts b/src/data/weather.ts index ca64b62e5c5..b6a4a01392d 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -100,17 +100,10 @@ export class Weather { } isEffectSuppressed(scene: BattleScene): boolean { - const playerPokemon = scene.getPlayerPokemon(); - const enemyPokemon = scene.getEnemyPokemon(); + const field = scene.getField().filter(p => p); - if (playerPokemon) { - const suppressWeatherEffectAbAttr = playerPokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr; - if (suppressWeatherEffectAbAttr && (!this.isImmutable() || suppressWeatherEffectAbAttr.affectsImmutable)) - return true; - } - - if (enemyPokemon) { - const suppressWeatherEffectAbAttr = enemyPokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr; + for (let pokemon of field) { + const suppressWeatherEffectAbAttr = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr).find(() => true) as SuppressWeatherEffectAbAttr; if (suppressWeatherEffectAbAttr && (!this.isImmutable() || suppressWeatherEffectAbAttr.affectsImmutable)) return true; } diff --git a/src/pokemon.ts b/src/pokemon.ts index a7fd81a9309..4ee2dea4c5b 100644 --- a/src/pokemon.ts +++ b/src/pokemon.ts @@ -179,6 +179,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { abstract isPlayer(): boolean; + abstract getFieldIndex(): integer; + loadAssets(): Promise { return new Promise(resolve => { const moveIds = this.getMoveset().map(m => m.getMove().id); @@ -496,13 +498,24 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.levelExp = this.exp - getLevelTotalExp(this.level, this.getSpeciesForm().growthRate); } - getOpponent(): Pokemon { - const ret = this.isPlayer() ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon(); + getOpponent(targetIndex: integer): Pokemon { + const ret = this.getOpponents()[targetIndex]; if (ret.summonData) return ret; return null; } + getOpponents(): Pokemon[] { + return this.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField(); + } + + getOpponentDescriptor(): string { + const opponents = this.getOpponents(); + if (opponents.length === 1) + return opponents[0].name; + return this.isPlayer() ? 'the opposing team' : 'your team'; + } + apply(source: Pokemon, battlerMove: PokemonMove): MoveResult { let result: MoveResult; const move = battlerMove.getMove(); @@ -626,9 +639,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.hp = Math.max(this.hp - damage, 0); if (!this.hp) { - this.scene.pushPhase(new FaintPhase(this.scene, this.isPlayer())); + this.scene.pushPhase(new FaintPhase(this.scene, this.isPlayer(), this.getFieldIndex())); this.resetSummonData(); - this.getOpponent()?.resetBattleSummonData(); } } @@ -946,6 +958,10 @@ export class PlayerPokemon extends Pokemon { return true; } + getFieldIndex(): integer { + return this.scene.getPlayerField().indexOf(this); + } + generateCompatibleTms(): void { this.compatibleTms = []; @@ -1127,6 +1143,10 @@ export class EnemyPokemon extends Pokemon { return false; } + getFieldIndex(): integer { + return this.scene.getEnemyField().indexOf(this); + } + addToParty() { const party = this.scene.getParty(); let ret: PlayerPokemon = null; diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 68dddbe671f..51a64b22f19 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -21,7 +21,7 @@ interface SystemSaveData { interface SessionSaveData { party: PokemonData[]; - enemyParty: PokemonData[]; + enemyField: PokemonData[]; modifiers: PersistentModifierData[]; enemyModifiers: PersistentModifierData[]; arena: ArenaData; @@ -126,7 +126,7 @@ export class GameData { saveSession(scene: BattleScene): boolean { const sessionData = { party: scene.getParty().map(p => new PokemonData(p)), - enemyParty: scene.getEnemyParty().map(p => new PokemonData(p)), + enemyField: scene.getEnemyField().map(p => new PokemonData(p)), modifiers: scene.findModifiers(m => true).map(m => new PersistentModifierData(m, true)), enemyModifiers: scene.findModifiers(m => true, false).map(m => new PersistentModifierData(m, false)), arena: new ArenaData(scene.arena), @@ -187,17 +187,20 @@ export class GameData { loadPokemonAssets.push(pokemon.loadAssets()); party.push(pokemon); } - - const enemyPokemon = sessionData.enemyParty[0].toPokemon(scene) as EnemyPokemon; Object.keys(scene.pokeballCounts).forEach((key: string) => { scene.pokeballCounts[key] = sessionData.pokeballCounts[key] || 0; }); - scene.newArena(sessionData.arena.biome, true); - scene.newBattle(sessionData.waveIndex).enemyPokemon = enemyPokemon; + scene.newArena(sessionData.arena.biome, sessionData.enemyField.length > 1); + const battle = scene.newBattle(sessionData.waveIndex, sessionData.enemyField.length > 1); - loadPokemonAssets.push(enemyPokemon.loadAssets()); + sessionData.enemyField.forEach((enemyData, e) => { + const enemyPokemon = enemyData.toPokemon(scene) as EnemyPokemon; + battle.enemyField[e] = enemyPokemon; + + loadPokemonAssets.push(enemyPokemon.loadAssets()); + }); scene.arena.weather = sessionData.arena.weather; // TODO diff --git a/src/system/modifier-data.ts b/src/system/modifier-data.ts index 602526057a2..84badd15e5c 100644 --- a/src/system/modifier-data.ts +++ b/src/system/modifier-data.ts @@ -37,7 +37,7 @@ export default class ModifierData { type.generatorId = this.typeGeneratorId; if (type instanceof ModifierTypeGenerator) - type = (type as ModifierTypeGenerator).generateType(this.player ? scene.getParty() : scene.getEnemyParty(), this.typePregenArgs); + type = (type as ModifierTypeGenerator).generateType(this.player ? scene.getParty() : scene.getEnemyField(), this.typePregenArgs); const ret = Reflect.construct(constructor, ([ type ] as any[]).concat(this.args).concat(this.stackCount)) as PersistentModifier diff --git a/src/ui/command-ui-handler.ts b/src/ui/command-ui-handler.ts index 5e85399bb37..2882b495d50 100644 --- a/src/ui/command-ui-handler.ts +++ b/src/ui/command-ui-handler.ts @@ -43,7 +43,7 @@ export default class CommandUiHandler extends UiHandler { const messageHandler = this.getUi().getMessageHandler(); messageHandler.bg.setTexture('bg_command'); messageHandler.message.setWordWrapWidth(1110); - messageHandler.showText(`What will\n${this.scene.getPlayerPokemon().name} do?`, 0); + messageHandler.showText(`What will\n${(this.scene.getCurrentPhase() as CommandPhase).getPokemon().name} do?`, 0); this.setCursor(this.cursor); } @@ -65,7 +65,7 @@ export default class CommandUiHandler extends UiHandler { success = true; break; case 2: - ui.setMode(Mode.PARTY, PartyUiMode.SWITCH); + ui.setMode(Mode.PARTY, PartyUiMode.SWITCH, (this.scene.getCurrentPhase() as CommandPhase).getPokemon().getFieldIndex()); success = true; break; case 3: diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index f856e380ce1..4a8efef99f4 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -91,7 +91,7 @@ export default class FightUiHandler extends UiHandler { ui.add(this.cursorObj); } - const moveset = this.scene.getPlayerPokemon().getMoveset(); + const moveset = (this.scene.getCurrentPhase() as CommandPhase).getPokemon().getMoveset(); const hasMove = cursor < moveset.length; @@ -114,7 +114,7 @@ export default class FightUiHandler extends UiHandler { } displayMoves() { - const moveset = this.scene.getPlayerPokemon().getMoveset(); + const moveset = (this.scene.getCurrentPhase() as CommandPhase).getPokemon().getMoveset(); for (let m = 0; m < 4; m++) { const moveText = addTextObject(this.scene, m % 2 === 0 ? 0 : 100, m < 2 ? 0 : 16, '-', TextStyle.WINDOW); if (m < moveset.length) diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 8e196e80085..4fd059803cf 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -42,6 +42,7 @@ export type PokemonMoveSelectFilter = (pokemonMove: PokemonMove) => string; export default class PartyUiHandler extends MessageUiHandler { private partyUiMode: PartyUiMode; + private fieldIndex: integer; private partyContainer: Phaser.GameObjects.Container; private partySlotsContainer: Phaser.GameObjects.Container; @@ -143,17 +144,19 @@ export default class PartyUiHandler extends MessageUiHandler { this.partyUiMode = args[0] as PartyUiMode; + this.fieldIndex = args.length > 1 ? args[1] as integer : -1; + this.partyContainer.setVisible(true); this.populatePartySlots(); this.setCursor(this.cursor < 6 ? this.cursor : 0); - if (args.length > 1 && args[1] instanceof Function) - this.selectCallback = args[1]; - this.selectFilter = args.length > 2 && args[2] instanceof Function - ? args[2] as PokemonSelectFilter + if (args.length > 2 && args[2] instanceof Function) + this.selectCallback = args[2]; + this.selectFilter = args.length > 3 && args[3] instanceof Function + ? args[3] as PokemonSelectFilter : PartyUiHandler.FilterAll; - this.moveSelectFilter = args.length > 3 && args[3] instanceof Function - ? args[3] as PokemonMoveSelectFilter + this.moveSelectFilter = args.length > 4 && args[4] instanceof Function + ? args[4] as PokemonMoveSelectFilter : PartyUiHandler.FilterAllMoves; } @@ -414,7 +417,8 @@ export default class PartyUiHandler extends MessageUiHandler { if (this.cursor) { this.options.push(PartyOption.SEND_OUT); if (this.partyUiMode !== PartyUiMode.FAINT_SWITCH - && this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === this.scene.getPlayerPokemon().id)) + && this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier + && (m as SwitchEffectTransferModifier).pokemonId === this.scene.getPlayerField()[this.fieldIndex].id)) this.options.push(PartyOption.PASS_BATON); } break;