From 64cc901fe17829ef527632c0948cde2739898295 Mon Sep 17 00:00:00 2001 From: RedstonewolfX <108761527+RedstonewolfX@users.noreply.github.com> Date: Sun, 25 Aug 2024 17:11:19 -0400 Subject: [PATCH] Update all phases Migrated all the edits I made in `phases.ts` over to the files in the new `phases` folder --- src/battle-scene.ts | 2 + src/logger.ts | 2 +- src/phases/battle-end-phase.ts | 31 ++ src/phases/check-switch-phase.ts | 105 ++++++ src/phases/command-phase.ts | 28 +- src/phases/encounter-phase.ts | 50 ++- src/phases/enemy-command-phase.ts | 61 +++- src/phases/faint-phase.ts | 1 + src/phases/learn-move-phase.ts | 123 ++++--- src/phases/move-effect-phase.ts | 174 ++++----- src/phases/move-phase.ts | 12 +- src/phases/obtain-status-effect-phase.ts | 2 +- src/phases/party-heal-phase.ts | 2 +- src/phases/scan-ivs-phase.ts | 1 + src/phases/select-biome-phase.ts | 4 +- src/phases/select-modifier-phase.ts | 259 ++++++++++++-- src/phases/select-target-phase.ts | 4 +- src/phases/stat-change-phase.ts | 2 +- src/phases/summon-phase.ts | 4 +- src/phases/switch-phase.ts | 30 +- src/phases/switch-summon-phase.ts | 18 +- src/phases/title-phase.ts | 434 +++++++++++++++++++++-- src/phases/turn-end-phase.ts | 2 + src/phases/turn-init-phase.ts | 87 ++++- src/phases/turn-start-phase.ts | 162 ++++++++- src/phases/victory-phase.ts | 3 + 26 files changed, 1334 insertions(+), 269 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index b15f451dff0..08d93b39d90 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -411,6 +411,7 @@ export default class BattleScene extends SceneBase { this.fieldUI = fieldUI; + /* const transition = this.make.rexTransitionImagePack({ x: 0, y: 0, @@ -430,6 +431,7 @@ export default class BattleScene extends SceneBase { }); this.add.existing(transition); + */ const uiContainer = this.add.container(0, 0); uiContainer.setName("ui"); diff --git a/src/logger.ts b/src/logger.ts index ba91c41df63..5c7e4d01559 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1905,7 +1905,7 @@ export function resetWaveActions(scene: BattleScene, floor: integer = scene.curr // #region Utils from Phases.ts -const tierNames = [ +export const tierNames = [ "Poké", "Great", "Ultra", diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index dd681aab404..344d1a7731f 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -51,6 +51,37 @@ export class BattleEndPhase extends BattlePhase { } } + // Format this wave's logs + var drpd: LoggerTools.DRPD = LoggerTools.getDRPD(this.scene) + var wv: LoggerTools.Wave = LoggerTools.getWave(drpd, this.scene.currentBattle.waveIndex, this.scene) + var lastcount = 0; + var lastval; + var tempActions: string[] = wv.actions.slice(); + var prevWaveActions: string[] = [] + wv.actions = [] + // Loop through each action + for (var i = 0; i < tempActions.length; i++) { + if (tempActions[i].substring(0, 10) == "[MOVEBACK]") { + prevWaveActions.push(tempActions[i].substring(10)) + } else if (tempActions[i] != lastval) { + if (lastcount > 0) { + wv.actions.push(lastval + (lastcount == 1 ? "" : " x" + lastcount)) + } + lastval = tempActions[i] + lastcount = 1 + } else { + lastcount++ + } + } + if (lastcount > 0) { + wv.actions.push(lastval + (lastcount == 1 ? "" : " x" + lastcount)) + } + console.log(tempActions, wv.actions) + var wv2: LoggerTools.Wave = LoggerTools.getWave(drpd, this.scene.currentBattle.waveIndex - 1, this.scene) + wv2.actions = wv2.actions.concat(prevWaveActions) + console.log(drpd) + LoggerTools.save(this.scene, drpd) + this.scene.updateModifiers().then(() => this.end()); } } diff --git a/src/phases/check-switch-phase.ts b/src/phases/check-switch-phase.ts index 0717d859db7..a540d26d3b8 100644 --- a/src/phases/check-switch-phase.ts +++ b/src/phases/check-switch-phase.ts @@ -8,6 +8,7 @@ import { BattlePhase } from "./battle-phase"; import { PostSummonPhase } from "./post-summon-phase"; import { SummonMissingPhase } from "./summon-missing-phase"; import { SwitchPhase } from "./switch-phase"; +import { getNatureName } from "#app/data/nature.js"; import * as LoggerTools from "../logger"; export class CheckSwitchPhase extends BattlePhase { @@ -47,14 +48,118 @@ export class CheckSwitchPhase extends BattlePhase { return; } + for (var i = 0; i < this.scene.getEnemyField().length; i++) { + var pk = this.scene.getEnemyField()[i] + var maxIVs: string[] = [] + var ivnames = ["HP", "Atk", "Def", "Sp.Atk", "Sp.Def", "Speed"] + pk.ivs.forEach((iv, j) => {if (iv == 31) maxIVs.push(ivnames[j])}) + var ivDesc = maxIVs.join(",") + if (ivDesc == "") { + ivDesc = "No Max IVs" + } else { + ivDesc = "31: " + ivDesc + } + pk.getBattleInfo().flyoutMenu.toggleFlyout(true) + pk.getBattleInfo().flyoutMenu.flyoutText[0].text = getNatureName(pk.nature) + pk.getBattleInfo().flyoutMenu.flyoutText[1].text = ivDesc + pk.getBattleInfo().flyoutMenu.flyoutText[2].text = pk.getAbility().name + pk.getBattleInfo().flyoutMenu.flyoutText[3].text = pk.getPassiveAbility().name + if (pk.hasAbility(pk.species.abilityHidden, true, true)) { + pk.getBattleInfo().flyoutMenu.flyoutText[2].setColor("#e8e8a8") + } + } + if (false) { + this.scene.pokemonInfoContainer.show(this.scene.getEnemyField()[0], false, 1, true); + if (this.scene.getEnemyField()[1] != undefined) { + this.scene.tweens.add({ + targets: this.scene.pokemonInfoContainer, + alpha: 1, + duration: 5000, + onComplete: () => { + this.scene.pokemonInfoContainer.hide(1.3) + this.scene.tweens.add({ + targets: this.scene.pokemonInfoContainer, + alpha: 1, + duration: 1000, + onComplete: () => { + this.scene.pokemonInfoContainer.show(this.scene.getEnemyField()[1], false, 1, true); + } + }) + } + }) + } + } + + for (var i = 0; i < this.scene.getEnemyField().length; i++) { + var pk = this.scene.getEnemyField()[i] + var maxIVs: string[] = [] + var ivnames = ["HP", "Atk", "Def", "Sp.Atk", "Sp.Def", "Speed"] + pk.ivs.forEach((iv, j) => {if (iv == 31) maxIVs.push(ivnames[j])}) + var ivDesc = maxIVs.join(",") + if (ivDesc == "") { + ivDesc = "No Max IVs" + } else { + ivDesc = "31: " + ivDesc + } + pk.getBattleInfo().flyoutMenu.toggleFlyout(true) + pk.getBattleInfo().flyoutMenu.flyoutText[0].text = getNatureName(pk.nature) + pk.getBattleInfo().flyoutMenu.flyoutText[1].text = ivDesc + pk.getBattleInfo().flyoutMenu.flyoutText[2].text = pk.getAbility().name + pk.getBattleInfo().flyoutMenu.flyoutText[3].text = pk.getPassiveAbility().name + if (pk.hasAbility(pk.species.abilityHidden, true, true)) { + pk.getBattleInfo().flyoutMenu.flyoutText[2].setColor("#e8e8a8") + } + } + if (false) { + this.scene.pokemonInfoContainer.show(this.scene.getEnemyField()[0], false, 1, true); + if (this.scene.getEnemyField()[1] != undefined) { + this.scene.tweens.add({ + targets: this.scene.pokemonInfoContainer, + alpha: 1, + duration: 5000, + onComplete: () => { + this.scene.pokemonInfoContainer.hide(1.3) + this.scene.tweens.add({ + targets: this.scene.pokemonInfoContainer, + alpha: 1, + duration: 1000, + onComplete: () => { + this.scene.pokemonInfoContainer.show(this.scene.getEnemyField()[1], false, 1, true); + } + }) + } + }) + } + } + this.scene.ui.showText(i18next.t("battle:switchQuestion", { pokemonName: this.useName ? getPokemonNameWithAffix(pokemon) : i18next.t("battle:pokemon") }), null, () => { this.scene.ui.setMode(Mode.CONFIRM, () => { this.scene.ui.setMode(Mode.MESSAGE); + LoggerTools.isPreSwitch.value = true this.scene.tryRemovePhase(p => p instanceof PostSummonPhase && p.player && p.fieldIndex === this.fieldIndex); this.scene.unshiftPhase(new SwitchPhase(this.scene, this.fieldIndex, false, true)); + for (var i = 0; i < this.scene.getEnemyField().length; i++) { + this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.toggleFlyout(false) + this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.flyoutText[0].text = "???" + this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.flyoutText[1].text = "???" + this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.flyoutText[2].text = "???" + this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.flyoutText[3].text = "???" + this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.flyoutText[2].setColor("#f8f8f8") + this.scene.getEnemyField()[i].flyout.setText() + } + //this.scene.pokemonInfoContainer.hide() this.end(); }, () => { this.scene.ui.setMode(Mode.MESSAGE); + for (var i = 0; i < this.scene.getEnemyField().length; i++) { + this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.toggleFlyout(false) + this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.flyoutText[0].text = "???" + this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.flyoutText[1].text = "???" + this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.flyoutText[2].text = "???" + this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.flyoutText[3].text = "???" + this.scene.getEnemyField()[i].getBattleInfo().flyoutMenu.flyoutText[2].setColor("#f8f8f8") + } + //this.scene.pokemonInfoContainer.hide() this.end(); }); }); diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 69ed3c001bd..fc3378b0844 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -54,8 +54,8 @@ export class CommandPhase extends FieldPhase { const moveQueue = playerPokemon.getMoveQueue(); while (moveQueue.length && moveQueue[0] - && moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move) - || !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable(playerPokemon, moveQueue[0].ignorePP))) { // TODO: is the bang correct? + && moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move) + || !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable(playerPokemon, moveQueue[0].ignorePP))) { // TODO: is the bang correct? moveQueue.shift(); } @@ -85,8 +85,8 @@ export class CommandPhase extends FieldPhase { case Command.FIGHT: let useStruggle = false; if (cursor === -1 || - playerPokemon.trySelectMove(cursor, args[0] as boolean) || - (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m?.isUsable(playerPokemon)).length)) { + playerPokemon.trySelectMove(cursor, args[0] as boolean) || + (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m?.isUsable(playerPokemon)).length)) { const moveId = !useStruggle ? cursor > -1 ? playerPokemon.getMoveset()[cursor]!.moveId : Moves.NONE : Moves.STRUGGLE; // TODO: is the bang correct? const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args }; const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, moveId) : args[2]; @@ -98,9 +98,9 @@ export class CommandPhase extends FieldPhase { this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex)); } if (moveTargets.targets.length <= 1 || moveTargets.multiple) { - turnCommand.move!.targets = moveTargets.targets; //TODO: is the bang correct here? + turnCommand.move!.targets = moveTargets.targets; //TODO: is the bang correct here? } else if (playerPokemon.getTag(BattlerTagType.CHARGING) && playerPokemon.getMoveQueue().length >= 1) { - turnCommand.move!.targets = playerPokemon.getMoveQueue()[0].targets; //TODO: is the bang correct here? + turnCommand.move!.targets = playerPokemon.getMoveQueue()[0].targets; //TODO: is the bang correct here? } else { this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex)); } @@ -112,8 +112,8 @@ export class CommandPhase extends FieldPhase { // Decides between a Disabled, Not Implemented, or No PP translation message const errorMessage = - playerPokemon.summonData.disabledMove === move.moveId ? "battle:moveDisabled" : - move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP"; + playerPokemon.summonData.disabledMove === move.moveId ? "battle:moveDisabled" : + move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP"; const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator this.scene.ui.showText(i18next.t(errorMessage, { moveName: moveName }), null, () => { @@ -158,11 +158,11 @@ export class CommandPhase extends FieldPhase { }, null, true); } else { this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor }; - this.scene.currentBattle.turnCommands[this.fieldIndex]!.targets = targets; - if (this.fieldIndex) { - this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; - } - success = true; + this.scene.currentBattle.turnCommands[this.fieldIndex]!.targets = targets; + if (this.fieldIndex) { + this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; + } + success = true; } } } @@ -198,7 +198,7 @@ export class CommandPhase extends FieldPhase { : { command: Command.RUN }; success = true; if (!isSwitch && this.fieldIndex) { - this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; + this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; } } else if (trapTag) { if (trapTag.sourceMove === Moves.INGRAIN && trapTag.sourceId && this.scene.getPokemonById(trapTag.sourceId)?.isOfType(Type.GHOST)) { diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index c331c2b4471..014acffdc6a 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -26,6 +26,7 @@ import { ScanIvsPhase } from "./scan-ivs-phase"; import { ShinySparklePhase } from "./shiny-sparkle-phase"; import { SummonPhase } from "./summon-phase"; import { ToggleDoublePositionPhase } from "./toggle-double-position-phase"; +import { GameModes } from "#app/game-mode.js"; import * as LoggerTools from "../logger"; export class EncounterPhase extends BattlePhase { @@ -57,11 +58,17 @@ export class EncounterPhase extends BattlePhase { let totalBst = 0; + while (LoggerTools.rarities.length > 0) { + LoggerTools.rarities.pop() + } + LoggerTools.rarityslot[0] = 0 + //console.log(this.scene.gameMode.getDailyOverride()) battle.enemyLevels?.forEach((level, e) => { if (!this.loaded) { if (battle.battleType === BattleType.TRAINER) { battle.enemyParty[e] = battle.trainer?.genPartyMember(e)!; // TODO:: is the bang correct here? } else { + LoggerTools.rarityslot[0] = e const enemySpecies = this.scene.randomSpecies(battle.waveIndex, level, true); battle.enemyParty[e] = this.scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, !!this.scene.getEncounterBossSegments(battle.waveIndex, level, enemySpecies)); if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) { @@ -105,6 +112,7 @@ export class EncounterPhase extends BattlePhase { console.log(getPokemonNameWithAffix(enemyPokemon), enemyPokemon.species.speciesId, enemyPokemon.stats); }); + console.log(LoggerTools.rarities) if (this.scene.getParty().filter(p => p.isShiny()).length === 6) { this.scene.validateAchv(achvs.SHINY_PARTY); @@ -174,10 +182,10 @@ export class EncounterPhase extends BattlePhase { this.scene.setFieldScale(1); /*if (startingWave > 10) { - for (let m = 0; m < Math.min(Math.floor(startingWave / 10), 99); m++) - this.scene.addModifier(getPlayerModifierTypeOptionsForWave((m + 1) * 10, 1, this.scene.getParty())[0].type.newModifier(), true); - this.scene.updateModifiers(true); - }*/ + for (let m = 0; m < Math.min(Math.floor(startingWave / 10), 99); m++) + this.scene.addModifier(getPlayerModifierTypeOptionsForWave((m + 1) * 10, 1, this.scene.getParty())[0].type.newModifier(), true); + this.scene.updateModifiers(true); + }*/ for (const pokemon of this.scene.getParty()) { if (pokemon) { @@ -226,6 +234,30 @@ export class EncounterPhase extends BattlePhase { doEncounterCommon(showEncounterMessage: boolean = true) { const enemyField = this.scene.getEnemyField(); + //LoggerTools.resetWave(this.scene, this.scene.currentBattle.waveIndex) + if (this.scene.lazyReloads) { + LoggerTools.flagResetIfExists(this.scene) + } + LoggerTools.logTeam(this.scene, this.scene.currentBattle.waveIndex) + if (this.scene.getEnemyParty()[0].hasTrainer()) { + LoggerTools.logTrainer(this.scene, this.scene.currentBattle.waveIndex) + } + if (this.scene.currentBattle.waveIndex == 1) { + LoggerTools.logPlayerTeam(this.scene) + if (this.scene.gameMode.modeId == GameModes.DAILY && this.scene.disableDailyShinies) { + this.scene.getParty().forEach(p => { + p.species.luckOverride = 0; // Disable shiny luck for party members + }) + } + } + LoggerTools.resetWaveActions(this.scene, undefined, true) + + //this.scene.doShinyCheck() + + if (LoggerTools.autoCheckpoints.includes(this.scene.currentBattle.waveIndex)) { + //this.scene.gameData.saveGameToAuto(this.scene) + } + if (this.scene.currentBattle.battleType === BattleType.WILD) { enemyField.forEach(enemyPokemon => { enemyPokemon.untint(100, "Sine.easeOut"); @@ -351,7 +383,15 @@ export class EncounterPhase extends BattlePhase { } } } - handleTutorial(this.scene, Tutorial.Access_Menu).then(() => super.end()); + handleTutorial(this.scene, Tutorial.Access_Menu).then(() => { + // Auto-show the flyout + if (this.scene.currentBattle.battleType !== BattleType.TRAINER) { + this.scene.arenaFlyout.display2() + this.scene.arenaFlyout.toggleFlyout(true) + this.scene.arenaFlyout.isAuto = true + } + super.end() + }); } tryOverrideForBattleSpec(): boolean { diff --git a/src/phases/enemy-command-phase.ts b/src/phases/enemy-command-phase.ts index 20042ae6c17..12691a7d8a9 100644 --- a/src/phases/enemy-command-phase.ts +++ b/src/phases/enemy-command-phase.ts @@ -6,6 +6,7 @@ import { Command } from "#app/ui/command-ui-handler.js"; import * as Utils from "#app/utils.js"; import { FieldPhase } from "./field-phase"; import * as LoggerTools from "../logger"; +import { EnemyPokemon, PokemonMove } from "#app/field/pokemon.js"; /** * Phase for determining an enemy AI's action for the next turn. @@ -29,20 +30,21 @@ export class EnemyCommandPhase extends FieldPhase { super.start(); const enemyPokemon = this.scene.getEnemyField()[this.fieldIndex]; + console.log(enemyPokemon.getMoveset().map(m => m?.getName())) const battle = this.scene.currentBattle; const trainer = battle.trainer; /** - * If the enemy has a trainer, decide whether or not the enemy should switch - * to another member in its party. - * - * This block compares the active enemy Pokemon's {@linkcode Pokemon.getMatchupScore | matchup score} - * against the active player Pokemon with the enemy party's other non-fainted Pokemon. If a party - * member's matchup score is 3x the active enemy's score (or 2x for "boss" trainers), - * the enemy will switch to that Pokemon. - */ + * If the enemy has a trainer, decide whether or not the enemy should switch + * to another member in its party. + * + * This block compares the active enemy Pokemon's {@linkcode Pokemon.getMatchupScore | matchup score} + * against the active player Pokemon with the enemy party's other non-fainted Pokemon. If a party + * member's matchup score is 3x the active enemy's score (or 2x for "boss" trainers), + * the enemy will switch to that Pokemon. + */ if (trainer && !enemyPokemon.getMoveQueue().length) { const opponents = enemyPokemon.getOpponents(); @@ -64,10 +66,17 @@ export class EnemyCommandPhase extends FieldPhase { const index = trainer.getNextSummonIndex(enemyPokemon.trainerSlot, partyMemberScores); battle.turnCommands[this.fieldIndex + BattlerIndex.ENEMY] = - { command: Command.POKEMON, cursor: index, args: [false] }; - + { command: Command.POKEMON, cursor: index, args: [false] }; + console.log(enemyPokemon.name + " selects:", "Switch to " + this.scene.getEnemyParty()[index].name) battle.enemySwitchCounter++; + LoggerTools.enemyPlan[this.fieldIndex*2] = "Switching out" + LoggerTools.enemyPlan[this.fieldIndex*2 + 1] = "→ " + this.scene.getEnemyParty()[index].name + + enemyPokemon.flyout.setText() + + this.scene.updateCatchRate() + return this.end(); } } @@ -76,12 +85,40 @@ export class EnemyCommandPhase extends FieldPhase { /** Select a move to use (and a target to use it against, if applicable) */ const nextMove = enemyPokemon.getNextMove(); + const mv = new PokemonMove(nextMove.move) this.scene.currentBattle.turnCommands[this.fieldIndex + BattlerIndex.ENEMY] = - { command: Command.FIGHT, move: nextMove }; - + { command: Command.FIGHT, move: nextMove }; + const targetLabels = ["Counter", "[PLAYER L]", "[PLAYER R]", "[ENEMY L]", "[ENEMY R]"] + this.scene.getParty().forEach((v, i, a) => { + if (v.isActive() && v.name) { + targetLabels[i + 1] = v.name + } + }) + this.scene.getEnemyParty().forEach((v, i, a) => { + if (v.isActive() && v.name) { + targetLabels[i + 3] = v.name + } + }) + if (this.fieldIndex == 0) { + targetLabels[3] = "Self" + } + if (this.fieldIndex == 1) { + targetLabels[4] = "Self" + } + if (targetLabels[1] == targetLabels[2]) { + targetLabels[1] += " (L)" + targetLabels[2] += " (R)" + } + console.log(enemyPokemon.name + " selects:", mv.getName() + " → " + nextMove.targets.map((m) => targetLabels[m + 1])) this.scene.currentBattle.enemySwitchCounter = Math.max(this.scene.currentBattle.enemySwitchCounter - 1, 0); + LoggerTools.enemyPlan[this.fieldIndex*2] = mv.getName() + LoggerTools.enemyPlan[this.fieldIndex*2 + 1] = "→ " + nextMove.targets.map((m) => targetLabels[m + 1]) + this.scene.arenaFlyout.updateFieldText() + + this.scene.updateCatchRate() + this.end(); } } diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 7a07fde79fa..7fb6da64665 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -101,6 +101,7 @@ export class FaintPhase extends PokemonPhase { * If previous conditions weren't met, and the player has at least 1 legal Pokemon off the field, * push a phase that prompts the player to summon a Pokemon from their party. */ + LoggerTools.isFaintSwitch.value = true; this.scene.pushPhase(new SwitchPhase(this.scene, this.fieldIndex, true, false)); } } else { diff --git a/src/phases/learn-move-phase.ts b/src/phases/learn-move-phase.ts index d280b27c3f6..d8d540ac32f 100644 --- a/src/phases/learn-move-phase.ts +++ b/src/phases/learn-move-phase.ts @@ -9,6 +9,7 @@ import { SummaryUiMode } from "#app/ui/summary-ui-handler.js"; import { Mode } from "#app/ui/ui.js"; import i18next from "i18next"; import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; +import { PokemonMove } from "#app/field/pokemon.js"; import * as LoggerTools from "../logger"; export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { @@ -39,14 +40,81 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { const messageMode = this.scene.ui.getHandler() instanceof EvolutionSceneHandler ? Mode.EVOLUTION_SCENE : Mode.MESSAGE; - + const noHandler = () => { + this.scene.ui.setMode(messageMode).then(() => { + this.scene.ui.showText(i18next.t("battle:learnMoveStopTeaching", { moveName: move.name }), null, () => { + this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => { + this.scene.ui.setMode(messageMode); + var W = LoggerTools.getWave(LoggerTools.getDRPD(this.scene), this.scene.currentBattle.waveIndex, this.scene) + if (W.shop != "") { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, W.shop + "; skip learning it") + } else { + var actions = LoggerTools.getActionCount(this.scene, this.scene.currentBattle.waveIndex) + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, "Skip " + move.name) + } + this.scene.ui.showText(i18next.t("battle:learnMoveNotLearned", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => this.end(), null, true); + }, (false ? movesFullHandler : () => { + this.scene.ui.setMode(messageMode); + this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId)); + this.end(); + })); + }); + }); + }; + const noHandlerInstant = () => { + this.scene.ui.setMode(messageMode); + var W = LoggerTools.getWave(LoggerTools.getDRPD(this.scene), this.scene.currentBattle.waveIndex, this.scene) + if (W.shop != "") { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, W.shop + "; skip learning it") + } else { + var actions = LoggerTools.getActionCount(this.scene, this.scene.currentBattle.waveIndex) + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, (actions == 0 ? "" : "") + LoggerTools.playerPokeName(this.scene, pokemon) + " | Skip " + move.name) + } + this.scene.ui.showText(i18next.t("battle:learnMoveNotLearned", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => this.end(), null, true); + }; + const movesFullHandler = () => { + this.scene.ui.showText(i18next.t("battle:learnMovePrompt", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => { + this.scene.ui.showText(i18next.t("battle:learnMoveLimitReached", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { + this.scene.ui.showText(i18next.t("battle:learnMoveReplaceQuestion", { moveName: move.name }), null, () => { + this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => { + this.scene.ui.setMode(messageMode); + this.scene.ui.showText(i18next.t("battle:learnMoveForgetQuestion"), null, () => { + this.scene.ui.setModeWithoutClear(Mode.SUMMARY, this.getPokemon(), SummaryUiMode.LEARN_MOVE, move, (moveIndex: integer) => { + if (moveIndex === 4) { + noHandler(); + return; + } + this.scene.ui.setMode(messageMode).then(() => { + this.scene.ui.showText(i18next.t("battle:countdownPoof"), null, () => { + this.scene.ui.showText(i18next.t("battle:learnMoveForgetSuccess", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: pokemon.moveset[moveIndex]!.getName() }), null, () => { // TODO: is the bang correct? + this.scene.ui.showText(i18next.t("battle:learnMoveAnd"), null, () => { + var W = LoggerTools.getWave(LoggerTools.getDRPD(this.scene), this.scene.currentBattle.waveIndex, this.scene) + if (W.shop != "") { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, W.shop + " | " + new PokemonMove(this.moveId).getName() + " → " + pokemon.moveset[moveIndex]!.getName()) + } else { + var actions = LoggerTools.getActionCount(this.scene, this.scene.currentBattle.waveIndex) + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, (actions == 0 ? "" : "") + LoggerTools.playerPokeName(this.scene, pokemon) + " | " + new PokemonMove(this.moveId).getName() + " → " + pokemon.moveset[moveIndex]!.getName()) + } + pokemon.setMove(moveIndex, Moves.NONE); + this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId)); + this.end(); + }, null, true); + }, null, true); + }, null, true); + }); + }); + }, null, true); + }, noHandler); + }); + }, null, true); + }, null, true); + } if (emptyMoveIndex > -1) { pokemon.setMove(emptyMoveIndex, this.moveId); initMoveAnim(this.scene, this.moveId).then(() => { loadMoveAnimAssets(this.scene, [this.moveId], true) .then(() => { this.scene.ui.setMode(messageMode).then(() => { - // Sound loaded into game as is this.scene.playSound("level_up_fanfare"); this.scene.ui.showText(i18next.t("battle:learnMove", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => { this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeMoveLearnedTrigger, true); @@ -55,51 +123,16 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { }); }); }); - } else { + } else if (move.isUnimplemented() && false) { this.scene.ui.setMode(messageMode).then(() => { - this.scene.ui.showText(i18next.t("battle:learnMovePrompt", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => { - this.scene.ui.showText(i18next.t("battle:learnMoveLimitReached", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { - this.scene.ui.showText(i18next.t("battle:learnMoveReplaceQuestion", { moveName: move.name }), null, () => { - const noHandler = () => { - this.scene.ui.setMode(messageMode).then(() => { - this.scene.ui.showText(i18next.t("battle:learnMoveStopTeaching", { moveName: move.name }), null, () => { - this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => { - this.scene.ui.setMode(messageMode); - this.scene.ui.showText(i18next.t("battle:learnMoveNotLearned", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name }), null, () => this.end(), null, true); - }, () => { - this.scene.ui.setMode(messageMode); - this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId)); - this.end(); - }); - }); - }); - }; - this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => { - this.scene.ui.setMode(messageMode); - this.scene.ui.showText(i18next.t("battle:learnMoveForgetQuestion"), null, () => { - this.scene.ui.setModeWithoutClear(Mode.SUMMARY, this.getPokemon(), SummaryUiMode.LEARN_MOVE, move, (moveIndex: integer) => { - if (moveIndex === 4) { - noHandler(); - return; - } - this.scene.ui.setMode(messageMode).then(() => { - this.scene.ui.showText(i18next.t("battle:countdownPoof"), null, () => { - this.scene.ui.showText(i18next.t("battle:learnMoveForgetSuccess", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: pokemon.moveset[moveIndex]!.getName() }), null, () => { // TODO: is the bang correct? - this.scene.ui.showText(i18next.t("battle:learnMoveAnd"), null, () => { - pokemon.setMove(moveIndex, Moves.NONE); - this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId)); - this.end(); - }, null, true); - }, null, true); - }, null, true); - }); - }); - }, null, true); - }, noHandler); - }); - }, null, true); - }, null, true); + this.scene.ui.showText(`${getPokemonNameWithAffix(pokemon)} wants to learn ${move.name}, but this move does nothing.`, null, () => { + this.scene.ui.showText(`Would you like to teach ${move.name} anyways? (This will be logged as normal)`, null, () => { + this.scene.ui.setModeWithoutClear(Mode.CONFIRM, movesFullHandler, noHandler) + }) + }) }); + } else { + this.scene.ui.setMode(messageMode).then(movesFullHandler); } } } diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index 503a563a0c8..2f68367d24b 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -24,10 +24,10 @@ export class MoveEffectPhase extends PokemonPhase { super(scene, battlerIndex); this.move = move; /** - * In double battles, if the right Pokemon selects a spread move and the left Pokemon dies - * with no party members available to switch in, then the right Pokemon takes the index - * of the left Pokemon and gets hit unless this is checked. - */ + * In double battles, if the right Pokemon selects a spread move and the left Pokemon dies + * with no party members available to switch in, then the right Pokemon takes the index + * of the left Pokemon and gets hit unless this is checked. + */ if (targets.includes(battlerIndex) && this.move.getMove().moveTarget === MoveTarget.ALL_NEAR_OTHERS) { const i = targets.indexOf(battlerIndex); targets.splice(i, i + 1); @@ -49,9 +49,9 @@ export class MoveEffectPhase extends PokemonPhase { } /** - * Does an effect from this move override other effects on this turn? - * e.g. Charging moves (Fly, etc.) on their first turn of use. - */ + * Does an effect from this move override other effects on this turn? + * e.g. Charging moves (Fly, etc.) on their first turn of use. + */ const overridden = new Utils.BooleanHolder(false); /** The {@linkcode Move} object from {@linkcode allMoves} invoked by this phase */ const move = this.move.getMove(); @@ -66,10 +66,10 @@ export class MoveEffectPhase extends PokemonPhase { user.lapseTags(BattlerTagLapseType.MOVE_EFFECT); /** - * If this phase is for the first hit of the invoked move, - * resolve the move's total hit count. This block combines the - * effects of the move itself, Parental Bond, and Multi-Lens to do so. - */ + * If this phase is for the first hit of the invoked move, + * resolve the move's total hit count. This block combines the + * effects of the move itself, Parental Bond, and Multi-Lens to do so. + */ if (user.turnData.hitsLeft === undefined) { const hitCount = new Utils.IntegerHolder(1); // Assume single target for multi hit @@ -86,23 +86,23 @@ export class MoveEffectPhase extends PokemonPhase { } /** - * Log to be entered into the user's move history once the move result is resolved. - * Note that `result` (a {@linkcode MoveResult}) logs whether the move was successfully - * used in the sense of "Does it have an effect on the user?". - */ + * Log to be entered into the user's move history once the move result is resolved. + * Note that `result` (a {@linkcode MoveResult}) logs whether the move was successfully + * used in the sense of "Does it have an effect on the user?". + */ const moveHistoryEntry = { move: this.move.moveId, targets: this.targets, result: MoveResult.PENDING, virtual: this.move.virtual }; /** - * Stores results of hit checks of the invoked move against all targets, organized by battler index. - * @see {@linkcode hitCheck} - */ + * Stores results of hit checks of the invoked move against all targets, organized by battler index. + * @see {@linkcode hitCheck} + */ const targetHitChecks = Object.fromEntries(targets.map(p => [p.getBattlerIndex(), this.hitCheck(p)])); const hasActiveTargets = targets.some(t => t.isActive(true)); /** - * If no targets are left for the move to hit (FAIL), or the invoked move is single-target - * (and not random target) and failed the hit check against its target (MISS), log the move - * as FAILed or MISSed (depending on the conditions above) and end this phase. - */ + * If no targets are left for the move to hit (FAIL), or the invoked move is single-target + * (and not random target) and failed the hit check against its target (MISS), log the move + * as FAILed or MISSed (depending on the conditions above) and end this phase. + */ if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) { this.stopMultiHit(); if (hasActiveTargets) { @@ -126,9 +126,9 @@ export class MoveEffectPhase extends PokemonPhase { let hasHit: boolean = false; for (const target of targets) { /** - * If the move missed a target, stop all future hits against that target - * and move on to the next target (if there is one). - */ + * If the move missed a target, stop all future hits against that target + * and move on to the next target (if there is one). + */ if (!targetHitChecks[target.getBattlerIndex()]) { this.stopMultiHit(target); this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); @@ -153,7 +153,7 @@ export class MoveEffectPhase extends PokemonPhase { /** Is the target protected by Protect, etc. or a relevant conditional protection effect? */ const isProtected = (bypassIgnoreProtect.value || !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target)) - && (hasConditionalProtectApplied.value || target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))); + && (hasConditionalProtectApplied.value || target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))); /** Does this phase represent the invoked move's first strike? */ const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount); @@ -164,23 +164,23 @@ export class MoveEffectPhase extends PokemonPhase { } /** - * Since all fail/miss checks have applied, the move is considered successfully applied. - * It's worth noting that if the move has no effect or is protected against, this assignment - * is overwritten and the move is logged as a FAIL. - */ + * Since all fail/miss checks have applied, the move is considered successfully applied. + * It's worth noting that if the move has no effect or is protected against, this assignment + * is overwritten and the move is logged as a FAIL. + */ moveHistoryEntry.result = MoveResult.SUCCESS; /** - * Stores the result of applying the invoked move to the target. - * If the target is protected, the result is always `NO_EFFECT`. - * Otherwise, the hit result is based on type effectiveness, immunities, - * and other factors that may negate the attack or status application. - * - * Internally, the call to {@linkcode Pokemon.apply} is where damage is calculated - * (for attack moves) and the target's HP is updated. However, this isn't - * made visible to the user until the resulting {@linkcode DamagePhase} - * is invoked. - */ + * Stores the result of applying the invoked move to the target. + * If the target is protected, the result is always `NO_EFFECT`. + * Otherwise, the hit result is based on type effectiveness, immunities, + * and other factors that may negate the attack or status application. + * + * Internally, the call to {@linkcode Pokemon.apply} is where damage is calculated + * (for attack moves) and the target's HP is updated. However, this isn't + * made visible to the user until the resulting {@linkcode DamagePhase} + * is invoked. + */ const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT; /** Does {@linkcode hitResult} indicate that damage was dealt to the target? */ @@ -198,9 +198,9 @@ export class MoveEffectPhase extends PokemonPhase { } /** - * If the move has no effect on the target (i.e. the target is protected or immune), - * change the logged move result to FAIL. - */ + * If the move has no effect on the target (i.e. the target is protected or immune), + * change the logged move result to FAIL. + */ if (hitResult === HitResult.NO_EFFECT) { moveHistoryEntry.result = MoveResult.FAIL; } @@ -209,19 +209,19 @@ export class MoveEffectPhase extends PokemonPhase { const lastHit = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive()); /** - * If the user can change forms by using the invoked move, - * it only changes forms after the move's last hit - * (see Relic Song's interaction with Parental Bond when used by Meloetta). - */ + * If the user can change forms by using the invoked move, + * it only changes forms after the move's last hit + * (see Relic Song's interaction with Parental Bond when used by Meloetta). + */ if (lastHit) { this.scene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger); } /** - * Create a Promise that applys *all* effects from the invoked move's MoveEffectAttrs. - * These are ordered by trigger type (see {@linkcode MoveEffectTrigger}), and each trigger - * type requires different conditions to be met with respect to the move's hit result. - */ + * Create a Promise that applys *all* effects from the invoked move's MoveEffectAttrs. + * These are ordered by trigger type (see {@linkcode MoveEffectTrigger}), and each trigger + * type requires different conditions to be met with respect to the move's hit result. + */ applyAttrs.push(new Promise(resolve => { // Apply all effects with PRE_MOVE triggers (if the target isn't immune to the move) applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && hitResult !== HitResult.NO_EFFECT, @@ -231,21 +231,21 @@ export class MoveEffectPhase extends PokemonPhase { /** Are the move's effects tied to the first turn of a charge move? */ const chargeEffect = !!move.getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget() ?? null, move)); /** - * If the invoked move's effects are meant to trigger during the move's "charge turn," - * ignore all effects after this point. - * Otherwise, apply all self-targeted POST_APPLY effects. - */ + * If the invoked move's effects are meant to trigger during the move's "charge turn," + * ignore all effects after this point. + * Otherwise, apply all self-targeted POST_APPLY effects. + */ Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY - && attr.selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, move)).then(() => { + && attr.selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, move)).then(() => { // All effects past this point require the move to have hit the target if (hitResult !== HitResult.NO_EFFECT) { // Apply all non-self-targeted POST_APPLY effects applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY - && !(attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, this.move.getMove()).then(() => { + && !(attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, this.move.getMove()).then(() => { /** - * If the move hit, and the target doesn't have Shield Dust, - * apply the chance to flinch the target gained from King's Rock - */ + * If the move hit, and the target doesn't have Shield Dust, + * apply the chance to flinch the target gained from King's Rock + */ if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr)) { const flinched = new Utils.BooleanHolder(false); user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched); @@ -255,7 +255,7 @@ export class MoveEffectPhase extends PokemonPhase { } // If the move was not protected against, apply all HIT effects Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT - && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && (!attr.firstTargetOnly || firstTarget), user, target, this.move.getMove()).then(() => { + && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && (!attr.firstTargetOnly || firstTarget), user, target, this.move.getMove()).then(() => { // Apply the target's post-defend ability effects (as long as the target is active or can otherwise apply them) return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult).then(() => { // If the invoked move is an enemy attack, apply the enemy's status effect-inflicting tags and tokens @@ -270,9 +270,9 @@ export class MoveEffectPhase extends PokemonPhase { // Apply the user's post-attack ability effects applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move.getMove(), hitResult).then(() => { /** - * If the invoked move is an attack, apply the user's chance to - * steal an item from the target granted by Grip Claw - */ + * If the invoked move is an attack, apply the user's chance to + * steal an item from the target granted by Grip Claw + */ if (this.move.getMove() instanceof AttackMove) { this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target); } @@ -316,12 +316,12 @@ export class MoveEffectPhase extends PokemonPhase { move.type = move.defaultType; const user = this.getUserPokemon(); /** - * If this phase isn't for the invoked move's last strike, - * unshift another MoveEffectPhase for the next strike. - * Otherwise, queue a message indicating the number of times the move has struck - * (if the move has struck more than once), then apply the heal from Shell Bell - * to the user. - */ + * If this phase isn't for the invoked move's last strike, + * unshift another MoveEffectPhase for the next strike. + * Otherwise, queue a message indicating the number of times the move has struck + * (if the move has struck more than once), then apply the heal from Shell Bell + * to the user. + */ if (user) { if (user.turnData.hitsLeft && --user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) { this.scene.unshiftPhase(this.getNewHitPhase()); @@ -341,10 +341,10 @@ export class MoveEffectPhase extends PokemonPhase { } /** - * Resolves whether this phase's invoked move hits or misses the given target - * @param target {@linkcode Pokemon} the Pokemon targeted by the invoked move - * @returns `true` if the move does not miss the target; `false` otherwise - */ + * Resolves whether this phase's invoked move hits or misses the given target + * @param target {@linkcode Pokemon} the Pokemon targeted by the invoked move + * @returns `true` if the move does not miss the target; `false` otherwise + */ hitCheck(target: Pokemon): boolean { // Moves targeting the user and entry hazards can't miss if ([MoveTarget.USER, MoveTarget.ENEMY_SIDE].includes(this.move.getMove().moveTarget)) { @@ -387,7 +387,7 @@ export class MoveEffectPhase extends PokemonPhase { } const accuracyMultiplier = user.getAccuracyMultiplier(target, this.move.getMove()); - const rand = user.randSeedInt(100, 1); + const rand = user.randSeedInt(100, 1, "Accuracy roll"); return rand <= moveAccuracy * (accuracyMultiplier!); // TODO: is this bang correct? } @@ -411,9 +411,9 @@ export class MoveEffectPhase extends PokemonPhase { } /** - * Removes the given {@linkcode Pokemon} from this phase's target list - * @param target {@linkcode Pokemon} the Pokemon to be removed - */ + * Removes the given {@linkcode Pokemon} from this phase's target list + * @param target {@linkcode Pokemon} the Pokemon to be removed + */ removeTarget(target: Pokemon): void { const targetIndex = this.targets.findIndex(ind => ind === target.getBattlerIndex()); if (targetIndex !== -1) { @@ -422,22 +422,22 @@ export class MoveEffectPhase extends PokemonPhase { } /** - * Prevents subsequent strikes of this phase's invoked move from occurring - * @param target {@linkcode Pokemon} if defined, only stop subsequent - * strikes against this Pokemon - */ + * Prevents subsequent strikes of this phase's invoked move from occurring + * @param target {@linkcode Pokemon} if defined, only stop subsequent + * strikes against this Pokemon + */ stopMultiHit(target?: Pokemon): void { /** If given a specific target, remove the target from subsequent strikes */ if (target) { this.removeTarget(target); } /** - * If no target specified, or the specified target was the last of this move's - * targets, completely cancel all subsequent strikes. - */ + * If no target specified, or the specified target was the last of this move's + * targets, completely cancel all subsequent strikes. + */ if (!target || this.targets.length === 0 ) { - this.getUserPokemon()!.turnData.hitCount = 1; // TODO: is the bang correct here? - this.getUserPokemon()!.turnData.hitsLeft = 1; // TODO: is the bang correct here? + this.getUserPokemon()!.turnData.hitCount = 1; // TODO: is the bang correct here? + this.getUserPokemon()!.turnData.hitsLeft = 1; // TODO: is the bang correct here? } } diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 9d7be9c7b7d..8a912b023dd 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -218,10 +218,10 @@ export class MovePhase extends BattlePhase { } /** - * Trigger pokemon type change before playing the move animation - * Will still change the user's type when using Roar, Whirlwind, Trick-or-Treat, and Forest's Curse, - * regardless of whether the move successfully executes or not. - */ + * Trigger pokemon type change before playing the move animation + * Will still change the user's type when using Roar, Whirlwind, Trick-or-Treat, and Forest's Curse, + * regardless of whether the move successfully executes or not. + */ if (success || [Moves.ROAR, Moves.WHIRLWIND, Moves.TRICK_OR_TREAT, Moves.FORESTS_CURSE].includes(this.move.moveId)) { applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove()); } @@ -254,7 +254,7 @@ export class MovePhase extends BattlePhase { switch (this.pokemon.status.effect) { case StatusEffect.PARALYSIS: - if (!this.pokemon.randSeedInt(4)) { + if (!this.pokemon.randSeedInt(4, undefined, "Paralysis chance")) { activated = true; this.cancelled = true; } @@ -266,7 +266,7 @@ export class MovePhase extends BattlePhase { this.cancelled = activated; break; case StatusEffect.FREEZE: - healed = !!this.move.getMove().findAttr(attr => attr instanceof HealStatusEffectAttr && attr.selfTarget && attr.isOfEffect(StatusEffect.FREEZE)) || !this.pokemon.randSeedInt(5); + healed = !!this.move.getMove().findAttr(attr => attr instanceof HealStatusEffectAttr && attr.selfTarget && attr.isOfEffect(StatusEffect.FREEZE)) || !this.pokemon.randSeedInt(5, undefined, "Chance to thaw out from freeze"); activated = !healed; this.cancelled = activated; break; diff --git a/src/phases/obtain-status-effect-phase.ts b/src/phases/obtain-status-effect-phase.ts index eff9ce00f6b..11f27aefb61 100644 --- a/src/phases/obtain-status-effect-phase.ts +++ b/src/phases/obtain-status-effect-phase.ts @@ -29,7 +29,7 @@ export class ObtainStatusEffectPhase extends PokemonPhase { if (!pokemon?.status) { if (pokemon?.trySetStatus(this.statusEffect, false, this.sourcePokemon)) { if (this.cureTurn) { - pokemon.status!.cureTurn = this.cureTurn; // TODO: is this bang correct? + pokemon.status!.cureTurn = this.cureTurn; // TODO: is this bang correct? } pokemon.updateInfo(true); new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect! - 1), pokemon).play(this.scene, () => { diff --git a/src/phases/party-heal-phase.ts b/src/phases/party-heal-phase.ts index 92afbd64f54..0eb93f2c390 100644 --- a/src/phases/party-heal-phase.ts +++ b/src/phases/party-heal-phase.ts @@ -24,7 +24,7 @@ export class PartyHealPhase extends BattlePhase { pokemon.hp = pokemon.getMaxHp(); pokemon.resetStatus(); for (const move of pokemon.moveset) { - move!.ppUsed = 0; // TODO: is this bang correct? + move!.ppUsed = 0; // TODO: is this bang correct? } pokemon.updateInfo(true); } diff --git a/src/phases/scan-ivs-phase.ts b/src/phases/scan-ivs-phase.ts index f750125f901..44c7fc9cb1b 100644 --- a/src/phases/scan-ivs-phase.ts +++ b/src/phases/scan-ivs-phase.ts @@ -52,6 +52,7 @@ export class ScanIvsPhase extends PokemonPhase { if (!this.scene.hideIvs) { this.scene.ui.showText(i18next.t("battle:ivScannerUseQuestion", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { this.scene.ui.setMode(Mode.CONFIRM, () => { + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, "IV Scanner → " + LoggerTools.enemyPokeName(this.scene, pokemon)) this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.clearText(); new CommonBattleAnim(CommonAnim.LOCK_ON, pokemon, pokemon).play(this.scene, () => { diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index bd08ea4003b..42b1d22ac91 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -30,8 +30,8 @@ export class SelectBiomePhase extends BattlePhase { }; if ((this.scene.gameMode.isClassic && this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex + 9)) - || (this.scene.gameMode.isDaily && this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) - || (this.scene.gameMode.hasShortBiomes && !(this.scene.currentBattle.waveIndex % 50))) { + || (this.scene.gameMode.isDaily && this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) + || (this.scene.gameMode.hasShortBiomes && !(this.scene.currentBattle.waveIndex % 50))) { setNextBiome(Biome.END); } else if (this.scene.gameMode.hasRandomBiomes) { setNextBiome(this.generateNextBiome()); diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts index 4b1682fccd1..1c967698ef5 100644 --- a/src/phases/select-modifier-phase.ts +++ b/src/phases/select-modifier-phase.ts @@ -1,6 +1,6 @@ import BattleScene from "#app/battle-scene.js"; import { ModifierTier } from "#app/modifier/modifier-tier.js"; -import { regenerateModifierPoolThresholds, ModifierTypeOption, ModifierType, getPlayerShopModifierTypeOptionsForWave, PokemonModifierType, FusePokemonModifierType, PokemonMoveModifierType, TmModifierType, RememberMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, ModifierPoolType, getPlayerModifierTypeOptions } from "#app/modifier/modifier-type.js"; +import { regenerateModifierPoolThresholds, ModifierTypeOption, ModifierType, getPlayerShopModifierTypeOptionsForWave, PokemonModifierType, FusePokemonModifierType, PokemonMoveModifierType, TmModifierType, RememberMoveModifierType, PokemonPpRestoreModifierType, PokemonPpUpModifierType, ModifierPoolType, getPlayerModifierTypeOptions, getPartyLuckValue, getLuckString, setEvioliteOverride, calculateItemConditions } from "#app/modifier/modifier-type.js"; import { ExtraModifierModifier, Modifier, PokemonHeldItemModifier } from "#app/modifier/modifier.js"; import ModifierSelectUiHandler, { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler.js"; import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler.js"; @@ -13,13 +13,70 @@ import * as LoggerTools from "../logger"; export class SelectModifierPhase extends BattlePhase { private rerollCount: integer; - private modifierTiers: ModifierTier[]; + private modifierTiers: ModifierTier[] = []; + private modifierPredictions: ModifierTypeOption[][] = [] + private predictionCost: integer = 0; + private costTiers: integer[] = []; - constructor(scene: BattleScene, rerollCount: integer = 0, modifierTiers?: ModifierTier[]) { + constructor(scene: BattleScene, rerollCount: integer = 0, modifierTiers?: ModifierTier[], predictionCost?: integer, modifierPredictions?: ModifierTypeOption[][]) { super(scene); this.rerollCount = rerollCount; this.modifierTiers = modifierTiers!; // TODO: is this bang correct? + this.modifierPredictions = [] + if (modifierPredictions != undefined) { + this.modifierPredictions = modifierPredictions; + } + this.predictionCost = 0 + this.costTiers = [] + } + + generateSelection(rerollOverride: integer, modifierOverride?: integer, eviolite?: boolean) { + //const STATE = Phaser.Math.RND.state() // Store RNG state + //console.log("====================") + //console.log(" Reroll Prediction: " + rerollOverride) + const party = this.scene.getParty(); + if (eviolite) { + setEvioliteOverride("on") + } else { + setEvioliteOverride("off") + } + regenerateModifierPoolThresholds(party, this.getPoolType(), rerollOverride); + const modifierCount = new Utils.IntegerHolder(3); + if (this.isPlayer()) { + this.scene.applyModifiers(ExtraModifierModifier, true, modifierCount); + } + if (modifierOverride) { + //modifierCount.value = modifierOverride + } + const typeOptions: ModifierTypeOption[] = this.getModifierTypeOptions(modifierCount.value, true, true); + setEvioliteOverride("") + typeOptions.forEach((option, idx) => { + option.netprice = this.predictionCost + if (option.type.name == "Nugget") { + option.netprice -= this.scene.getWaveMoneyAmount(1) + } + if (option.type.name == "Big Nugget") { + option.netprice -= this.scene.getWaveMoneyAmount(2.5) + } + if (option.type.name == "Relic Gold") { + option.netprice -= this.scene.getWaveMoneyAmount(10) + } + //console.log(option.type.name) + }) + //console.log("====================") + if (eviolite) { + this.modifierPredictions[rerollOverride].forEach((m, i) => { + if (m.type.name != typeOptions[i].type.name) { + m.eviolite = typeOptions[i].type + } + }) + } else { + this.modifierPredictions[rerollOverride] = typeOptions + } + this.costTiers.push(this.predictionCost) + this.predictionCost += this.getRerollCost(typeOptions, false, rerollOverride) + //Phaser.Math.RND.state(STATE) // Restore RNG state like nothing happened } start() { @@ -27,6 +84,21 @@ export class SelectModifierPhase extends BattlePhase { if (!this.rerollCount) { this.updateSeed(); + console.log(calculateItemConditions(this.scene.getParty(), false, true)) + console.log("\n\nPerforming reroll prediction (Eviolite OFF)\n\n\n") + this.predictionCost = 0 + this.costTiers = [] + for (var idx = 0; idx < 10 && this.predictionCost < this.scene.money; idx++) { + this.generateSelection(idx, undefined, false) + } + this.updateSeed(); + console.log("\n\nPerforming reroll prediction (Eviolite ON)\n\n\n") + this.predictionCost = 0 + this.costTiers = [] + for (var idx = 0; idx < 10 && this.predictionCost < this.scene.money; idx++) { + this.generateSelection(idx, undefined, true) + } + this.updateSeed(); } else { this.scene.reroll = false; } @@ -43,10 +115,11 @@ export class SelectModifierPhase extends BattlePhase { if (rowCursor < 0 || cursor < 0) { this.scene.ui.showText(i18next.t("battle:skipItemQuestion"), null, () => { this.scene.ui.setOverlayMode(Mode.CONFIRM, () => { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "Skip taking items") this.scene.ui.revertMode(); this.scene.ui.setMode(Mode.MESSAGE); super.end(); - }, () => this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers))); + }, () => this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers), this.modifierPredictions)); }); return false; } @@ -55,30 +128,72 @@ export class SelectModifierPhase extends BattlePhase { switch (rowCursor) { case 0: switch (cursor) { - case 0: - const rerollCost = this.getRerollCost(typeOptions, this.scene.lockModifierTiers); - if (this.scene.money < rerollCost) { - this.scene.ui.playError(); - return false; - } else { - this.scene.reroll = true; - this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount + 1, typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as ModifierTier[])); - this.scene.ui.clearText(); - this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); - if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { - this.scene.money -= rerollCost; - this.scene.updateMoneyText(); - this.scene.animateMoneyChanged(false); + case 0: + const rerollCost1 = this.getRerollCost(typeOptions, this.scene.lockModifierTiers); + if (this.scene.money < rerollCost1) { + this.scene.ui.playError(); + return false; + } else { + this.scene.reroll = true; + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, "Reroll" + (this.scene.lockModifierTiers ? " (Locked)" : "")) + this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount + 1, typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as ModifierTier[], this.predictionCost, this.modifierPredictions)); + this.scene.ui.clearText(); + this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); + if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { + this.scene.money -= rerollCost1; + this.scene.updateMoneyText(); + this.scene.animateMoneyChanged(false); + this.scene.playSound("se/buy"); + } } - this.scene.playSound("se/buy"); - } - break; + break; + case 0.1: + const rerollCost2 = this.getRerollCost(this.modifierPredictions[this.rerollCount], false); + if (this.scene.money < rerollCost2) { + this.scene.ui.playError(); + return false; + } else { + this.scene.reroll = true; + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, "+1 Reroll") + this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount + 1, typeOptions.map(o => o.type!.tier), this.predictionCost, this.modifierPredictions)); + this.scene.ui.clearText(); + this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); + if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { + this.scene.money -= rerollCost2; + this.scene.updateMoneyText(); + this.scene.animateMoneyChanged(false); + this.scene.playSound("se/buy"); + } + } + break; + case 0.2: + const rerollCost3 = this.getRerollCost(this.modifierPredictions[this.rerollCount + 1], false); + { + this.scene.reroll = true; + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, "-1 Reroll") + this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount - 1, typeOptions.map(o => o.type!.tier), this.predictionCost, this.modifierPredictions)); + this.scene.ui.clearText(); + this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); + if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { + this.scene.money -= rerollCost3; + this.scene.updateMoneyText(); + this.scene.animateMoneyChanged(false); + this.scene.playSound("se/buy"); + } + } + break; case 1: - this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, itemQuantity: integer, toSlotIndex: integer) => { + this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, itemQuantity: integer, toSlotIndex: integer, isAll: boolean, isFirst: boolean) => { if (toSlotIndex !== undefined && fromSlotIndex < 6 && toSlotIndex < 6 && fromSlotIndex !== toSlotIndex && itemIndex > -1) { const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier - && m.isTransferrable && m.pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[]; + && m.isTransferrable && m.pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[]; const itemModifier = itemModifiers[itemIndex]; + if (isAll) { + if (isFirst) + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, `Transfer ALL | ${LoggerTools.playerPokeName(this.scene, fromSlotIndex)} → ${LoggerTools.playerPokeName(this.scene, toSlotIndex)}`) + } else { + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, `Transfer ${itemModifier.type.name + (itemQuantity == itemModifier.getStackCount() ? "" : " x" + itemQuantity)} | ${LoggerTools.playerPokeName(this.scene, fromSlotIndex)} → ${LoggerTools.playerPokeName(this.scene, toSlotIndex)}`) + } this.scene.tryTransferHeldItemModifier(itemModifier, party[toSlotIndex], true, itemQuantity); } else { this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); @@ -153,6 +268,7 @@ export class SelectModifierPhase extends BattlePhase { if (modifierType instanceof FusePokemonModifierType) { this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.SPLICE, -1, (fromSlotIndex: integer, spliceSlotIndex: integer) => { if (spliceSlotIndex !== undefined && fromSlotIndex < 6 && spliceSlotIndex < 6 && fromSlotIndex !== spliceSlotIndex) { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, modifierType.name + " → " + this.scene.getParty()[fromSlotIndex].name + " + " + this.scene.getParty()[spliceSlotIndex].name) this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer()).then(() => { const modifier = modifierType.newModifier(party[fromSlotIndex], party[spliceSlotIndex])!; //TODO: is the bang correct? applyModifier(modifier, true); @@ -182,6 +298,15 @@ export class SelectModifierPhase extends BattlePhase { ? modifierType.newModifier(party[slotIndex]) : modifierType.newModifier(party[slotIndex], option as integer) : modifierType.newModifier(party[slotIndex], option - PartyOption.MOVE_1); + if (isPpRestoreModifier) { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, modifierType.name + " → " + this.scene.getParty()[slotIndex].name + " → " + this.scene.getParty()[slotIndex].moveset[option - PartyOption.MOVE_1]!.getName()) + } else if (isRememberMoveModifier) { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, modifierType.name + " → " + this.scene.getParty()[slotIndex].name) + } else if (isTmModifier) { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, modifierType.name + " → " + this.scene.getParty()[slotIndex].name) + } else { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, modifierType.name + " → " + this.scene.getParty()[slotIndex].name) + } applyModifier(modifier!, true); // TODO: is the bang correct? }); } else { @@ -190,11 +315,91 @@ export class SelectModifierPhase extends BattlePhase { }, pokemonModifierType.selectFilter, modifierType instanceof PokemonMoveModifierType ? (modifierType as PokemonMoveModifierType).moveSelectFilter : undefined, tmMoveId, isPpRestoreModifier); } } else { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, modifierType!.name) applyModifier(modifierType!.newModifier()!); // TODO: is the bang correct? } return !cost!;// TODO: is the bang correct? }; + if (this.rerollCount == 0) { + if (true) { + this.modifierPredictions.forEach((mp, r) => { + // costTiers + console.log("Rerolls: " + r + (this.costTiers[r] != 0 ? " - ₽" + this.costTiers[r] : "")) + mp.forEach((m, i) => { + console.log(" " + m.type!.name + (m.netprice != this.costTiers[r] ? " - ₽" + m.netprice : "")) + if (m.eviolite) { + console.log(" With Eviolite unlocked: " + m.eviolite.name) + } + if (m.alternates) { + //console.log(m.alternates) + let showedLuckFlag = false + for (var j = 0, currentTier = m.type!.tier; j < m.alternates.length; j++) { + if (m.alternates[j] > currentTier) { + currentTier = m.alternates[j] + if (m.advancedAlternates) { + if (!showedLuckFlag) { + showedLuckFlag = true + console.log(" Your luck: " + getPartyLuckValue(party) + " (" + getLuckString(getPartyLuckValue(party)) + ")") + } + console.log(" At " + j + " luck (" + getLuckString(j) + "): " + m.advancedAlternates[j]) + } else { + if (!showedLuckFlag) { + showedLuckFlag = true + console.log(" Your luck: " + getPartyLuckValue(party) + " (" + getLuckString(getPartyLuckValue(party)) + ")") + } + console.log(" At " + j + " luck (" + getLuckString(j) + "): " + LoggerTools.tierNames[currentTier] + "-tier item (failed to generate item)") + } + } + } + } else { + //console.log(" No alt-luck data") + } + }) + }) + } else { + let modifierList: string[] = [] + this.modifierPredictions.forEach((mp, r) => { + //console.log("Rerolls: " + r) + mp.forEach((m, i) => { + modifierList.push(m.type!.name + (r > 0 ? " (x" + r + ")" : "")) + //console.log(" " + m.type!.name) + if (m.eviolite) { + modifierList.push(m.type!.name + (r > 0 ? " (x" + r + " with eviolite unlocked)" : " (With eviolite unlocked)")) + //console.log(" With Eviolite unlocked: " + m.eviolite.name) + } + if (m.alternates) { + //console.log(m.alternates) + let showedLuckFlag = false + for (var j = 0, currentTier = m.type!.tier; j < m.alternates.length; j++) { + if (m.alternates[j] > currentTier) { + currentTier = m.alternates[j] + if (m.advancedAlternates) { + if (!showedLuckFlag) { + showedLuckFlag = true + console.log(" Your luck: " + getPartyLuckValue(party) + " (" + getLuckString(getPartyLuckValue(party)) + ")") + } + console.log(" At " + j + " luck (" + getLuckString(j) + "): " + m.advancedAlternates[j]) + } else { + if (!showedLuckFlag) { + showedLuckFlag = true + console.log(" Your luck: " + getPartyLuckValue(party) + " (" + getLuckString(getPartyLuckValue(party)) + ")") + } + console.log(" At " + j + " luck (" + getLuckString(j) + "): " + LoggerTools.tierNames[currentTier] + "-tier item (failed to generate item)") + } + } + } + } else { + //console.log(" No alt-luck data") + } + }) + }) + modifierList.sort() + modifierList.forEach(v => { + console.log(v) + }) + } + } this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); } @@ -206,7 +411,7 @@ export class SelectModifierPhase extends BattlePhase { return true; } - getRerollCost(typeOptions: ModifierTypeOption[], lockRarities: boolean): number { + getRerollCost(typeOptions: ModifierTypeOption[], lockRarities: boolean, rerollOverride?: integer): integer { let baseValue = 0; if (Overrides.WAIVE_ROLL_FEE_OVERRIDE) { return baseValue; @@ -218,15 +423,15 @@ export class SelectModifierPhase extends BattlePhase { } else { baseValue = 250; } - return Math.min(Math.ceil(this.scene.currentBattle.waveIndex / 10) * baseValue * Math.pow(2, this.rerollCount), Number.MAX_SAFE_INTEGER); + return Math.min(Math.ceil(this.scene.currentBattle.waveIndex / 10) * baseValue * Math.pow(2, (rerollOverride != undefined ? rerollOverride : this.rerollCount)), Number.MAX_SAFE_INTEGER); } getPoolType(): ModifierPoolType { return ModifierPoolType.PLAYER; } - getModifierTypeOptions(modifierCount: integer): ModifierTypeOption[] { - return getPlayerModifierTypeOptions(modifierCount, this.scene.getParty(), this.scene.lockModifierTiers ? this.modifierTiers : undefined); + getModifierTypeOptions(modifierCount: integer, shutUpBro?: boolean, calcAllLuck?: boolean, advanced?: boolean): ModifierTypeOption[] { + return getPlayerModifierTypeOptions(modifierCount, this.scene.getParty(), this.scene.lockModifierTiers ? this.modifierTiers : undefined, this.scene, shutUpBro, calcAllLuck, advanced); } addModifier(modifier: Modifier): Promise { diff --git a/src/phases/select-target-phase.ts b/src/phases/select-target-phase.ts index abf63c60124..bdb4ffd299a 100644 --- a/src/phases/select-target-phase.ts +++ b/src/phases/select-target-phase.ts @@ -22,10 +22,10 @@ export class SelectTargetPhase extends PokemonPhase { this.scene.currentBattle.turnCommands[this.fieldIndex] = null; this.scene.unshiftPhase(new CommandPhase(this.scene, this.fieldIndex)); } else { - turnCommand!.targets = targets; //TODO: is the bang correct here? + turnCommand!.targets = targets; //TODO: is the bang correct here? } if (turnCommand?.command === Command.BALL && this.fieldIndex) { - this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; //TODO: is the bang correct here? + this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; //TODO: is the bang correct here? } this.end(); }); diff --git a/src/phases/stat-change-phase.ts b/src/phases/stat-change-phase.ts index 6ec4c3b3229..8db276bc061 100644 --- a/src/phases/stat-change-phase.ts +++ b/src/phases/stat-change-phase.ts @@ -170,7 +170,7 @@ export class StatChangePhase extends PokemonPhase { getRandomStat(): BattleStat { const allStats = Utils.getEnumValues(BattleStat); - return this.getPokemon() ? allStats[this.getPokemon()!.randSeedInt(BattleStat.SPD + 1)] : BattleStat.ATK; // TODO: return default ATK on random? idk... + return this.getPokemon() ? allStats[this.getPokemon()!.randSeedInt(BattleStat.SPD + 1, undefined, "Randomly selecting a stat")] : BattleStat.ATK; // TODO: return default ATK on random? idk... } aggregateStatChanges(random: boolean = false): void { diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index 2044d271bae..e816d40944a 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -30,8 +30,8 @@ export class SummonPhase extends PartyMemberPokemonPhase { } /** - * Sends out a Pokemon before the battle begins and shows the appropriate messages - */ + * Sends out a Pokemon before the battle begins and shows the appropriate messages + */ preSummon(): void { const partyMember = this.getPokemon(); // If the Pokemon about to be sent out is fainted or illegal under a challenge, switch to the first non-fainted legal Pokemon diff --git a/src/phases/switch-phase.ts b/src/phases/switch-phase.ts index 2a538ccf4b6..33479c736ac 100644 --- a/src/phases/switch-phase.ts +++ b/src/phases/switch-phase.ts @@ -15,14 +15,14 @@ export class SwitchPhase extends BattlePhase { private doReturn: boolean; /** - * Creates a new SwitchPhase - * @param scene {@linkcode BattleScene} Current battle scene - * @param fieldIndex Field index to switch out - * @param isModal Indicates if the switch should be forced (true) or is - * optional (false). - * @param doReturn Indicates if the party member on the field should be - * recalled to ball or has already left the field. Passed to {@linkcode SwitchSummonPhase}. - */ + * Creates a new SwitchPhase + * @param scene {@linkcode BattleScene} Current battle scene + * @param fieldIndex Field index to switch out + * @param isModal Indicates if the switch should be forced (true) or is + * optional (false). + * @param doReturn Indicates if the party member on the field should be + * recalled to ball or has already left the field. Passed to {@linkcode SwitchSummonPhase}. + */ constructor(scene: BattleScene, fieldIndex: integer, isModal: boolean, doReturn: boolean) { super(scene); @@ -36,6 +36,8 @@ export class SwitchPhase extends BattlePhase { // Skip modal switch if impossible (no remaining party members that aren't in battle) if (this.isModal && !this.scene.getParty().filter(p => p.isAllowedInBattle() && !p.isActive(true)).length) { + LoggerTools.isPreSwitch.value = false; + LoggerTools.isFaintSwitch.value = false; return super.end(); } @@ -50,6 +52,8 @@ export class SwitchPhase extends BattlePhase { // Check if there is any space still in field if (this.isModal && this.scene.getPlayerField().filter(p => p.isAllowedInBattle() && p.isActive(true)).length >= this.scene.currentBattle.getBattlerCount()) { + LoggerTools.isPreSwitch.value = false; + LoggerTools.isFaintSwitch.value = false; return super.end(); } @@ -57,9 +61,19 @@ export class SwitchPhase extends BattlePhase { const fieldIndex = this.scene.currentBattle.getBattlerCount() === 1 || this.scene.getParty().filter(p => p.isAllowedInBattle()).length > 1 ? this.fieldIndex : 0; this.scene.ui.setMode(Mode.PARTY, this.isModal ? PartyUiMode.FAINT_SWITCH : PartyUiMode.POST_BATTLE_SWITCH, fieldIndex, (slotIndex: integer, option: PartyOption) => { + if (this.isModal) {console.error("Forced Switch Detected")} if (slotIndex >= this.scene.currentBattle.getBattlerCount() && slotIndex < 6) { + if (LoggerTools.isPreSwitch.value) { + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, "Pre-switch" + (option == PartyOption.PASS_BATON ? "+ Baton" : "") + " " + LoggerTools.playerPokeName(this.scene, fieldIndex) + " to " + LoggerTools.playerPokeName(this.scene, slotIndex)) + } else if (LoggerTools.isFaintSwitch.value) { + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, (option == PartyOption.PASS_BATON ? "Baton" : "Send") + " in " + LoggerTools.playerPokeName(this.scene, slotIndex)) + } else { + //LoggerTools.Actions[this.scene.getParty()[fieldIndex].getBattlerIndex()] += " to " + LoggerTools.playerPokeName(this.scene, slotIndex) + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, `Switch ${LoggerTools.playerPokeName(this.scene, fieldIndex)} to ${LoggerTools.playerPokeName(this.scene, slotIndex)}`) + } this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, fieldIndex, slotIndex, this.doReturn, option === PartyOption.PASS_BATON)); } + LoggerTools.isPreSwitch.value = false; this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); }, PartyUiHandler.FilterNonFainted); } diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index 62b115aac46..921aa129c8b 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -21,14 +21,14 @@ export class SwitchSummonPhase extends SummonPhase { private lastPokemon: Pokemon; /** - * Constructor for creating a new SwitchSummonPhase - * @param scene {@linkcode BattleScene} the scene the phase is associated with - * @param fieldIndex integer representing position on the battle field - * @param slotIndex integer for the index of pokemon (in party of 6) to switch into - * @param doReturn boolean whether to render "comeback" dialogue - * @param batonPass boolean if the switch is from baton pass - * @param player boolean if the switch is from the player - */ + * Constructor for creating a new SwitchSummonPhase + * @param scene {@linkcode BattleScene} the scene the phase is associated with + * @param fieldIndex integer representing position on the battle field + * @param slotIndex integer for the index of pokemon (in party of 6) to switch into + * @param doReturn boolean whether to render "comeback" dialogue + * @param batonPass boolean if the switch is from baton pass + * @param player boolean if the switch is from the player + */ constructor(scene: BattleScene, fieldIndex: integer, slotIndex: integer, doReturn: boolean, batonPass: boolean, player?: boolean) { super(scene, fieldIndex, player !== undefined ? player : true); @@ -99,7 +99,7 @@ export class SwitchSummonPhase extends SummonPhase { (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.transferTagsBySourceId(this.lastPokemon.id, switchedInPokemon.id)); if (!this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id)) { const batonPassModifier = this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier - && (m as SwitchEffectTransferModifier).pokemonId === this.lastPokemon.id) as SwitchEffectTransferModifier; + && (m as SwitchEffectTransferModifier).pokemonId === this.lastPokemon.id) as SwitchEffectTransferModifier; if (batonPassModifier && !this.scene.findModifier(m => m instanceof SwitchEffectTransferModifier && (m as SwitchEffectTransferModifier).pokemonId === switchedInPokemon.id)) { this.scene.tryTransferHeldItemModifier(batonPassModifier, switchedInPokemon, false); } diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index c810e325ca1..14169b19a58 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -22,6 +22,8 @@ import { SelectChallengePhase } from "./select-challenge-phase"; import { SelectStarterPhase } from "./select-starter-phase"; import { SummonPhase } from "./summon-phase"; import * as LoggerTools from "../logger"; +import { Biome } from "#app/enums/biome.js"; +import { GameDataType } from "#app/enums/game-data-type.js"; export class TitlePhase extends Phase { @@ -35,20 +37,68 @@ export class TitlePhase extends Phase { this.loaded = false; } + setBiomeByType(biome: Biome, override?: boolean): void { + if (!this.scene.menuChangesBiome && !override) + return; + this.scene.arenaBg.setTexture(`${getBiomeKey(biome)}_bg`); + } + setBiomeByName(biome: string, override?: boolean): void { + if (!this.scene.menuChangesBiome && !override) + return; + this.scene.arenaBg.setTexture(`${getBiomeKey(Utils.getEnumValues(Biome)[Utils.getEnumKeys(Biome).indexOf(biome)])}_bg`); + } + setBiomeByFile(sessionData: SessionSaveData, override?: boolean): void { + if (!this.scene.menuChangesBiome && !override) + return; + this.scene.arenaBg.setTexture(`${getBiomeKey(sessionData.arena.biome)}_bg`); + } + + confirmSlot = (message: string, slotFilter: (i: integer) => boolean, callback: (i: integer) => void) => { + const p = this; + this.scene.ui.revertMode(); + this.scene.ui.showText(message, null, () => { + const config: OptionSelectConfig = { + options: new Array(5).fill(null).map((_, i) => i).filter(slotFilter).map(i => { + var data = LoggerTools.parseSlotData(i) + return { + //label: `${i18next.t("menuUiHandler:slot", {slotNumber: i+1})}`, + label: (data ? `${i18next.t("menuUiHandler:slot", {slotNumber: i+1})}${data.description.substring(1)}` : `${i18next.t("menuUiHandler:slot", {slotNumber: i+1})}`), + handler: () => { + callback(i); + this.scene.ui.revertMode(); + this.scene.ui.showText("", 0); + return true; + } + }; + }).concat([{ + label: i18next.t("menuUiHandler:cancel"), + handler: () => { + p.callEnd() + return true + } + }]), + //xOffset: 98 + }; + this.scene.ui.setOverlayMode(Mode.MENU_OPTION_SELECT, config); + }); + }; + start(): void { super.start(); + //console.log(LoggerTools.importDocument(JSON.stringify(LoggerTools.newDocument()))) this.scene.ui.clearText(); this.scene.ui.fadeIn(250); this.scene.playBgm("title", true); + this.scene.biomeChangeMode = false + this.scene.gameData.getSession(loggedInUser?.lastSessionSlot ?? -1).then(sessionData => { if (sessionData) { this.lastSessionData = sessionData; - const biomeKey = getBiomeKey(sessionData.arena.biome); - const bgTexture = `${biomeKey}_bg`; - this.scene.arenaBg.setTexture(bgTexture); + this.setBiomeByFile(sessionData, true) + this.setBiomeByType(Biome.END) } this.showOptions(); }).catch(err => { @@ -57,20 +107,245 @@ export class TitlePhase extends Phase { }); } - showOptions(): void { + getLastSave(log?: boolean, dailyOnly?: boolean, noDaily?: boolean): SessionSaveData | undefined { + var saves: Array> = []; + for (var i = 0; i < 5; i++) { + var s = LoggerTools.parseSlotData(i); + if (s != undefined) { + if ((!noDaily && !dailyOnly) || (s.gameMode == GameModes.DAILY && dailyOnly) || (s.gameMode != GameModes.DAILY && noDaily)) { + saves.push([i, s, s.timestamp]); + } + } + } + saves.sort((a, b): integer => {return b[2] - a[2]}) + if (log) console.log(saves) + if (saves == undefined) return undefined; + if (saves[0] == undefined) return undefined; + return saves[0][1] + } + getLastSavesOfEach(log?: boolean): SessionSaveData[] | undefined { + var saves: Array> = []; + for (var i = 0; i < 5; i++) { + var s = LoggerTools.parseSlotData(i); + if (s != undefined) { + saves.push([i, s, s.timestamp]); + } + } + saves.sort((a, b): integer => {return (b[2] as number) - (a[2] as number)}) + if (log) console.log(saves) + if (saves == undefined) return undefined; + if (saves[0] == undefined) return undefined; + var validSaves: Array> = [] + var hasNormal = false; + var hasDaily = false; + for (var i = 0; i < saves.length; i++) { + if ((saves[i][1] as SessionSaveData).gameMode == GameModes.DAILY && !hasDaily) { + hasDaily = true; + validSaves.push(saves[i]) + } + if ((saves[i][1] as SessionSaveData).gameMode != GameModes.DAILY && !hasNormal) { + hasNormal = true; + validSaves.push(saves[i]) + } + } + console.log(saves, validSaves) + if (validSaves.length == 0) + return undefined; + return validSaves.map(f => f[1] as SessionSaveData); + } + getSaves(log?: boolean, dailyOnly?: boolean): SessionSaveData[] | undefined { + var saves: Array> = []; + for (var i = 0; i < 5; i++) { + var s = LoggerTools.parseSlotData(i); + if (s != undefined) { + if (!dailyOnly || s.gameMode == GameModes.DAILY) { + saves.push([i, s, s.timestamp]); + } + } + } + saves.sort((a, b): integer => {return b[2] - a[2]}) + if (log) console.log(saves) + if (saves == undefined) return undefined; + return saves.map(f => f[1]); + } + getSavesUnsorted(log?: boolean, dailyOnly?: boolean): SessionSaveData[] | undefined { + var saves: Array> = []; + for (var i = 0; i < 5; i++) { + var s = LoggerTools.parseSlotData(i); + if (s != undefined) { + if (!dailyOnly || s.gameMode == GameModes.DAILY) { + saves.push([i, s, s.timestamp]); + } + } + } + if (log) console.log(saves) + if (saves == undefined) return undefined; + return saves.map(f => f[1]); + } + + callEnd(): boolean { + this.scene.clearPhaseQueue(); + this.scene.pushPhase(new TitlePhase(this.scene)); + super.end(); + return true; + } + + showLoggerOptions(txt: string, options: OptionSelectItem[]): boolean { + this.scene.ui.showText("Export or clear game logs.", null, () => this.scene.ui.setOverlayMode(Mode.OPTION_SELECT, { options: options })); + return true; + } + + logMenu(): boolean { const options: OptionSelectItem[] = []; - if (loggedInUser && loggedInUser.lastSessionSlot > -1) { + LoggerTools.getLogs() + for (var i = 0; i < LoggerTools.logs.length; i++) { + if (localStorage.getItem(LoggerTools.logs[i][1]) != null) { + options.push(LoggerTools.generateOption(i, this.getSaves()) as OptionSelectItem) + } else { + //options.push(LoggerTools.generateAddOption(i, this.scene, this)) + } + } + options.push({ + label: "Delete all", + handler: () => { + for (var i = 0; i < LoggerTools.logs.length; i++) { + if (localStorage.getItem(LoggerTools.logs[i][1]) != null) { + localStorage.removeItem(LoggerTools.logs[i][1]) + } + } + this.scene.clearPhaseQueue(); + this.scene.pushPhase(new TitlePhase(this.scene)); + super.end(); + return true; + } + }, { + label: i18next.t("menu:cancel"), + handler: () => { + this.scene.clearPhaseQueue(); + this.scene.pushPhase(new TitlePhase(this.scene)); + super.end(); + return true; + } + }); + this.scene.ui.showText("Export or clear game logs.", null, () => this.scene.ui.setOverlayMode(Mode.OPTION_SELECT, { options: options })); + return true; + } + logRenameMenu(): boolean { + const options: OptionSelectItem[] = []; + LoggerTools.getLogs() + this.setBiomeByType(Biome.FACTORY) + for (var i = 0; i < LoggerTools.logs.length; i++) { + if (localStorage.getItem(LoggerTools.logs[i][1]) != null) { + options.push(LoggerTools.generateEditOption(this.scene, i, this.getSaves(), this) as OptionSelectItem) + } else { + //options.push(LoggerTools.generateAddOption(i, this.scene, this)) + } + } + options.push({ + label: "Delete all", + handler: () => { + for (var i = 0; i < LoggerTools.logs.length; i++) { + if (localStorage.getItem(LoggerTools.logs[i][1]) != null) { + localStorage.removeItem(LoggerTools.logs[i][1]) + } + } + this.scene.clearPhaseQueue(); + this.scene.pushPhase(new TitlePhase(this.scene)); + super.end(); + return true; + } + }, { + label: i18next.t("menu:cancel"), + handler: () => { + this.scene.clearPhaseQueue(); + this.scene.pushPhase(new TitlePhase(this.scene)); + super.end(); + return true; + } + }); + this.scene.ui.showText("Export, rename, or delete logs.", null, () => this.scene.ui.setOverlayMode(Mode.OPTION_SELECT, { options: options })); + return true; + } + + showOptions(): void { + this.scene.biomeChangeMode = true + const options: OptionSelectItem[] = []; + if (false) + if (loggedInUser && loggedInUser!.lastSessionSlot > -1) { options.push({ label: i18next.t("continue", {ns: "menu"}), handler: () => { - this.loadSaveSlot(this.lastSessionData || !loggedInUser ? -1 : loggedInUser.lastSessionSlot); + this.loadSaveSlot(this.lastSessionData ? -1 : loggedInUser!.lastSessionSlot); return true; } }); } + // Replaces 'Continue' with all Daily Run saves, sorted by when they last saved + // If there are no daily runs, it instead shows the most recently saved run + // If this fails too, there are no saves, and the option does not appear + var lastsaves = this.getSaves(false, true); // Gets all Daily Runs sorted by last play time + var lastsave = this.getLastSave(); // Gets the last save you played + var ls1 = this.getLastSave(false, true) + var ls2 = this.getLastSavesOfEach() + this.scene.quickloadDisplayMode = "Both" + switch (true) { + case (this.scene.quickloadDisplayMode == "Daily" && ls1 != undefined): + options.push({ + label: (ls1.description ? ls1.description : "[???]"), + handler: () => { + this.loadSaveSlot(ls1!.slot); + return true; + } + }) + break; + case this.scene.quickloadDisplayMode == "Dailies" && lastsaves != undefined && ls1 != undefined: + lastsaves.forEach(lastsave1 => { + options.push({ + label: (lastsave1.description ? lastsave1.description : "[???]"), + handler: () => { + this.loadSaveSlot(lastsave1.slot); + return true; + } + }) + }) + break; + case lastsave != undefined && (this.scene.quickloadDisplayMode == "Latest" || ((this.scene.quickloadDisplayMode == "Daily" || this.scene.quickloadDisplayMode == "Dailies") && ls1 == undefined)): + options.push({ + label: (lastsave.description ? lastsave.description : "[???]"), + handler: () => { + this.loadSaveSlot(lastsave!.slot); + return true; + } + }) + break; + case this.scene.quickloadDisplayMode == "Both" && ls2 != undefined: + ls2.forEach(lastsave2 => { + options.push({ + label: (lastsave2.description ? lastsave2.description : "[???]"), + handler: () => { + this.loadSaveSlot(lastsave2.slot); + return true; + } + }) + }) + break; + default: // If set to "Off" or all above conditions failed + if (loggedInUser && loggedInUser.lastSessionSlot > -1) { + options.push({ + label: i18next.t("continue", { ns: "menu"}), + handler: () => { + this.loadSaveSlot(this.lastSessionData ? -1 : loggedInUser!.lastSessionSlot); + return true; + } + }); + } + break; + } options.push({ label: i18next.t("menu:newGame"), handler: () => { + this.scene.biomeChangeMode = false + this.setBiomeByType(Biome.TOWN) const setModeAndEnd = (gameMode: GameModes) => { this.gameMode = gameMode; this.scene.ui.setMode(Mode.MESSAGE); @@ -110,6 +385,14 @@ export class TitlePhase extends Phase { } }); } + options.push({ + label: i18next.t("menuUiHandler:importSession"), + handler: () => { + this.confirmSlot(i18next.t("menuUiHandler:importSlotSelect"), () => true, slotId => this.scene.gameData.importData(GameDataType.SESSION, slotId)); + return true; + }, + keepOpen: true + }) options.push({ label: i18next.t("menu:cancel"), handler: () => { @@ -121,38 +404,88 @@ export class TitlePhase extends Phase { }); this.scene.ui.showText(i18next.t("menu:selectGameMode"), null, () => this.scene.ui.setOverlayMode(Mode.OPTION_SELECT, { options: options })); } else { - this.gameMode = GameModes.CLASSIC; - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.clearText(); - this.end(); + const options: OptionSelectItem[] = [ + { + label: GameMode.getModeName(GameModes.CLASSIC), + handler: () => { + setModeAndEnd(GameModes.CLASSIC); + return true; + } + } + ]; + options.push({ + label: i18next.t("menuUiHandler:importSession"), + handler: () => { + this.confirmSlot(i18next.t("menuUiHandler:importSlotSelect"), () => true, slotId => this.scene.gameData.importData(GameDataType.SESSION, slotId)); + return true; + }, + keepOpen: true + }) + options.push({ + label: i18next.t("menu:cancel"), + handler: () => { + this.scene.clearPhaseQueue(); + this.scene.pushPhase(new TitlePhase(this.scene)); + super.end(); + return true; + } + }); + this.scene.ui.showText(i18next.t("menu:selectGameMode"), null, () => this.scene.ui.setOverlayMode(Mode.OPTION_SELECT, { options: options })); } return true; } - }, - { - label: i18next.t("menu:loadGame"), + }, { + label: "Manage Logs", handler: () => { - this.scene.ui.setOverlayMode(Mode.SAVE_SLOT, SaveSlotUiMode.LOAD, - (slotId: integer) => { - if (slotId === -1) { + this.scene.biomeChangeMode = false + //return this.logRenameMenu() + this.scene.ui.setOverlayMode(Mode.LOG_HANDLER, + (k: string) => { + if (k === undefined) { return this.showOptions(); } - this.loadSaveSlot(slotId); + console.log(k) + this.showOptions(); + }, () => { + this.showOptions(); }); return true; } - }, - { - label: i18next.t("menu:dailyRun"), + }, { + label: "Manage Logs (Old Menu)", handler: () => { - this.initDailyRun(); + return this.logRenameMenu() + } + }) + options.push({ + label: i18next.t("menu:loadGame"), + handler: () => { + this.scene.biomeChangeMode = false + this.scene.ui.setOverlayMode(Mode.SAVE_SLOT, SaveSlotUiMode.LOAD, + (slotId: integer, autoSlot: integer) => { + if (slotId === -1) { + return this.showOptions(); + } + this.loadSaveSlot(slotId, autoSlot); + }); return true; - }, - keepOpen: true - }, - { + } + }) + if (false) { + options.push({ + label: i18next.t("menu:dailyRun"), + handler: () => { + this.scene.biomeChangeMode = false + this.setupDaily(); + return true; + }, + keepOpen: true + }) + } + options.push({ label: i18next.t("menu:settings"), handler: () => { + this.scene.biomeChangeMode = false this.scene.ui.setOverlayMode(Mode.SETTINGS); return true; }, @@ -166,11 +499,11 @@ export class TitlePhase extends Phase { this.scene.ui.setMode(Mode.TITLE, config); } - loadSaveSlot(slotId: integer): void { + loadSaveSlot(slotId: integer, autoSlot?: integer): void { this.scene.sessionSlotId = slotId > -1 || !loggedInUser ? slotId : loggedInUser.lastSessionSlot; this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.resetModeChain(); - this.scene.gameData.loadSession(this.scene, slotId, slotId === -1 ? this.lastSessionData : undefined).then((success: boolean) => { + this.scene.gameData.loadSession(this.scene, slotId, slotId === -1 ? this.lastSessionData : undefined, autoSlot).then((success: boolean) => { if (success) { this.loaded = true; this.scene.ui.showText(i18next.t("menu:sessionSuccess"), null, () => this.end()); @@ -218,7 +551,6 @@ export class TitlePhase extends Phase { } regenerateModifierPoolThresholds(party, ModifierPoolType.DAILY_STARTER); - const modifiers: Modifier[] = Array(3).fill(null).map(() => modifierTypes.EXP_SHARE().withIdFromFunc(modifierTypes.EXP_SHARE).newModifier()) .concat(Array(3).fill(null).map(() => modifierTypes.GOLDEN_EXP_CHARM().withIdFromFunc(modifierTypes.GOLDEN_EXP_CHARM).newModifier())) .concat(getDailyRunStarterModifiers(party)) @@ -257,8 +589,52 @@ export class TitlePhase extends Phase { } }); } - + setupDaily(): void { + // TODO + var saves = this.getSaves() + var saveNames = new Array(5).fill("") + for (var i = 0; i < saves!.length; i++) { + saveNames[saves![i][0]] = saves![i][1].description + } + const ui = this.scene.ui + const confirmSlot = (message: string, slotFilter: (i: integer) => boolean, callback: (i: integer) => void) => { + ui.revertMode(); + ui.showText(message, null, () => { + const config: OptionSelectConfig = { + options: new Array(5).fill(null).map((_, i) => i).filter(slotFilter).map(i => { + return { + label: (i+1) + " " + saveNames[i], + handler: () => { + callback(i); + ui.revertMode(); + ui.showText("", 0); + return true; + } + }; + }).concat([{ + label: i18next.t("menuUiHandler:cancel"), + handler: () => { + ui.revertMode(); + ui.showText("", 0); + return true; + } + }]), + xOffset: 98 + }; + ui.setOverlayMode(Mode.MENU_OPTION_SELECT, config); + }); + }; + ui.showText("This feature is incomplete.", null, () => { + this.scene.clearPhaseQueue(); + this.scene.pushPhase(new TitlePhase(this.scene)); + super.end(); + return true; + }) + return; + confirmSlot("Select a slot to replace.", () => true, slotId => this.scene.gameData.importData(GameDataType.SESSION, slotId)); + } end(): void { + this.scene.biomeChangeMode = false if (!this.loaded && !this.scene.gameMode.isDaily) { this.scene.arena.preloadBgm(); this.scene.gameMode = getGameMode(this.gameMode); diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index dde7b3308f7..0ab1e12272e 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -23,6 +23,8 @@ export class TurnEndPhase extends FieldPhase { start() { super.start(); + this.scene.arenaFlyout.updateFieldText() + this.scene.currentBattle.incrementTurn(this.scene); this.scene.eventTarget.dispatchEvent(new TurnEndEvent(this.scene.currentBattle.turn)); diff --git a/src/phases/turn-init-phase.ts b/src/phases/turn-init-phase.ts index dd2799b66d3..1a8a82bded7 100644 --- a/src/phases/turn-init-phase.ts +++ b/src/phases/turn-init-phase.ts @@ -1,7 +1,7 @@ import BattleScene from "#app/battle-scene.js"; import { BattlerIndex } from "#app/battle.js"; import { TurnInitEvent } from "#app/events/battle-scene.js"; -import { PlayerPokemon } from "#app/field/pokemon.js"; +import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon.js"; import i18next from "i18next"; import { FieldPhase } from "./field-phase"; import { ToggleDoublePositionPhase } from "./toggle-double-position-phase"; @@ -19,6 +19,9 @@ export class TurnInitPhase extends FieldPhase { start() { super.start(); + // If the flyout was shown automatically, and the user hasn't made it go away, auto-hide it + this.scene.arenaFlyout.dismiss() + this.scene.getPlayerField().forEach(p => { // If this pokemon is in play and evolved into something illegal under the current challenge, force a switch if (p.isOnField() && !p.isAllowedInBattle()) { @@ -47,20 +50,86 @@ export class TurnInitPhase extends FieldPhase { //this.scene.pushPhase(new MoveAnimTestPhase(this.scene)); this.scene.eventTarget.dispatchEvent(new TurnInitEvent()); - this.scene.getField().forEach((pokemon, i) => { - if (pokemon?.isActive()) { - if (pokemon.isPlayer()) { - this.scene.currentBattle.addParticipant(pokemon as PlayerPokemon); + LoggerTools.enemyPlan[0] = "" + LoggerTools.enemyPlan[1] = "" + LoggerTools.enemyPlan[2] = "" + LoggerTools.enemyPlan[3] = "" + + if (false) { + this.scene.getField().forEach((pokemon, i) => { + if (pokemon != undefined && pokemon != null) + console.log("Handle " + pokemon.name) + if (pokemon?.isActive()) { + if (pokemon.isPlayer()) { + this.scene.currentBattle.addParticipant(pokemon as PlayerPokemon); + } else { + console.log("Marked " + pokemon.name + " as used") + pokemon.usedInBattle = true; + pokemon.flyout.setText() + pokemon.getBattleInfo().iconsActive = true + } + pokemon.resetTurnData(); + this.scene.pushPhase(pokemon.isPlayer() ? new CommandPhase(this.scene, i) : new EnemyCommandPhase(this.scene, i - BattlerIndex.ENEMY)); } + }); + } else { + this.scene.getField().forEach((pokemon, i) => { + if (pokemon?.isActive()) { + if (!pokemon.isPlayer()) { + pokemon.flyout.setText() + pokemon.usedInBattle = true; + pokemon.getBattleInfo().iconsActive = true + pokemon.resetTurnData(); + this.scene.pushPhase(pokemon.isPlayer() ? new CommandPhase(this.scene, i) : new EnemyCommandPhase(this.scene, i - BattlerIndex.ENEMY)); + } + } + }); + this.scene.getField().forEach((pokemon, i) => { + if (pokemon?.isActive()) { + if (pokemon.isPlayer()) { + this.scene.currentBattle.addParticipant(pokemon as PlayerPokemon); + pokemon.resetTurnData(); + this.scene.pushPhase(pokemon.isPlayer() ? new CommandPhase(this.scene, i) : new EnemyCommandPhase(this.scene, i - BattlerIndex.ENEMY)); + } + } + }); + } - pokemon.resetTurnData(); - - this.scene.pushPhase(pokemon.isPlayer() ? new CommandPhase(this.scene, i) : new EnemyCommandPhase(this.scene, i - BattlerIndex.ENEMY)); + var Pt = this.scene.getEnemyParty() + var Pt1: EnemyPokemon[] = [] + var Pt2: EnemyPokemon[] = [] + for (var i = 0; i < Pt.length; i++) { + if (i % 2 == 0) { + Pt1.push(Pt[i]) + } else { + Pt2.push(Pt[i]) } - }); + } + Pt.forEach((pokemon, i) => { + if (pokemon != undefined && pokemon.hp > 0 && pokemon.isActive()) + if (pokemon.hasTrainer() || true) { + console.log(i) + if (pokemon.getFieldIndex() == 1 && pokemon.isOnField()) { + // Switch this to cycle between + // - hiding the top mon's team bar + // - showing the bottom mon's team bar with its active slots reversed + if (false) { + pokemon.getBattleInfo().displayParty(Pt) + Pt[0].getBattleInfo().switchIconVisibility(false); // Make the top mon's team bar go away + Pt[0].getBattleInfo().iconsActive = false; // Prevent the top mon from re-opening its bar + } else { + pokemon.getBattleInfo().displayParty(Pt2) + } + } else { + pokemon.getBattleInfo().displayParty((this.scene.currentBattle.double ? Pt1 : Pt)) + } + } + }) this.scene.pushPhase(new TurnStartPhase(this.scene)); + this.scene.updateCatchRate() + this.end(); } } diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index ff4b7050678..ffc2e75c19b 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -3,7 +3,7 @@ import { applyAbAttrs, BypassSpeedChanceAbAttr, PreventBypassSpeedChanceAbAttr, import { allMoves, applyMoveAttrs, IncrementMovePriorityAttr, MoveHeaderAttr } from "#app/data/move.js"; import { Abilities } from "#app/enums/abilities.js"; import { Stat } from "#app/enums/stat.js"; -import { PokemonMove } from "#app/field/pokemon.js"; +import Pokemon, { PokemonMove } from "#app/field/pokemon.js"; import { BypassSpeedChanceModifier } from "#app/modifier/modifier.js"; import { Command } from "#app/ui/command-ui-handler.js"; import * as Utils from "#app/utils.js"; @@ -18,12 +18,41 @@ import { SwitchSummonPhase } from "./switch-summon-phase"; import { TurnEndPhase } from "./turn-end-phase"; import { WeatherEffectPhase } from "./weather-effect-phase"; import * as LoggerTools from "../logger"; +import { BattlerIndex } from "#app/battle.js"; export class TurnStartPhase extends FieldPhase { constructor(scene: BattleScene) { super(scene); } + generateTargString(t: BattlerIndex[]) { + var targets = ['Self'] + for (var i = 0; i < this.scene.getField().length; i++) { + if (this.scene.getField()[i] != null) + targets[this.scene.getField()[i].getBattlerIndex() + 1] = this.scene.getField()[i].name + } + for (var i = 0; i < this.scene.getEnemyField().length; i++) { + if (this.scene.getEnemyField()[i] != null) + targets[this.scene.getEnemyField()[i].getBattlerIndex() + 1] = this.scene.getEnemyField()[i].name + } + var targetFull: string[] = [] + for (var i = 0; i < t.length; i++) { + targetFull.push(targets[t[i] + 1]) + } + if (targetFull.join(", ") == targets.join(", ")) return "" + return " → " + targetFull.join(", ") + } + + getBattlers(user: Pokemon): Pokemon[] { + var battlers: Pokemon[] = [] + battlers[0] = this.scene.getField()[0] + battlers[1] = this.scene.getField()[1] + battlers[2] = this.scene.getEnemyField()[0] + battlers[3] = this.scene.getEnemyField()[1] + battlers.unshift(user) + return battlers; + } + start() { super.start(); @@ -32,6 +61,54 @@ export class TurnStartPhase extends FieldPhase { const battlerBypassSpeed = {}; + const playerActions: string[] = [] + + const moveOrder = order.slice(0); + + while (LoggerTools.Actions.length > 0) { + LoggerTools.Actions.pop() + } + + for (const o of moveOrder) { + + const pokemon = field[o]; + const turnCommand = this.scene.currentBattle.turnCommands[o]; + + if (turnCommand?.skip || !pokemon.isPlayer()) { + continue; + } + + switch (turnCommand?.command) { + case Command.FIGHT: + const queuedMove = turnCommand.move; + if (!queuedMove) { + continue; + } + LoggerTools.Actions[pokemon.getBattlerIndex()] = `[[ ${new PokemonMove(queuedMove.move).getName()} unknown target ]]` + break; + case Command.BALL: + var ballNames = [ + "Poké Ball", + "Great Ball", + "Ultra Ball", + "Rogue Ball", + "Master Ball", + "Luxury Ball" + ] + LoggerTools.Actions[pokemon.getBattlerIndex()] = ballNames[turnCommand.cursor!] + playerActions.push(ballNames[turnCommand.cursor!]) + //this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targets[0] % 2, turnCommand.cursor)); + break; + case Command.POKEMON: + break; + case Command.RUN: + LoggerTools.Actions[pokemon.getBattlerIndex()] = "Run" + playerActions.push("Run") + break; + } + } + //LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, playerActions.join(" | ")) + this.scene.getField(true).filter(p => p.summonData).map(p => { const bypassSpeed = new Utils.BooleanHolder(false); const canCheckHeldItems = new Utils.BooleanHolder(true); @@ -43,8 +120,6 @@ export class TurnStartPhase extends FieldPhase { battlerBypassSpeed[p.getBattlerIndex()] = bypassSpeed; }); - const moveOrder = order.slice(0); - moveOrder.sort((a, b) => { const aCommand = this.scene.currentBattle.turnCommands[a]; const bCommand = this.scene.currentBattle.turnCommands[b]; @@ -112,19 +187,81 @@ export class TurnStartPhase extends FieldPhase { } if (pokemon.isPlayer()) { if (turnCommand.cursor === -1) { + //console.log("turncommand cursor was -1 -- running TOP block") this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move));//TODO: is the bang correct here? + var targets = turnCommand.targets || turnCommand.move!.targets + var mv = move + if (pokemon.isPlayer()) { + console.log(turnCommand.targets, turnCommand.move!.targets) + LoggerTools.Actions[pokemon.getBattlerIndex()] = mv.getName() + if (this.scene.currentBattle.double) { + var targIDs = ["Self", "Self", "Ally", "L", "R"] + if (pokemon.getBattlerIndex() == 1) targIDs = ["Self", "Ally", "Self", "L", "R"] + LoggerTools.Actions[pokemon.getBattlerIndex()] += " → " + targets.map(v => targIDs[v+1]) + } else { + var targIDs = ["Self", "", "", "", ""] + var myField = this.scene.getField() + if (myField[0]) + targIDs[1] = myField[0].name + if (myField[1]) + targIDs[2] = myField[1].name + var eField = this.scene.getEnemyField() + if (eField[0]) + targIDs[3] = eField[0].name + if (eField[1]) + targIDs[4] = eField[1].name + //LoggerTools.Actions[pokemon.getBattlerIndex()] += " → " + targets.map(v => targIDs[v+1]) + } + console.log(mv.getName(), targets) + } } else { + //console.log("turncommand = ", turnCommand, " -- running BOTTOM block") const playerPhase = new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move, false, queuedMove.ignorePP);//TODO: is the bang correct here? + var targets = turnCommand.targets || turnCommand.move!.targets + var mv = move + if (pokemon.isPlayer()) { + console.log(turnCommand.targets, turnCommand.move!.targets) + if (turnCommand.args && turnCommand.args[1] && turnCommand.args[1].isContinuing != undefined) { + console.log(mv.getName(), targets) + } else { + LoggerTools.Actions[pokemon.getBattlerIndex()] = mv.getName() + if (this.scene.currentBattle.double) { + var targIDs = ["Self", "Self", "Ally", "L", "R"] + if (pokemon.getBattlerIndex() == 1) targIDs = ["Self", "Ally", "Self", "L", "R"] + LoggerTools.Actions[pokemon.getBattlerIndex()] += " → " + targets.map(v => targIDs[v+1]) + } else { + var targIDs = ["Self", "", "", "", ""] + var myField = this.scene.getField() + if (myField[0]) + targIDs[1] = myField[0].name + if (myField[1]) + targIDs[2] = myField[1].name + var eField = this.scene.getEnemyField() + if (eField[0]) + targIDs[3] = eField[0].name + if (eField[1]) + targIDs[4] = eField[1].name + //LoggerTools.Actions[pokemon.getBattlerIndex()] += " → " + targets.map(v => targIDs[v+1]) + } + console.log(mv.getName(), targets) + } + } this.scene.pushPhase(playerPhase); } } else { this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move, false, queuedMove.ignorePP));//TODO: is the bang correct here? + var targets = turnCommand.targets || turnCommand.move!.targets + var mv = new PokemonMove(queuedMove.move) } break; case Command.BALL: this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targets![0] % 2, turnCommand.cursor!));//TODO: is the bang correct here? break; case Command.POKEMON: + if (pokemon.isPlayer()) { + // " " + LoggerTools.playerPokeName(this.scene, pokemon) + + LoggerTools.Actions[pokemon.getBattlerIndex()] = ((turnCommand.args![0] as boolean) ? "Baton" : "Switch") + " to " + LoggerTools.playerPokeName(this.scene, turnCommand.cursor!) + } this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, pokemon.getFieldIndex(), turnCommand.cursor!, true, turnCommand.args![0] as boolean, pokemon.isPlayer()));//TODO: is the bang correct here? break; case Command.RUN: @@ -137,7 +274,7 @@ export class TurnStartPhase extends FieldPhase { return; } }); - // if only one pokemon is alive, use that one + // if only one pokemon is alive, use that one if (playerActivePokemon.length > 1) { // find which active pokemon has faster speed const fasterPokemon = playerActivePokemon[0].getStat(Stat.SPD) > playerActivePokemon[1].getStat(Stat.SPD) ? playerActivePokemon[0] : playerActivePokemon[1]; @@ -163,11 +300,20 @@ export class TurnStartPhase extends FieldPhase { this.scene.pushPhase(new BerryPhase(this.scene)); this.scene.pushPhase(new TurnEndPhase(this.scene)); + this.scene.arenaFlyout.updateFieldText() + + if (LoggerTools.Actions.length > 1 && !this.scene.currentBattle.double) { + LoggerTools.Actions.pop() // If this is a single battle, but we somehow have two actions, delete the second + } + if (LoggerTools.Actions.length > 1 && (LoggerTools.Actions[0] == "" || LoggerTools.Actions[0] == undefined || LoggerTools.Actions[0] == null)) + LoggerTools.Actions.shift() // If the left slot isn't doing anything, delete its entry + LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, LoggerTools.Actions.join(" & ")) + /** - * this.end() will call shiftPhase(), which dumps everything from PrependQueue (aka everything that is unshifted()) to the front - * of the queue and dequeues to start the next phase - * this is important since stuff like SwitchSummon, AttemptRun, AttemptCapture Phases break the "flow" and should take precedence - */ + * this.end() will call shiftPhase(), which dumps everything from PrependQueue (aka everything that is unshifted()) to the front + * of the queue and dequeues to start the next phase + * this is important since stuff like SwitchSummon, AttemptRun, AttemptCapture Phases break the "flow" and should take precedence + */ this.end(); } } diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index f0c5ba11995..ef41ea7fe75 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -118,11 +118,13 @@ export class VictoryPhase extends PokemonPhase { if (this.scene.currentBattle.waveIndex % 10) { this.scene.pushPhase(new SelectModifierPhase(this.scene)); } else if (this.scene.gameMode.isDaily) { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "") this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_CHARM)); if (this.scene.currentBattle.waveIndex > 10 && !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) { this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_POKEBALL)); } } else { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "") const superExpWave = !this.scene.gameMode.isEndless ? (this.scene.offsetGym ? 0 : 20) : 10; if (this.scene.gameMode.isEndless && this.scene.currentBattle.waveIndex === 10) { this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_SHARE)); @@ -140,6 +142,7 @@ export class VictoryPhase extends PokemonPhase { } this.scene.pushPhase(new NewBattlePhase(this.scene)); } else { + LoggerTools.logShop(this.scene, this.scene.currentBattle.waveIndex, "") this.scene.currentBattle.battleType = BattleType.CLEAR; this.scene.score += this.scene.gameMode.getClearScoreBonus(); this.scene.updateScoreText();