diff --git a/src/battle-phases.ts b/src/battle-phases.ts index 245a9c51f..7ca8d8978 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -188,7 +188,7 @@ export class SelectBiomePhase extends BattlePhase { start() { super.start(); - this.scene.arena.fadeOutBgm(2000); + this.scene.arena.fadeOutBgm(2000, true); const currentBiome = this.scene.arena.biomeType; @@ -1147,8 +1147,8 @@ export class WeatherEffectPhase extends CommonAnimPhase { const playerPokemon = this.scene.getPlayerPokemon(); const enemyPokemon = this.scene.getEnemyPokemon(); - const playerImmune = !!playerPokemon.getTypes().filter(t => this.weather.isTypeDamageImmune(t)).length; - const enemyImmune = !!enemyPokemon.getTypes().filter(t => this.weather.isTypeDamageImmune(t)).length; + const playerImmune = !playerPokemon || !!playerPokemon.getTypes().filter(t => this.weather.isTypeDamageImmune(t)).length; + const enemyImmune = !enemyPokemon || !!enemyPokemon.getTypes().filter(t => this.weather.isTypeDamageImmune(t)).length; if (!this.playerDelayed && !playerImmune) inflictDamage(playerPokemon); @@ -1291,9 +1291,9 @@ export class FaintPhase extends PokemonPhase { this.scene.queueMessage(getPokemonMessage(this.getPokemon(), ' fainted!'), null, true); - if (this.player) - this.scene.unshiftPhase(new SwitchPhase(this.scene, true, false)); - else + if (this.player) { + this.scene.unshiftPhase(this.scene.getParty().filter(p => p.hp).length ? new SwitchPhase(this.scene, true, false) : new GameOverPhase(this.scene)); + } else this.scene.unshiftPhase(new VictoryPhase(this.scene)); const pokemon = this.getPokemon(); @@ -1396,6 +1396,27 @@ export class VictoryPhase extends PokemonPhase { } } +export class GameOverPhase extends BattlePhase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + super.start(); + + this.scene.time.delayedCall(1000, () => { + this.scene.fadeOutBgm(5000, true); + this.scene.ui.fadeOut(5000).then(() => { + this.scene.clearPhaseQueue(); + this.scene.ui.clearText(); + this.scene.reset(); + this.scene.newBattle(); + this.end(); + }); + }); + } +} + export class SwitchPhase extends BattlePhase { private isModal: boolean; private doReturn: boolean; diff --git a/src/battle-scene.ts b/src/battle-scene.ts index f721bc277..4f56ff241 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -37,12 +37,15 @@ export enum Button { CYCLE_FORM, CYCLE_GENDER, QUICK_START, - RANDOM, AUTO, SPEED_UP, SLOW_DOWN } +interface PokeballCounts { + [pb: string]: integer; +} + export default class BattleScene extends Phaser.Scene { public auto: boolean; public gameSpeed: integer = 1; @@ -65,7 +68,7 @@ export default class BattleScene extends Phaser.Scene { public arena: Arena; public trainer: Phaser.GameObjects.Sprite; public currentBattle: Battle; - public pokeballCounts = Object.fromEntries(Utils.getEnumValues(PokeballType).filter(p => p <= PokeballType.MASTER_BALL).map(t => [ t, 0 ])); + public pokeballCounts: PokeballCounts; private party: PlayerPokemon[]; private waveCountText: Phaser.GameObjects.Text; private modifierBar: ModifierBar; @@ -302,7 +305,7 @@ export default class BattleScene extends Phaser.Scene { this.add.existing(this.enemyModifierBar); uiContainer.add(this.enemyModifierBar); - this.waveCountText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, '1', TextStyle.BATTLE_INFO); + this.waveCountText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, startingWave.toString(), TextStyle.BATTLE_INFO); this.waveCountText.setOrigin(1, 0); this.updateWaveCountPosition(); this.fieldUI.add(this.waveCountText); @@ -311,22 +314,14 @@ export default class BattleScene extends Phaser.Scene { let loadPokemonAssets = []; - const isRandom = this.isButtonPressed(Button.RANDOM); // For testing purposes - this.quickStart = this.quickStart || isRandom || this.isButtonPressed(Button.QUICK_START); + this.quickStart = this.quickStart || this.isButtonPressed(Button.QUICK_START); - if (isRandom) { - const biomes = Utils.getEnumValues(Biome); - this.newArena(biomes[Utils.randInt(biomes.length)]); - } else - this.newArena(startingBiome); - - const biomeKey = this.arena.getBiomeKey(); - this.arenaBg = this.add.sprite(0, 0, `${biomeKey}_bg`); - this.arenaBgTransition = this.add.sprite(0, 0, `${biomeKey}_bg`); - this.arenaPlayer = this.add.sprite(340, 20, `${biomeKey}_a`); - this.arenaPlayerTransition = this.add.sprite(40, 20, `${biomeKey}_a`); - this.arenaEnemy = this.add.sprite(-240, 13, `${biomeKey}_b`); - this.arenaNextEnemy = this.add.sprite(-240, 13, `${biomeKey}_b`); + this.arenaBg = this.add.sprite(0, 0, 'plains_bg'); + this.arenaBgTransition = this.add.sprite(0, 0, `plains_bg`); + this.arenaPlayer = this.add.sprite(0, 0, `plains_a`); + this.arenaPlayerTransition = this.add.sprite(0, 0, `plains_a`); + this.arenaEnemy = this.add.sprite(0, 0, `plains_b`); + this.arenaNextEnemy = this.add.sprite(0, 0, `plains_b`); this.arenaBgTransition.setVisible(false); this.arenaPlayerTransition.setVisible(false); @@ -338,9 +333,7 @@ export default class BattleScene extends Phaser.Scene { if (this.quickStart) { for (let s = 0; s < 3; s++) { - const playerSpecies = (!isRandom - ? getPokemonSpecies((getPokemonSpecies(s === 0 ? Species.TORCHIC : s === 1 ? Species.TREECKO : Species.MUDKIP)).getSpeciesForLevel(startingLevel, true)) - : this.randomSpecies(startingWave, startingLevel)); + const playerSpecies = getPokemonSpecies((getPokemonSpecies(s === 0 ? Species.TORCHIC : s === 1 ? Species.TREECKO : Species.MUDKIP)).getSpeciesForLevel(startingLevel, true)); const playerPokemon = new PlayerPokemon(this, playerSpecies, startingLevel, 0); playerPokemon.setVisible(false); loadPokemonAssets.push(playerPokemon.loadAssets()); @@ -356,7 +349,7 @@ export default class BattleScene extends Phaser.Scene { frameRate: 16 }); - const trainer = this.add.sprite(406, 132, 'trainer_m'); + const trainer = this.add.sprite(0, 0, 'trainer_m'); trainer.setOrigin(0.5, 1); field.add(trainer); @@ -371,6 +364,8 @@ export default class BattleScene extends Phaser.Scene { showOnStart: true }); + this.reset(); + const ui = new UI(this); this.uiContainer.add(ui); @@ -407,7 +402,6 @@ export default class BattleScene extends Phaser.Scene { [Button.CYCLE_FORM]: [keyCodes.F], [Button.CYCLE_GENDER]: [keyCodes.G], [Button.QUICK_START]: [keyCodes.Q], - [Button.RANDOM]: [keyCodes.R], [Button.AUTO]: [keyCodes.F2], [Button.SPEED_UP]: [keyCodes.PLUS], [Button.SLOW_DOWN]: [keyCodes.MINUS] @@ -436,7 +430,44 @@ export default class BattleScene extends Phaser.Scene { } getEnemyPokemon(): EnemyPokemon { - return this.currentBattle.enemyPokemon; + return this.currentBattle?.enemyPokemon; + } + + reset(): void { + this.pokeballCounts = Object.fromEntries(Utils.getEnumValues(PokeballType).filter(p => p <= PokeballType.MASTER_BALL).map(t => [ t, 0 ])); + + this.modifiers = []; + this.enemyModifiers = []; + this.modifierBar.removeAll(true); + this.enemyModifierBar.removeAll(true); + + for (let p of this.getParty()) + p.destroy(); + this.party = []; + for (let p of this.getEnemyParty()) + p.destroy(); + + this.currentBattle = null; + this.waveCountText.setText(startingWave.toString()); + + this.newArena(startingBiome); + const biomeKey = this.arena.getBiomeKey(); + + this.arenaBg.setTexture(`${biomeKey}_bg`); + this.arenaBgTransition.setTexture(`${biomeKey}_bg`); + this.arenaPlayer.setTexture(`${biomeKey}_a`); + this.arenaPlayerTransition.setTexture(`${biomeKey}_a`); + this.arenaEnemy.setTexture(`${biomeKey}_b`); + this.arenaNextEnemy.setTexture(`${biomeKey}_b`); + + this.arenaBgTransition.setPosition(0, 0); + this.arenaPlayer.setPosition(340, 20); + this.arenaPlayerTransition.setPosition(40, 2); + this.arenaEnemy.setPosition(-240, 13); + this.arenaNextEnemy.setPosition(-240, 13); + + this.trainer.setTexture('trainer_m'); + this.trainer.setPosition(406, 132); } newBattle(): Battle { @@ -565,8 +596,8 @@ export default class BattleScene extends Phaser.Scene { this.bgm.resume(); } - fadeOutBgm(destroy?: boolean): void { - this.arena.fadeOutBgm(500, destroy); + fadeOutBgm(duration?: integer, destroy?: boolean): void { + this.arena.fadeOutBgm(duration || 500, destroy); } playSoundWithoutBgm(soundName: string, pauseDuration?: integer): void { diff --git a/src/evolution-phase.ts b/src/evolution-phase.ts index 85fa85894..658b8689e 100644 --- a/src/evolution-phase.ts +++ b/src/evolution-phase.ts @@ -40,7 +40,7 @@ export class EvolutionPhase extends BattlePhase { return; } - this.scene.fadeOutBgm(false); + this.scene.fadeOutBgm(null, false); this.evolutionContainer = (this.scene.ui.getHandler() as EvolutionSceneHandler).evolutionContainer; diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 27c838309..943b03bf8 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -539,60 +539,60 @@ const modifierPool = { new WeightedModifierType(modifierTypes.POKEBALL, 6), new WeightedModifierType(modifierTypes.RARE_CANDY, 2), new WeightedModifierType(modifierTypes.POTION, (party: Pokemon[]) => { - const thresholdPartyMemberCount = party.filter(p => p.getInverseHp() >= 10 || p.getHpRatio() <= 0.875).length; + const thresholdPartyMemberCount = Math.min(party.filter(p => p.getInverseHp() >= 10 || p.getHpRatio() <= 0.875).length, 3); return thresholdPartyMemberCount * 3; }), new WeightedModifierType(modifierTypes.SUPER_POTION, (party: Pokemon[]) => { - const thresholdPartyMemberCount = party.filter(p => p.getInverseHp() >= 25 || p.getHpRatio() <= 0.75).length; + const thresholdPartyMemberCount = Math.min(party.filter(p => p.getInverseHp() >= 25 || p.getHpRatio() <= 0.75).length, 3); return thresholdPartyMemberCount; }), new WeightedModifierType(modifierTypes.ETHER, (party: Pokemon[]) => { - const thresholdPartyMemberCount = party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length; + const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3); return thresholdPartyMemberCount * 3; }), new WeightedModifierType(modifierTypes.MAX_ETHER, (party: Pokemon[]) => { - const thresholdPartyMemberCount = party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length; + const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3); return thresholdPartyMemberCount; }), new WeightedModifierType(modifierTypes.TEMP_STAT_BOOSTER, 4), new WeightedModifierType(modifierTypes.BERRY, 2) ].map(m => { m.setTier(ModifierTier.COMMON); return m; }), [ModifierTier.GREAT]: [ - new WeightedModifierType(modifierTypes.GREAT_BALL, 12), + new WeightedModifierType(modifierTypes.GREAT_BALL, 6), new WeightedModifierType(modifierTypes.FULL_HEAL, (party: Pokemon[]) => { - const statusEffectPartyMemberCount = party.filter(p => p.hp && !!p.status).length; - return statusEffectPartyMemberCount * 8; + const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status).length, 3); + return statusEffectPartyMemberCount * 6; }), new WeightedModifierType(modifierTypes.REVIVE, (party: Pokemon[]) => { - const faintedPartyMemberCount = party.filter(p => !p.hp).length; - return faintedPartyMemberCount * 6; + const faintedPartyMemberCount = Math.min(party.filter(p => !p.hp).length, 3); + return faintedPartyMemberCount * 9; }), new WeightedModifierType(modifierTypes.MAX_REVIVE, (party: Pokemon[]) => { - const faintedPartyMemberCount = party.filter(p => !p.hp).length; - return faintedPartyMemberCount * 2; + const faintedPartyMemberCount = Math.min(party.filter(p => !p.hp).length, 3); + return faintedPartyMemberCount * 3; }), new WeightedModifierType(modifierTypes.SACRED_ASH, (party: Pokemon[]) => { return party.filter(p => !p.hp).length >= Math.ceil(party.length / 2) ? 1 : 0; }), new WeightedModifierType(modifierTypes.HYPER_POTION, (party: Pokemon[]) => { - const thresholdPartyMemberCount = party.filter(p => p.getInverseHp() >= 100 || p.getHpRatio() <= 0.625).length; - return thresholdPartyMemberCount * 2; + const thresholdPartyMemberCount = Math.min(party.filter(p => p.getInverseHp() >= 100 || p.getHpRatio() <= 0.625).length, 3); + return thresholdPartyMemberCount * 3; }), new WeightedModifierType(modifierTypes.MAX_POTION, (party: Pokemon[]) => { - const thresholdPartyMemberCount = party.filter(p => p.getInverseHp() >= 150 || p.getHpRatio() <= 0.5).length; - return Math.ceil(thresholdPartyMemberCount / 1.5); + const thresholdPartyMemberCount = Math.min(party.filter(p => p.getInverseHp() >= 150 || p.getHpRatio() <= 0.5).length, 3); + return thresholdPartyMemberCount; }), new WeightedModifierType(modifierTypes.ELIXIR, (party: Pokemon[]) => { - const thresholdPartyMemberCount = party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length; - return thresholdPartyMemberCount * 2; + const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3); + return thresholdPartyMemberCount * 3; }), new WeightedModifierType(modifierTypes.MAX_ELIXIR, (party: Pokemon[]) => { - const thresholdPartyMemberCount = party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length; - return Math.ceil(thresholdPartyMemberCount / 1.5); + const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3); + return thresholdPartyMemberCount; }), - new WeightedModifierType(modifierTypes.TM, 4), - new WeightedModifierType(modifierTypes.EXP_SHARE, 2), - new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 4) + new WeightedModifierType(modifierTypes.TM, 2), + modifierTypes.EXP_SHARE, + new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3) ].map(m => { m.setTier(ModifierTier.GREAT); return m; }), [ModifierTier.ULTRA]: [ new WeightedModifierType(modifierTypes.ULTRA_BALL, 8), diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index e83650227..0276b0001 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -78,7 +78,7 @@ export abstract class PersistentModifier extends Modifier { add(modifiers: PersistentModifier[], virtual: boolean): boolean { for (let modifier of modifiers) { if (this.match(modifier)) - return modifier.incrementStack(modifier.stackCount, virtual); + return modifier.incrementStack(this.stackCount, virtual); } if (virtual) { diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index fae7680e9..703357671 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -536,5 +536,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { super.clear(); this.cursor = -1; this.starterSelectContainer.setVisible(false); + + while (this.starterCursors.length) + this.popStarter(); } } \ No newline at end of file diff --git a/src/ui/ui.ts b/src/ui/ui.ts index ae43187fc..05716eea6 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -43,7 +43,7 @@ export default class UI extends Phaser.GameObjects.Container { private handlers: UiHandler[]; private overlay: Phaser.GameObjects.Rectangle; - private transitioning: boolean; + private overlayActive: boolean; constructor(scene: BattleScene) { super(scene, 0, scene.game.canvas.height / 6); @@ -83,7 +83,7 @@ export default class UI extends Phaser.GameObjects.Container { } processInput(button: Button): void { - if (this.transitioning) + if (this.overlayActive) return; this.getHandler().processInput(button); @@ -121,6 +121,41 @@ export default class UI extends Phaser.GameObjects.Container { this.scene.sound.play('error'); } + fadeOut(duration: integer): Promise { + return new Promise(resolve => { + if (this.overlayActive) { + resolve(); + return; + } + this.overlayActive = true; + this.overlay.setAlpha(0); + this.overlay.setVisible(true); + this.scene.tweens.add({ + targets: this.overlay, + alpha: 1, + duration: duration, + ease: 'Sine.easeOut', + onComplete: () => resolve() + }); + }); + } + + fadeIn(duration: integer): Promise { + return new Promise(resolve => { + this.scene.tweens.add({ + targets: this.overlay, + alpha: 0, + duration: duration, + ease: 'Sine.easeIn', + onComplete: () => { + this.overlay.setVisible(false); + resolve(); + } + }); + this.overlayActive = false; + }); + } + private setModeInternal(mode: Mode, clear: boolean, forceTransition: boolean, args: any[]): Promise { return new Promise(resolve => { if (this.mode === mode && !forceTransition) { @@ -138,28 +173,12 @@ export default class UI extends Phaser.GameObjects.Container { }; if ((transitionModes.indexOf(this.mode) > -1 || transitionModes.indexOf(mode) > -1) && (noTransitionModes.indexOf(this.mode) === -1 && noTransitionModes.indexOf(mode) === -1) && !(this.scene as BattleScene).auto) { - this.transitioning = true; - this.overlay.setAlpha(0); - this.overlay.setVisible(true); - this.scene.tweens.add({ - targets: this.overlay, - alpha: 1, - duration: 250, - ease: 'Sine.easeOut', - onComplete: () => { - this.scene.time.delayedCall(100, () => { - doSetMode(); - this.scene.tweens.add({ - targets: this.overlay, - alpha: 0, - duration: 250, - ease: 'Sine.easeIn', - onComplete: () => this.overlay.setVisible(false) - }); - this.transitioning = false; - }); - } - }); + this.fadeOut(250).then(() => { + this.scene.time.delayedCall(100, () => { + doSetMode(); + this.fadeIn(250); + }); + }) } else doSetMode(); });