From 39b8dc9a85de11ad7441bb1fd9020cdc9b02fe8d Mon Sep 17 00:00:00 2001 From: Wlowscha <54003515+Wlowscha@users.noreply.github.com> Date: Thu, 5 Jun 2025 23:54:50 +0200 Subject: [PATCH 01/23] [Refactor] Removing unused logic for mbh/grip claw (#5914) * Removing unused logic for mbh/grip claw * Updated docstring --------- Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> --- src/modifier/modifier.ts | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 11631b64451..2bebbcae90c 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -40,7 +40,6 @@ import { type TerastallizeModifierType, type TmModifierType, getModifierType, - ModifierPoolType, ModifierTypeGenerator, modifierTypes, PokemonHeldItemModifierType, @@ -3232,8 +3231,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier { } /** - * Steals an item from a set of target Pokemon. - * This prioritizes high-tier held items when selecting the item to steal. + * Steals an item, chosen randomly, from a set of target Pokemon. * @param pokemon The {@linkcode Pokemon} holding this item * @param target The {@linkcode Pokemon} to steal from (optional) * @param _args N/A @@ -3253,30 +3251,15 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier { return false; } - const poolType = pokemon.isPlayer() - ? ModifierPoolType.PLAYER - : pokemon.hasTrainer() - ? ModifierPoolType.TRAINER - : ModifierPoolType.WILD; - const transferredModifierTypes: ModifierType[] = []; const itemModifiers = globalScene.findModifiers( m => m instanceof PokemonHeldItemModifier && m.pokemonId === targetPokemon.id && m.isTransferable, targetPokemon.isPlayer(), ) as PokemonHeldItemModifier[]; - let highestItemTier = itemModifiers - .map(m => m.type.getOrInferTier(poolType)) - .reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is this bang correct? - let tierItemModifiers = itemModifiers.filter(m => m.type.getOrInferTier(poolType) === highestItemTier); for (let i = 0; i < transferredItemCount; i++) { - if (!tierItemModifiers.length) { - while (highestItemTier-- && !tierItemModifiers.length) { - tierItemModifiers = itemModifiers.filter(m => m.type.tier === highestItemTier); - } - if (!tierItemModifiers.length) { - break; - } + if (!itemModifiers.length) { + break; } const randItemIndex = pokemon.randBattleSeedInt(itemModifiers.length); const randItem = itemModifiers[randItemIndex]; From 193c5ffb0ca9b0669d687170ab175c6d4c617b56 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Thu, 5 Jun 2025 17:18:07 -0500 Subject: [PATCH 02/23] [UI/UX] Move Reload Required text to bottom of settings (#5928) * Move Reload Required text to bottom of settings * Remove unneeded reloadRequiredText field --- src/ui/settings/abstract-settings-ui-handler.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ui/settings/abstract-settings-ui-handler.ts b/src/ui/settings/abstract-settings-ui-handler.ts index 27ca95c25ac..1fb4b6d34dc 100644 --- a/src/ui/settings/abstract-settings-ui-handler.ts +++ b/src/ui/settings/abstract-settings-ui-handler.ts @@ -108,10 +108,12 @@ export default class AbstractSettingsUiHandler extends MessageUiHandler { this.reloadSettings = this.settings.filter(s => s?.requireReload); + let anyReloadRequired = false; this.settings.forEach((setting, s) => { let settingName = setting.label; if (setting?.requireReload) { - settingName += ` (${i18next.t("settings:requireReload")})`; + settingName += "*"; + anyReloadRequired = true; } this.settingLabels[s] = addTextObject(8, 28 + s * 16, settingName, TextStyle.SETTINGS_LABEL); @@ -187,6 +189,14 @@ export default class AbstractSettingsUiHandler extends MessageUiHandler { this.settingsContainer.add(iconAction); this.settingsContainer.add(iconCancel); this.settingsContainer.add(actionText); + // Only add the ReloadRequired text on pages that have settings that require a reload. + if (anyReloadRequired) { + const reloadRequired = addTextObject(0, 0, `*${i18next.t("settings:requireReload")}`, TextStyle.SETTINGS_LABEL) + .setOrigin(0, 0.15) + .setPositionRelative(actionsBg, 6, 0) + .setY(actionText.y); + this.settingsContainer.add(reloadRequired); + } this.settingsContainer.add(cancelText); this.settingsContainer.add(this.messageBoxContainer); From 0c54fc1be064a815b5bf6609208e4b2e4801d56e Mon Sep 17 00:00:00 2001 From: PrabbyDD <147005742+PrabbyDD@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:22:27 -0700 Subject: [PATCH 03/23] [Bug] Fix no EXP awarded if all active pokemon faint (#4688) Party pokemon will now gain EXP from EXP Share even if all the active pokemon are fainted --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/phases/faint-phase.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index bf0adf77061..cd40026e55f 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -78,10 +78,15 @@ export class FaintPhase extends PokemonPhase { } } - /** In case the current pokemon was just switched in, make sure it is counted as participating in the combat */ + /** + * In case the current pokemon was just switched in, make sure it is counted as participating in the combat. + * For EXP_SHARE purposes, if the current pokemon faints as the combat ends and it was the ONLY player pokemon + * involved in combat, it needs to be counted as a participant so the other party pokemon can get their EXP, + * so the fainted pokemon has been included. + */ for (const pokemon of globalScene.getPlayerField()) { - if (pokemon?.isActive(true) && pokemon.isPlayer()) { - globalScene.currentBattle.addParticipant(pokemon as PlayerPokemon); + if (pokemon?.isActive() || pokemon?.isFainted()) { + globalScene.currentBattle.addParticipant(pokemon); } } From e82e18250761a2d33af09c3af06487812c830378 Mon Sep 17 00:00:00 2001 From: Lugiad <2070109+Adri1@users.noreply.github.com> Date: Fri, 6 Jun 2025 18:33:13 +0200 Subject: [PATCH 04/23] [UI/UX] [Localization] Russian Gatcha Text Resize (#5929) Russian Gatch Text Resize Co-authored-by: Wlowscha <54003515+Wlowscha@users.noreply.github.com> --- src/ui/egg-gacha-ui-handler.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index 1bb7124d935..a9a978548a8 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -108,7 +108,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { let pokemonIconX = -20; let pokemonIconY = 6; - if (["de", "es-ES", "es-MX", "fr", "ko", "pt-BR"].includes(currentLanguage)) { + if (["de", "es-ES", "es-MX", "fr", "ko", "pt-BR", "ru"].includes(currentLanguage)) { gachaTextStyle = TextStyle.SMALLER_WINDOW_ALT; gachaX = 2; gachaY = 2; @@ -150,7 +150,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { gachaInfoContainer.add(pokemonIcon); break; case GachaType.MOVE: - if (["de", "es-ES", "fr", "pt-BR"].includes(currentLanguage)) { + if (["de", "es-ES", "fr", "pt-BR", "ru"].includes(currentLanguage)) { gachaUpLabel.setAlign("center"); gachaUpLabel.setY(0); } @@ -160,7 +160,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { gachaUpLabel.setOrigin(0.5, 0); break; case GachaType.SHINY: - if (["de", "fr", "ko"].includes(currentLanguage)) { + if (["de", "fr", "ko", "ru"].includes(currentLanguage)) { gachaUpLabel.setAlign("center"); gachaUpLabel.setY(0); } From 178de207f1885769e2c9a8440343d5ea98fb1d61 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Fri, 6 Jun 2025 14:34:38 -0500 Subject: [PATCH 05/23] [Refactor] Cleanup egg list ui handler (#5890) Cleanup egg list ui handler Co-authored-by: Wlowscha <54003515+Wlowscha@users.noreply.github.com> --- src/ui/egg-list-ui-handler.ts | 93 +++++++++++++++-------------------- 1 file changed, 39 insertions(+), 54 deletions(-) diff --git a/src/ui/egg-list-ui-handler.ts b/src/ui/egg-list-ui-handler.ts index 9f41feea8ab..9a1b1f51e25 100644 --- a/src/ui/egg-list-ui-handler.ts +++ b/src/ui/egg-list-ui-handler.ts @@ -35,81 +35,70 @@ export default class EggListUiHandler extends MessageUiHandler { setup() { const ui = this.getUi(); - this.eggListContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6); - this.eggListContainer.setVisible(false); + this.eggListContainer = globalScene.add.container(0, -globalScene.game.canvas.height / 6).setVisible(false); ui.add(this.eggListContainer); - const bgColor = globalScene.add.rectangle( - 0, - 0, - globalScene.game.canvas.width / 6, - globalScene.game.canvas.height / 6, - 0x006860, - ); - bgColor.setOrigin(0, 0); - this.eggListContainer.add(bgColor); + const bgColor = globalScene.add + .rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6, 0x006860) + .setOrigin(0); - const eggListBg = globalScene.add.image(0, 0, "egg_list_bg"); - eggListBg.setOrigin(0, 0); - this.eggListContainer.add(eggListBg); - - this.eggListContainer.add(addWindow(1, 85, 106, 22)); - this.eggListContainer.add(addWindow(1, 102, 106, 50, true)); - this.eggListContainer.add(addWindow(1, 147, 106, 32, true)); - this.eggListContainer.add(addWindow(107, 1, 212, 178)); + const eggListBg = globalScene.add.image(0, 0, "egg_list_bg").setOrigin(0); this.iconAnimHandler = new PokemonIconAnimHandler(); this.iconAnimHandler.setup(); - this.eggNameText = addTextObject(8, 68, "", TextStyle.SUMMARY); - this.eggNameText.setOrigin(0, 0); - this.eggListContainer.add(this.eggNameText); + this.eggNameText = addTextObject(8, 68, "", TextStyle.SUMMARY).setOrigin(0); this.eggDateText = addTextObject(8, 91, "", TextStyle.TOOLTIP_CONTENT); - this.eggListContainer.add(this.eggDateText); - this.eggHatchWavesText = addTextObject(8, 108, "", TextStyle.TOOLTIP_CONTENT); - this.eggHatchWavesText.setWordWrapWidth(540); - this.eggListContainer.add(this.eggHatchWavesText); + this.eggHatchWavesText = addTextObject(8, 108, "", TextStyle.TOOLTIP_CONTENT).setWordWrapWidth(540); - this.eggGachaInfoText = addTextObject(8, 152, "", TextStyle.TOOLTIP_CONTENT); - this.eggGachaInfoText.setWordWrapWidth(540); - this.eggListContainer.add(this.eggGachaInfoText); + this.eggGachaInfoText = addTextObject(8, 152, "", TextStyle.TOOLTIP_CONTENT).setWordWrapWidth(540); this.eggListIconContainer = globalScene.add.container(113, 5); - this.eggListContainer.add(this.eggListIconContainer); - this.cursorObj = globalScene.add.image(0, 0, "select_cursor"); - this.cursorObj.setOrigin(0, 0); - this.eggListContainer.add(this.cursorObj); + this.cursorObj = globalScene.add.image(0, 0, "select_cursor").setOrigin(0); this.eggSprite = globalScene.add.sprite(54, 37, "egg"); - this.eggListContainer.add(this.eggSprite); const scrollBar = new ScrollBar(310, 5, 4, 170, this.ROWS); - this.eggListContainer.add(scrollBar); this.scrollGridHandler = new ScrollableGridUiHandler(this, this.ROWS, this.COLUMNS) .withScrollBar(scrollBar) .withUpdateGridCallBack(() => this.updateEggIcons()) .withUpdateSingleElementCallback((i: number) => this.setEggDetails(i)); - this.eggListMessageBoxContainer = globalScene.add.container(0, globalScene.game.canvas.height / 6); - this.eggListMessageBoxContainer.setVisible(false); - this.eggListContainer.add(this.eggListMessageBoxContainer); + this.eggListMessageBoxContainer = globalScene.add + .container(0, globalScene.game.canvas.height / 6) + .setVisible(false); - const eggListMessageBox = addWindow(1, -1, 318, 28); - eggListMessageBox.setOrigin(0, 1); + const eggListMessageBox = addWindow(1, -1, 318, 28).setOrigin(0, 1); this.eggListMessageBoxContainer.add(eggListMessageBox); - this.message = addTextObject(8, -8, "", TextStyle.WINDOW, { maxLines: 1 }); - this.message.setOrigin(0, 1); - this.eggListMessageBoxContainer.add(this.message); + // Message isn't used, but is expected to exist as this subclasses MessageUiHandler + this.message = addTextObject(8, -8, "", TextStyle.WINDOW, { maxLines: 1 }).setActive(false).setVisible(false); this.cursor = -1; + + this.eggListContainer.add([ + bgColor, + eggListBg, + addWindow(1, 85, 106, 22), + addWindow(1, 102, 106, 50, true), + addWindow(1, 147, 106, 32, true), + addWindow(107, 1, 212, 178), + this.eggNameText, + this.eggDateText, + this.eggHatchWavesText, + this.eggGachaInfoText, + this.eggListIconContainer, + this.cursorObj, + this.eggSprite, + scrollBar, + ]); } - show(args: any[]): boolean { + override show(args: any[]): boolean { super.show(args); this.initEggIcons(); @@ -134,9 +123,10 @@ export default class EggListUiHandler extends MessageUiHandler { for (let i = 0; i < Math.min(this.ROWS * this.COLUMNS, globalScene.gameData.eggs.length); i++) { const x = (i % this.COLUMNS) * 18; const y = Math.floor(i / this.COLUMNS) * 18; - const icon = globalScene.add.sprite(x - 2, y + 2, "egg_icons"); - icon.setScale(0.5); - icon.setOrigin(0, 0); + const icon = globalScene.add + .sprite(x - 2, y + 2, "egg_icons") + .setScale(0.5) + .setOrigin(0); this.eggListIconContainer.add(icon); this.eggIcons.push(icon); } @@ -148,15 +138,13 @@ export default class EggListUiHandler extends MessageUiHandler { private updateEggIcons() { const indexOffset = this.scrollGridHandler.getItemOffset(); const eggsToShow = Math.min(this.eggIcons.length, globalScene.gameData.eggs.length - indexOffset); - this.eggIcons.forEach((icon, i) => { if (i !== this.cursor) { this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.NONE); } if (i < eggsToShow) { const egg = globalScene.gameData.eggs[i + indexOffset]; - icon.setFrame(egg.getKey()); - icon.setVisible(true); + icon.setFrame(egg.getKey()).setVisible(true); } else { icon.setVisible(false); } @@ -187,7 +175,6 @@ export default class EggListUiHandler extends MessageUiHandler { const ui = this.getUi(); let success = false; - const error = false; if (button === Button.CANCEL) { ui.revertMode(); @@ -198,11 +185,9 @@ export default class EggListUiHandler extends MessageUiHandler { if (success) { ui.playSelect(); - } else if (error) { - ui.playError(); } - return success || error; + return success; } setCursor(cursor: number): boolean { From 2a769e2733f7b05a297e62fd7bd2866af5b43b3a Mon Sep 17 00:00:00 2001 From: ShinigamiHolo <128856544+ShinigamiHolo@users.noreply.github.com> Date: Fri, 6 Jun 2025 22:45:12 +0300 Subject: [PATCH 06/23] [UI/UX] Adding more space in Egg Gacha for another languge (#5934) --- src/ui/egg-gacha-ui-handler.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index a9a978548a8..eb3c6c75d97 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -206,7 +206,17 @@ export default class EggGachaUiHandler extends MessageUiHandler { this.eggGachaOptionsContainer = globalScene.add.container(globalScene.game.canvas.width / 6, 148); this.eggGachaContainer.add(this.eggGachaOptionsContainer); - this.eggGachaOptionSelectBg = addWindow(0, 0, 96, 16 + 576 * this.scale); + // Increase egg box width on certain languages + let eggGachaOptionSelectWidth = 0; + switch (i18next.resolvedLanguage) { + case "ru": + eggGachaOptionSelectWidth = 100; + break; + default: + eggGachaOptionSelectWidth = 96; + } + + this.eggGachaOptionSelectBg = addWindow(0, 0, eggGachaOptionSelectWidth, 16 + 576 * this.scale); this.eggGachaOptionSelectBg.setOrigin(1, 1); this.eggGachaOptionsContainer.add(this.eggGachaOptionSelectBg); From 9021e4b41e67636e523811aa8266d59351904152 Mon Sep 17 00:00:00 2001 From: Lugiad <2070109+Adri1@users.noreply.github.com> Date: Fri, 6 Jun 2025 21:52:35 +0200 Subject: [PATCH 07/23] [i18n] More Controller settings localization (#5920) --- src/system/settings/settings-gamepad.ts | 9 +++++---- src/ui/settings/gamepad-binding-ui-handler.ts | 5 +++-- src/ui/settings/keyboard-binding-ui-handler.ts | 3 ++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/system/settings/settings-gamepad.ts b/src/system/settings/settings-gamepad.ts index 12add905096..d39d5cf5a41 100644 --- a/src/system/settings/settings-gamepad.ts +++ b/src/system/settings/settings-gamepad.ts @@ -4,6 +4,7 @@ import { truncateString } from "../../utils/common"; import { Button } from "#enums/buttons"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; import { globalScene } from "#app/global-scene"; +import i18next from "i18next"; export enum SettingGamepad { Controller = "CONTROLLER", @@ -27,11 +28,11 @@ export enum SettingGamepad { Button_Submit = "BUTTON_SUBMIT", } -const pressAction = "Press action to assign"; +const pressAction = i18next.t("settings:pressActionToAssign"); export const settingGamepadOptions = { - [SettingGamepad.Controller]: ["Default", "Change"], - [SettingGamepad.Gamepad_Support]: ["Auto", "Disabled"], + [SettingGamepad.Controller]: [i18next.t("settings:controllerDefault"), i18next.t("settings:controllerChange")], + [SettingGamepad.Gamepad_Support]: [i18next.t("settings:gamepadSupportAuto"), i18next.t("settings:gamepadSupportDisabled")], [SettingGamepad.Button_Up]: [`KEY ${Button.UP.toString()}`, pressAction], [SettingGamepad.Button_Down]: [`KEY ${Button.DOWN.toString()}`, pressAction], [SettingGamepad.Button_Left]: [`KEY ${Button.LEFT.toString()}`, pressAction], @@ -140,7 +141,7 @@ export function setSettingGamepad(setting: SettingGamepad, value: number): boole handler: () => changeGamepadHandler(g), })), { - label: "Cancel", + label: i18next.t("settings:cancelContollerChoice"), handler: cancelHandler, }, ], diff --git a/src/ui/settings/gamepad-binding-ui-handler.ts b/src/ui/settings/gamepad-binding-ui-handler.ts index 0f226ddcafa..9ddb54131d5 100644 --- a/src/ui/settings/gamepad-binding-ui-handler.ts +++ b/src/ui/settings/gamepad-binding-ui-handler.ts @@ -4,6 +4,7 @@ import { Device } from "#enums/devices"; import { getIconWithSettingName, getKeyWithKeycode } from "#app/configs/inputs/configHandler"; import { addTextObject, TextStyle } from "#app/ui/text"; import { globalScene } from "#app/global-scene"; +import i18next from "i18next"; export default class GamepadBindingUiHandler extends AbstractBindingUiHandler { constructor(mode: UiMode | null = null) { @@ -19,7 +20,7 @@ export default class GamepadBindingUiHandler extends AbstractBindingUiHandler { this.newButtonIcon.setOrigin(0.5); this.newButtonIcon.setVisible(false); - this.swapText = addTextObject(0, 0, "will swap with", TextStyle.WINDOW); + this.swapText = addTextObject(0, 0, i18next.t("settings:willSwapWith"), TextStyle.WINDOW); this.swapText.setOrigin(0.5); this.swapText.setPositionRelative( this.optionSelectBg, @@ -33,7 +34,7 @@ export default class GamepadBindingUiHandler extends AbstractBindingUiHandler { this.targetButtonIcon.setOrigin(0.5); this.targetButtonIcon.setVisible(false); - this.actionLabel = addTextObject(0, 0, "Confirm swap", TextStyle.SETTINGS_LABEL); + this.actionLabel = addTextObject(0, 0, i18next.t("settings:confirmSwap"), TextStyle.SETTINGS_LABEL); this.actionLabel.setOrigin(0, 0.5); this.actionLabel.setPositionRelative(this.actionBg, this.actionBg.width - 75, this.actionBg.height / 2); this.actionsContainer.add(this.actionLabel); diff --git a/src/ui/settings/keyboard-binding-ui-handler.ts b/src/ui/settings/keyboard-binding-ui-handler.ts index c05a31ca91e..183b8269e78 100644 --- a/src/ui/settings/keyboard-binding-ui-handler.ts +++ b/src/ui/settings/keyboard-binding-ui-handler.ts @@ -4,6 +4,7 @@ import { getKeyWithKeycode } from "#app/configs/inputs/configHandler"; import { Device } from "#enums/devices"; import { addTextObject, TextStyle } from "#app/ui/text"; import { globalScene } from "#app/global-scene"; +import i18next from "i18next"; export default class KeyboardBindingUiHandler extends AbstractBindingUiHandler { constructor(mode: UiMode | null = null) { @@ -21,7 +22,7 @@ export default class KeyboardBindingUiHandler extends AbstractBindingUiHandler { this.newButtonIcon.setOrigin(0.5); this.newButtonIcon.setVisible(false); - this.actionLabel = addTextObject(0, 0, "Assign button", TextStyle.SETTINGS_LABEL); + this.actionLabel = addTextObject(0, 0, i18next.t("settings:assignButton"), TextStyle.SETTINGS_LABEL); this.actionLabel.setOrigin(0, 0.5); this.actionLabel.setPositionRelative(this.actionBg, this.actionBg.width - 80, this.actionBg.height / 2); this.actionsContainer.add(this.actionLabel); From 1fc42b32312d8069deb9da1f3e461c14601495b1 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Fri, 6 Jun 2025 16:08:23 -0500 Subject: [PATCH 08/23] [Misc] Add `phase#is` method to help reduce circular imports (#5868) * Move phase types out of phase interceptor * Create isXPhase method and add properties to each phase * Replace instanceof phase with isXPhase * Fix missing union types for phaseName * Update doc comment in phase.ts * Fix incomplete comment in encounter-phase * Make phaseName as public and fix more uses * Move phaseName property declaration before constructor in move anim phase Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Rename isXPhase to is --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/@types/phase-types.ts | 286 ++++++++++++++++++ src/battle-scene.ts | 11 +- src/data/abilities/ability.ts | 4 +- src/data/arena-tag.ts | 2 +- src/data/battler-tags.ts | 20 +- src/data/moves/move.ts | 14 +- .../utils/encounter-phase-utils.ts | 2 +- src/field/pokemon.ts | 5 +- src/phase.ts | 26 +- src/phases/add-enemy-buff-modifier-phase.ts | 1 + src/phases/attempt-capture-phase.ts | 1 + src/phases/attempt-run-phase.ts | 1 + src/phases/battle-end-phase.ts | 5 +- src/phases/battle-phase.ts | 2 +- src/phases/berry-phase.ts | 1 + src/phases/check-status-effect-phase.ts | 1 + src/phases/check-switch-phase.ts | 1 + src/phases/command-phase.ts | 1 + src/phases/common-anim-phase.ts | 3 + src/phases/damage-anim-phase.ts | 1 + src/phases/egg-hatch-phase.ts | 3 +- src/phases/egg-lapse-phase.ts | 1 + src/phases/egg-summary-phase.ts | 1 + src/phases/encounter-phase.ts | 2 + src/phases/end-card-phase.ts | 1 + src/phases/end-evolution-phase.ts | 1 + src/phases/enemy-command-phase.ts | 1 + src/phases/evolution-phase.ts | 3 + src/phases/exp-phase.ts | 1 + src/phases/faint-phase.ts | 1 + src/phases/form-change-phase.ts | 1 + src/phases/game-over-modifier-reward-phase.ts | 1 + src/phases/game-over-phase.ts | 1 + src/phases/hide-ability-phase.ts | 1 + src/phases/hide-party-exp-bar-phase.ts | 1 + src/phases/learn-move-phase.ts | 6 +- src/phases/level-cap-phase.ts | 1 + src/phases/level-up-phase.ts | 1 + src/phases/load-move-anim-phase.ts | 1 + src/phases/login-phase.ts | 1 + src/phases/message-phase.ts | 1 + src/phases/modifier-reward-phase.ts | 4 + src/phases/money-reward-phase.ts | 1 + src/phases/move-anim-phase.ts | 2 + src/phases/move-charge-phase.ts | 4 +- src/phases/move-effect-phase.ts | 1 + src/phases/move-end-phase.ts | 1 + src/phases/move-header-phase.ts | 1 + src/phases/move-phase.ts | 1 + src/phases/mystery-encounter-phases.ts | 13 +- src/phases/new-battle-phase.ts | 5 +- src/phases/new-biome-encounter-phase.ts | 1 + src/phases/next-encounter-phase.ts | 1 + src/phases/obtain-status-effect-phase.ts | 1 + src/phases/party-exp-phase.ts | 1 + src/phases/party-heal-phase.ts | 1 + src/phases/pokemon-anim-phase.ts | 1 + src/phases/pokemon-heal-phase.ts | 1 + src/phases/pokemon-transform-phase.ts | 1 + src/phases/post-game-over-phase.ts | 1 + src/phases/post-summon-phase.ts | 1 + src/phases/post-turn-status-effect-phase.ts | 1 + src/phases/quiet-form-change-phase.ts | 5 +- src/phases/reload-session-phase.ts | 1 + src/phases/reset-status-phase.ts | 1 + src/phases/return-phase.ts | 1 + src/phases/revival-blessing-phase.ts | 1 + src/phases/ribbon-modifier-reward-phase.ts | 1 + src/phases/scan-ivs-phase.ts | 1 + src/phases/select-biome-phase.ts | 1 + src/phases/select-challenge-phase.ts | 1 + src/phases/select-gender-phase.ts | 1 + src/phases/select-modifier-phase.ts | 1 + src/phases/select-starter-phase.ts | 1 + src/phases/select-target-phase.ts | 1 + src/phases/shiny-sparkle-phase.ts | 1 + src/phases/show-ability-phase.ts | 1 + src/phases/show-party-exp-bar-phase.ts | 1 + src/phases/show-trainer-phase.ts | 1 + src/phases/stat-stage-change-phase.ts | 9 +- src/phases/summon-missing-phase.ts | 1 + src/phases/summon-phase.ts | 2 + src/phases/switch-biome-phase.ts | 1 + src/phases/switch-phase.ts | 4 +- src/phases/switch-summon-phase.ts | 1 + src/phases/tera-phase.ts | 1 + src/phases/title-phase.ts | 1 + src/phases/toggle-double-position-phase.ts | 1 + src/phases/trainer-victory-phase.ts | 1 + src/phases/turn-end-phase.ts | 1 + src/phases/turn-init-phase.ts | 1 + src/phases/turn-start-phase.ts | 1 + src/phases/unavailable-phase.ts | 1 + src/phases/unlock-phase.ts | 1 + src/phases/victory-phase.ts | 1 + src/phases/weather-effect-phase.ts | 1 + src/ui/command-ui-handler.ts | 4 +- src/ui/egg-hatch-scene-handler.ts | 3 +- src/ui/egg-summary-ui-handler.ts | 3 +- src/ui/menu-ui-handler.ts | 3 +- src/ui/party-ui-handler.ts | 5 +- src/ui/pokedex-page-ui-handler.ts | 2 +- test/testUtils/phaseInterceptor.ts | 122 +------- 103 files changed, 468 insertions(+), 187 deletions(-) create mode 100644 src/@types/phase-types.ts diff --git a/src/@types/phase-types.ts b/src/@types/phase-types.ts new file mode 100644 index 00000000000..596d9b15723 --- /dev/null +++ b/src/@types/phase-types.ts @@ -0,0 +1,286 @@ +import type { MoveAnim } from "#app/data/battle-anims"; +import type { AddEnemyBuffModifierPhase } from "#app/phases/add-enemy-buff-modifier-phase"; +import type { AttemptCapturePhase } from "#app/phases/attempt-capture-phase"; +import type { AttemptRunPhase } from "#app/phases/attempt-run-phase"; +import type { BattleEndPhase } from "#app/phases/battle-end-phase"; +import type { BerryPhase } from "#app/phases/berry-phase"; +import type { CheckStatusEffectPhase } from "#app/phases/check-status-effect-phase"; +import type { CheckSwitchPhase } from "#app/phases/check-switch-phase"; +import type { CommandPhase } from "#app/phases/command-phase"; +import type { CommonAnimPhase } from "#app/phases/common-anim-phase"; +import type { DamageAnimPhase } from "#app/phases/damage-anim-phase"; +import type { EggHatchPhase } from "#app/phases/egg-hatch-phase"; +import type { EggLapsePhase } from "#app/phases/egg-lapse-phase"; +import type { EggSummaryPhase } from "#app/phases/egg-summary-phase"; +import type { EncounterPhase } from "#app/phases/encounter-phase"; +import type { EndCardPhase } from "#app/phases/end-card-phase"; +import type { EndEvolutionPhase } from "#app/phases/end-evolution-phase"; +import type { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import type { EvolutionPhase } from "#app/phases/evolution-phase"; +import type { ExpPhase } from "#app/phases/exp-phase"; +import type { FaintPhase } from "#app/phases/faint-phase"; +import type { FormChangePhase } from "#app/phases/form-change-phase"; +import type { GameOverModifierRewardPhase } from "#app/phases/game-over-modifier-reward-phase"; +import type { GameOverPhase } from "#app/phases/game-over-phase"; +import type { HideAbilityPhase } from "#app/phases/hide-ability-phase"; +import type { HidePartyExpBarPhase } from "#app/phases/hide-party-exp-bar-phase"; +import type { LearnMovePhase } from "#app/phases/learn-move-phase"; +import type { LevelCapPhase } from "#app/phases/level-cap-phase"; +import type { LevelUpPhase } from "#app/phases/level-up-phase"; +import type { LoadMoveAnimPhase } from "#app/phases/load-move-anim-phase"; +import type { LoginPhase } from "#app/phases/login-phase"; +import type { MessagePhase } from "#app/phases/message-phase"; +import type { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; +import type { MoneyRewardPhase } from "#app/phases/money-reward-phase"; +import type { MoveAnimPhase } from "#app/phases/move-anim-phase"; +import type { MoveChargePhase } from "#app/phases/move-charge-phase"; +import type { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import type { MoveEndPhase } from "#app/phases/move-end-phase"; +import type { MoveHeaderPhase } from "#app/phases/move-header-phase"; +import type { MovePhase } from "#app/phases/move-phase"; +import type { + MysteryEncounterPhase, + MysteryEncounterOptionSelectedPhase, + MysteryEncounterBattlePhase, + MysteryEncounterRewardsPhase, + PostMysteryEncounterPhase, + MysteryEncounterBattleStartCleanupPhase, +} from "#app/phases/mystery-encounter-phases"; +import type { NewBattlePhase } from "#app/phases/new-battle-phase"; +import type { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase"; +import type { NextEncounterPhase } from "#app/phases/next-encounter-phase"; +import type { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase"; +import type { PartyExpPhase } from "#app/phases/party-exp-phase"; +import type { PartyHealPhase } from "#app/phases/party-heal-phase"; +import type { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase"; +import type { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; +import type { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase"; +import type { PostGameOverPhase } from "#app/phases/post-game-over-phase"; +import type { PostSummonPhase } from "#app/phases/post-summon-phase"; +import type { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase"; +import type { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; +import type { ReloadSessionPhase } from "#app/phases/reload-session-phase"; +import type { ResetStatusPhase } from "#app/phases/reset-status-phase"; +import type { ReturnPhase } from "#app/phases/return-phase"; +import type { RevivalBlessingPhase } from "#app/phases/revival-blessing-phase"; +import type { RibbonModifierRewardPhase } from "#app/phases/ribbon-modifier-reward-phase"; +import type { ScanIvsPhase } from "#app/phases/scan-ivs-phase"; +import type { SelectBiomePhase } from "#app/phases/select-biome-phase"; +import type { SelectChallengePhase } from "#app/phases/select-challenge-phase"; +import type { SelectGenderPhase } from "#app/phases/select-gender-phase"; +import type { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import type { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import type { SelectTargetPhase } from "#app/phases/select-target-phase"; +import type { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase"; +import type { ShowAbilityPhase } from "#app/phases/show-ability-phase"; +import type { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase"; +import type { ShowTrainerPhase } from "#app/phases/show-trainer-phase"; +import type { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; +import type { SummonMissingPhase } from "#app/phases/summon-missing-phase"; +import type { SummonPhase } from "#app/phases/summon-phase"; +import type { SwitchBiomePhase } from "#app/phases/switch-biome-phase"; +import type { SwitchPhase } from "#app/phases/switch-phase"; +import type { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; +import type { TeraPhase } from "#app/phases/tera-phase"; +import type { TitlePhase } from "#app/phases/title-phase"; +import type { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; +import type { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase"; +import type { TurnEndPhase } from "#app/phases/turn-end-phase"; +import type { TurnInitPhase } from "#app/phases/turn-init-phase"; +import type { TurnStartPhase } from "#app/phases/turn-start-phase"; +import type { UnavailablePhase } from "#app/phases/unavailable-phase"; +import type { UnlockPhase } from "#app/phases/unlock-phase"; +import type { VictoryPhase } from "#app/phases/victory-phase"; +import type { WeatherEffectPhase } from "#app/phases/weather-effect-phase"; + +export type PhaseClass = + | typeof AddEnemyBuffModifierPhase + | typeof AttemptCapturePhase + | typeof AttemptRunPhase + | typeof BattleEndPhase + | typeof BerryPhase + | typeof CheckStatusEffectPhase + | typeof CheckSwitchPhase + | typeof CommandPhase + | typeof CommonAnimPhase + | typeof DamageAnimPhase + | typeof EggHatchPhase + | typeof EggLapsePhase + | typeof EggSummaryPhase + | typeof EncounterPhase + | typeof EndCardPhase + | typeof EndEvolutionPhase + | typeof EnemyCommandPhase + | typeof EvolutionPhase + | typeof FormChangePhase + | typeof ExpPhase + | typeof FaintPhase + | typeof FormChangePhase + | typeof GameOverPhase + | typeof GameOverModifierRewardPhase + | typeof HideAbilityPhase + | typeof HidePartyExpBarPhase + | typeof LearnMovePhase + | typeof LevelUpPhase + | typeof LevelCapPhase + | typeof LoadMoveAnimPhase + | typeof LoginPhase + | typeof MessagePhase + | typeof ModifierRewardPhase + | typeof MoneyRewardPhase + | typeof MoveAnimPhase + | typeof MoveChargePhase + | typeof MoveEffectPhase + | typeof MoveEndPhase + | typeof MoveHeaderPhase + | typeof MovePhase + | typeof MysteryEncounterPhase + | typeof MysteryEncounterOptionSelectedPhase + | typeof MysteryEncounterBattlePhase + | typeof MysteryEncounterRewardsPhase + | typeof MysteryEncounterBattleStartCleanupPhase + | typeof MysteryEncounterRewardsPhase + | typeof PostMysteryEncounterPhase + | typeof NewBattlePhase + | typeof NewBiomeEncounterPhase + | typeof NextEncounterPhase + | typeof ObtainStatusEffectPhase + | typeof PartyExpPhase + | typeof PartyHealPhase + | typeof PokemonAnimPhase + | typeof PokemonHealPhase + | typeof PokemonTransformPhase + | typeof PostGameOverPhase + | typeof PostSummonPhase + | typeof PostTurnStatusEffectPhase + | typeof QuietFormChangePhase + | typeof ReloadSessionPhase + | typeof ResetStatusPhase + | typeof ReturnPhase + | typeof RevivalBlessingPhase + | typeof RibbonModifierRewardPhase + | typeof ScanIvsPhase + | typeof SelectBiomePhase + | typeof SelectChallengePhase + | typeof SelectGenderPhase + | typeof SelectModifierPhase + | typeof SelectStarterPhase + | typeof SelectTargetPhase + | typeof ShinySparklePhase + | typeof ShowAbilityPhase + | typeof ShowTrainerPhase + | typeof ShowPartyExpBarPhase + | typeof StatStageChangePhase + | typeof SummonMissingPhase + | typeof SummonPhase + | typeof SwitchBiomePhase + | typeof SwitchPhase + | typeof SwitchSummonPhase + | typeof TeraPhase + | typeof TitlePhase + | typeof ToggleDoublePositionPhase + | typeof TrainerVictoryPhase + | typeof TurnEndPhase + | typeof TurnInitPhase + | typeof TurnStartPhase + | typeof UnavailablePhase + | typeof UnlockPhase + | typeof VictoryPhase + | typeof WeatherEffectPhase; + +/** Typescript map used to map a string phase to the actual phase type */ +export type PhaseMap = { + AddEnemyBuffModifierPhase: AddEnemyBuffModifierPhase; + AttemptCapturePhase: AttemptCapturePhase; + AttemptRunPhase: AttemptRunPhase; + BattleEndPhase: BattleEndPhase; + BerryPhase: BerryPhase; + CheckStatusEffectPhase: CheckStatusEffectPhase; + CheckSwitchPhase: CheckSwitchPhase; + CommandPhase: CommandPhase; + CommonAnimPhase: CommonAnimPhase; + DamageAnimPhase: DamageAnimPhase; + EggHatchPhase: EggHatchPhase; + EggLapsePhase: EggLapsePhase; + EggSummaryPhase: EggSummaryPhase; + EncounterPhase: EncounterPhase; + EndCardPhase: EndCardPhase; + EndEvolutionPhase: EndEvolutionPhase; + EnemyCommandPhase: EnemyCommandPhase; + EvolutionPhase: EvolutionPhase; + ExpPhase: ExpPhase; + FaintPhase: FaintPhase; + FormChangePhase: FormChangePhase; + GameOverPhase: GameOverPhase; + GameOverModifierRewardPhase: GameOverModifierRewardPhase; + HideAbilityPhase: HideAbilityPhase; + HidePartyExpBarPhase: HidePartyExpBarPhase; + LearnMovePhase: LearnMovePhase; + LevelCapPhase: LevelCapPhase; + LevelUpPhase: LevelUpPhase; + LoadMoveAnimPhase: LoadMoveAnimPhase; + LoginPhase: LoginPhase; + MessagePhase: MessagePhase; + ModifierRewardPhase: ModifierRewardPhase; + MoneyRewardPhase: MoneyRewardPhase; + MoveAnimPhase: MoveAnimPhase; + MoveChargePhase: MoveChargePhase; + MoveEffectPhase: MoveEffectPhase; + MoveEndPhase: MoveEndPhase; + MoveHeaderPhase: MoveHeaderPhase; + MovePhase: MovePhase; + MysteryEncounterPhase: MysteryEncounterPhase; + MysteryEncounterOptionSelectedPhase: MysteryEncounterOptionSelectedPhase; + MysteryEncounterBattlePhase: MysteryEncounterBattlePhase; + MysteryEncounterBattleStartCleanupPhase: MysteryEncounterBattleStartCleanupPhase; + MysteryEncounterRewardsPhase: MysteryEncounterRewardsPhase; + PostMysteryEncounterPhase: PostMysteryEncounterPhase; + NewBattlePhase: NewBattlePhase; + NewBiomeEncounterPhase: NewBiomeEncounterPhase; + NextEncounterPhase: NextEncounterPhase; + ObtainStatusEffectPhase: ObtainStatusEffectPhase; + PartyExpPhase: PartyExpPhase; + PartyHealPhase: PartyHealPhase; + PokemonAnimPhase: PokemonAnimPhase; + PokemonHealPhase: PokemonHealPhase; + PokemonTransformPhase: PokemonTransformPhase; + PostGameOverPhase: PostGameOverPhase; + PostSummonPhase: PostSummonPhase; + PostTurnStatusEffectPhase: PostTurnStatusEffectPhase; + QuietFormChangePhase: QuietFormChangePhase; + ReloadSessionPhase: ReloadSessionPhase; + ResetStatusPhase: ResetStatusPhase; + ReturnPhase: ReturnPhase; + RevivalBlessingPhase: RevivalBlessingPhase; + RibbonModifierRewardPhase: RibbonModifierRewardPhase; + ScanIvsPhase: ScanIvsPhase; + SelectBiomePhase: SelectBiomePhase; + SelectChallengePhase: SelectChallengePhase; + SelectGenderPhase: SelectGenderPhase; + SelectModifierPhase: SelectModifierPhase; + SelectStarterPhase: SelectStarterPhase; + SelectTargetPhase: SelectTargetPhase; + ShinySparklePhase: ShinySparklePhase; + ShowAbilityPhase: ShowAbilityPhase; + ShowPartyExpBarPhase: ShowPartyExpBarPhase; + ShowTrainerPhase: ShowTrainerPhase; + StatStageChangePhase: StatStageChangePhase; + SummonMissingPhase: SummonMissingPhase; + SummonPhase: SummonPhase; + SwitchBiomePhase: SwitchBiomePhase; + SwitchPhase: SwitchPhase; + SwitchSummonPhase: SwitchSummonPhase; + TeraPhase: TeraPhase; + TitlePhase: TitlePhase; + ToggleDoublePositionPhase: ToggleDoublePositionPhase; + TrainerVictoryPhase: TrainerVictoryPhase; + TurnEndPhase: TurnEndPhase; + TurnInitPhase: TurnInitPhase; + TurnStartPhase: TurnStartPhase; + UnavailablePhase: UnavailablePhase; + UnlockPhase: UnlockPhase; + VictoryPhase: VictoryPhase; + WeatherEffectPhase: WeatherEffectPhase; +}; + +export type PhaseString = keyof PhaseMap; diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 34d26b3975c..f2f952ea301 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -145,7 +145,7 @@ import { LoadingScene } from "#app/loading-scene"; import { LevelCapPhase } from "#app/phases/level-cap-phase"; import { LoginPhase } from "#app/phases/login-phase"; import { MessagePhase } from "#app/phases/message-phase"; -import { MovePhase } from "#app/phases/move-phase"; +import type { MovePhase } from "#app/phases/move-phase"; import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase"; import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase"; @@ -153,7 +153,6 @@ import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; import { ReturnPhase } from "#app/phases/return-phase"; import { ShowTrainerPhase } from "#app/phases/show-trainer-phase"; import { SummonPhase } from "#app/phases/summon-phase"; -import { SwitchPhase } from "#app/phases/switch-phase"; import { TitlePhase } from "#app/phases/title-phase"; import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase"; @@ -901,7 +900,7 @@ export default class BattleScene extends SceneBase { do { targetingMovePhase = this.findPhase( mp => - mp instanceof MovePhase && + mp.is("MovePhase") && mp.targets.length === 1 && mp.targets[0] === removedPokemon.getBattlerIndex() && mp.pokemon.isPlayer() !== allyPokemon.isPlayer(), @@ -1450,7 +1449,7 @@ export default class BattleScene extends SceneBase { } if (lastBattle?.double && !newDouble) { - this.tryRemovePhase(p => p instanceof SwitchPhase); + this.tryRemovePhase((p: Phase) => p.is("SwitchPhase")); for (const p of this.getPlayerField()) { p.lapseTag(BattlerTagType.COMMANDED); } @@ -1588,9 +1587,7 @@ export default class BattleScene extends SceneBase { return 0; } - const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes( - this.getCurrentPhase()?.constructor.name ?? "", - ); + const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes(this.getCurrentPhase()?.phaseName ?? ""); if ( // Give trainers with specialty types an appropriately-typed form for Wormadam, Rotom, Arceus, Oricorio, Silvally, or Paldean Tauros. diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 8f5f267f7ef..1bd71df32e0 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -2828,7 +2828,7 @@ export class CommanderAbAttr extends AbAttr { // Apply boosts from this effect to the ally Dondozo pokemon.getAlly()?.addTag(BattlerTagType.COMMANDED, 0, MoveId.NONE, pokemon.id); // Cancel the source Pokemon's next move (if a move is queued) - globalScene.tryRemovePhase((phase) => phase instanceof MovePhase && phase.pokemon === pokemon); + globalScene.tryRemovePhase((phase) => phase.is("MovePhase") && phase.pokemon === pokemon); } } } @@ -6897,7 +6897,7 @@ export function initAbilities() { .ignorable(), new Ability(AbilityId.ANALYTIC, 5) .attr(MovePowerBoostAbAttr, (user, target, move) => { - const movePhase = globalScene.findPhase((phase) => phase instanceof MovePhase && phase.pokemon.id !== user?.id); + const movePhase = globalScene.findPhase((phase) => phase.is("MovePhase") && phase.pokemon.id !== user?.id); return isNullOrUndefined(movePhase); }, 1.3), new Ability(AbilityId.ILLUSION, 5) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 590319a01c0..0254aab37e1 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -383,7 +383,7 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (_arena, moveId) => { const move = allMoves[moveId]; const effectPhase = globalScene.getCurrentPhase(); - if (effectPhase instanceof MoveEffectPhase) { + if (effectPhase?.is("MoveEffectPhase")) { const attacker = effectPhase.getUserPokemon(); if (attacker) { return move.getPriority(attacker) > 0; diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 4f263fc152b..c047f424591 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -28,7 +28,7 @@ import type Pokemon from "#app/field/pokemon"; import { HitResult, MoveResult } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { CommonAnimPhase } from "#app/phases/common-anim-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import type { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MovePhase } from "#app/phases/move-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import type { StatStageChangeCallback } from "#app/phases/stat-stage-change-phase"; @@ -553,9 +553,9 @@ export class ShellTrapTag extends BattlerTag { // Trap should only be triggered by opponent's Physical moves if (phaseData?.move.category === MoveCategory.PHYSICAL && pokemon.isOpponent(phaseData.attacker)) { const shellTrapPhaseIndex = globalScene.phaseQueue.findIndex( - phase => phase instanceof MovePhase && phase.pokemon === pokemon, + phase => phase.is("MovePhase") && phase.pokemon === pokemon, ); - const firstMovePhaseIndex = globalScene.phaseQueue.findIndex(phase => phase instanceof MovePhase); + const firstMovePhaseIndex = globalScene.phaseQueue.findIndex(phase => phase.is("MovePhase")); // Only shift MovePhase timing if it's not already next up if (shellTrapPhaseIndex !== -1 && shellTrapPhaseIndex !== firstMovePhaseIndex) { @@ -1027,7 +1027,7 @@ export class PowderTag extends BattlerTag { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { if (lapseType === BattlerTagLapseType.PRE_MOVE) { const movePhase = globalScene.getCurrentPhase(); - if (movePhase instanceof MovePhase) { + if (movePhase?.is("MovePhase")) { const move = movePhase.move.getMove(); const weather = globalScene.arena.weather; if ( @@ -1183,13 +1183,13 @@ export class EncoreTag extends MoveRestrictionBattlerTag { }), ); - const movePhase = globalScene.findPhase(m => m instanceof MovePhase && m.pokemon === pokemon); + const movePhase = globalScene.findPhase(m => m.is("MovePhase") && m.pokemon === pokemon); if (movePhase) { const movesetMove = pokemon.getMoveset().find(m => m.moveId === this.moveId); if (movesetMove) { const lastMove = pokemon.getLastXMoves(1)[0]; globalScene.tryReplacePhase( - m => m instanceof MovePhase && m.pokemon === pokemon, + m => m.is("MovePhase") && m.pokemon === pokemon, new MovePhase(pokemon, lastMove.targets ?? [], movesetMove), ); } @@ -1624,7 +1624,7 @@ export class ProtectedTag extends BattlerTag { // Stop multi-hit moves early const effectPhase = globalScene.getCurrentPhase(); - if (effectPhase instanceof MoveEffectPhase) { + if (effectPhase?.is("MoveEffectPhase")) { effectPhase.stopMultiHit(pokemon); } return true; @@ -2646,7 +2646,7 @@ export class GulpMissileTag extends BattlerTag { } const moveEffectPhase = globalScene.getCurrentPhase(); - if (moveEffectPhase instanceof MoveEffectPhase) { + if (moveEffectPhase?.is("MoveEffectPhase")) { const attacker = moveEffectPhase.getUserPokemon(); if (!attacker) { @@ -3004,7 +3004,7 @@ export class SubstituteTag extends BattlerTag { /** If the Substitute redirects damage, queue a message to indicate it. */ onHit(pokemon: Pokemon): void { const moveEffectPhase = globalScene.getCurrentPhase(); - if (moveEffectPhase instanceof MoveEffectPhase) { + if (moveEffectPhase?.is("MoveEffectPhase")) { const attacker = moveEffectPhase.getUserPokemon(); if (!attacker) { return; @@ -3693,7 +3693,7 @@ export function loadBattlerTag(source: BattlerTag | any): BattlerTag { */ function getMoveEffectPhaseData(_pokemon: Pokemon): { phase: MoveEffectPhase; attacker: Pokemon; move: Move } | null { const phase = globalScene.getCurrentPhase(); - if (phase instanceof MoveEffectPhase) { + if (phase?.is("MoveEffectPhase")) { return { phase: phase, attacker: phase.getPokemon(), diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 7e9f99e28c1..91cc6350daa 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -3109,7 +3109,7 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr { const overridden = args[0] as BooleanHolder; - const allyMovePhase = globalScene.findPhase((phase) => phase instanceof MovePhase && phase.pokemon.isPlayer() === user.isPlayer()); + const allyMovePhase = globalScene.findPhase((phase) => phase.is("MovePhase") && phase.pokemon.isPlayer() === user.isPlayer()); if (allyMovePhase) { const allyMove = allyMovePhase.move.getMove(); if (allyMove !== move && allyMove.hasAttr(AwaitCombinedPledgeAttr)) { @@ -3123,7 +3123,7 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr { // Move the ally's MovePhase (if needed) so that the ally moves next const allyMovePhaseIndex = globalScene.phaseQueue.indexOf(allyMovePhase); - const firstMovePhaseIndex = globalScene.phaseQueue.findIndex((phase) => phase instanceof MovePhase); + const firstMovePhaseIndex = globalScene.phaseQueue.findIndex((phase) => phase.is("MovePhase")); if (allyMovePhaseIndex !== firstMovePhaseIndex) { globalScene.prependToPhase(globalScene.phaseQueue.splice(allyMovePhaseIndex, 1)[0], MovePhase); } @@ -4477,7 +4477,7 @@ export class CueNextRoundAttr extends MoveEffectAttr { override apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean { const nextRoundPhase = globalScene.findPhase(phase => - phase instanceof MovePhase && phase.move.moveId === MoveId.ROUND + phase.is("MovePhase") && phase.move.moveId === MoveId.ROUND ); if (!nextRoundPhase) { @@ -4486,7 +4486,7 @@ export class CueNextRoundAttr extends MoveEffectAttr { // Update the phase queue so that the next Pokemon using Round moves next const nextRoundIndex = globalScene.phaseQueue.indexOf(nextRoundPhase); - const nextMoveIndex = globalScene.phaseQueue.findIndex(phase => phase instanceof MovePhase); + const nextMoveIndex = globalScene.phaseQueue.findIndex(phase => phase.is("MovePhase")); if (nextRoundIndex !== nextMoveIndex) { globalScene.prependToPhase(globalScene.phaseQueue.splice(nextRoundIndex, 1)[0], MovePhase); } @@ -6177,7 +6177,7 @@ export class RevivalBlessingAttr extends MoveEffectAttr { // Handle cases where revived pokemon needs to get switched in on same turn if (allyPokemon.isFainted() || allyPokemon === pokemon) { // Enemy switch phase should be removed and replaced with the revived pkmn switching in - globalScene.tryRemovePhase((phase: SwitchSummonPhase) => phase instanceof SwitchSummonPhase && phase.getPokemon() === pokemon); + globalScene.tryRemovePhase((phase: SwitchSummonPhase) => phase.is("SwitchSummonPhase") && phase.getPokemon() === pokemon); // If the pokemon being revived was alive earlier in the turn, cancel its move // (revived pokemon can't move in the turn they're brought back) globalScene.findPhase((phase: MovePhase) => phase.pokemon === pokemon)?.cancel(); @@ -7896,7 +7896,7 @@ export class ForceLastAttr extends MoveEffectAttr { // Either the end of the turn or in front of another, slower move which has also been forced last const prependPhase = globalScene.findPhase((phase) => [ MovePhase, MoveEndPhase ].every(cls => !(phase instanceof cls)) - || (phase instanceof MovePhase) && phaseForcedSlower(phase, target, !!globalScene.arena.getTag(ArenaTagType.TRICK_ROOM)) + || (phase.is("MovePhase")) && phaseForcedSlower(phase, target, !!globalScene.arena.getTag(ArenaTagType.TRICK_ROOM)) ); if (prependPhase) { globalScene.phaseQueue.splice( @@ -7942,7 +7942,7 @@ const userSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target: const targetSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => target.status?.effect === StatusEffect.SLEEP || target.hasAbility(AbilityId.COMATOSE); -const failIfLastCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => globalScene.phaseQueue.find(phase => phase instanceof MovePhase) !== undefined; +const failIfLastCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => globalScene.phaseQueue.find(phase => phase.is("MovePhase")) !== undefined; const failIfLastInPartyCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => { const party: Pokemon[] = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 060de9c3a9e..c9eaa2e6968 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -769,7 +769,7 @@ export function setEncounterRewards( if (customShopRewards) { globalScene.unshiftPhase(new SelectModifierPhase(0, undefined, customShopRewards)); } else { - globalScene.tryRemovePhase(p => p instanceof SelectModifierPhase); + globalScene.tryRemovePhase(p => p.is("MysteryEncounterRewardsPhase")); } if (eggRewards) { diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index cd8563cfb30..d06553f2227 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -232,7 +232,6 @@ import { getPokemonNameWithAffix } from "#app/messages"; import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; import { FaintPhase } from "#app/phases/faint-phase"; import { LearnMovePhase } from "#app/phases/learn-move-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; @@ -1300,7 +1299,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // During the Pokemon's MoveEffect phase, the offset is removed to put the Pokemon "in focus" const currentPhase = globalScene.getCurrentPhase(); - if (currentPhase instanceof MoveEffectPhase && currentPhase.getPokemon() === this) { + if (currentPhase?.is("MoveEffectPhase") && currentPhase.getPokemon() === this) { return false; } return true; @@ -4775,7 +4774,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ if (effect === StatusEffect.SLEEP || effect === StatusEffect.FREEZE) { const currentPhase = globalScene.getCurrentPhase(); - if (currentPhase instanceof MoveEffectPhase && currentPhase.getUserPokemon() === this) { + if (currentPhase?.is("MoveEffectPhase") && currentPhase.getUserPokemon() === this) { this.turnData.hitCount = 1; this.turnData.hitsLeft = 1; } diff --git a/src/phase.ts b/src/phase.ts index 20cc7cc4063..9e2468ebdff 100644 --- a/src/phase.ts +++ b/src/phase.ts @@ -1,9 +1,33 @@ import { globalScene } from "#app/global-scene"; +import type { PhaseMap, PhaseString } from "./@types/phase-types"; -export class Phase { +export abstract class Phase { start() {} end() { globalScene.shiftPhase(); } + + /** + * The string name of the phase, used to identify the phase type for {@linkcode is} + * + * @privateremarks + * + * When implementing a phase, you must set the `phaseName` property to the name of the phase. + */ + public abstract readonly phaseName: PhaseString; + + /** + * Check if the phase is of the given type without requiring `instanceof`. + * + * @param phase - The string name of the phase to check. + * @returns Whether this phase is of the provided type. + * + * @remarks + * This does not check for subclasses! It only checks if the phase is *exactly* the given type. + * This method exists to avoid circular import issues, as using `instanceof` would require importing each phase. + */ + is(phase: K): this is PhaseMap[K] { + return this.phaseName === phase; + } } diff --git a/src/phases/add-enemy-buff-modifier-phase.ts b/src/phases/add-enemy-buff-modifier-phase.ts index 16ed78e6d0d..28eaf0dc4df 100644 --- a/src/phases/add-enemy-buff-modifier-phase.ts +++ b/src/phases/add-enemy-buff-modifier-phase.ts @@ -9,6 +9,7 @@ import { Phase } from "#app/phase"; import { globalScene } from "#app/global-scene"; export class AddEnemyBuffModifierPhase extends Phase { + public readonly phaseName = "AddEnemyBuffModifierPhase"; start() { super.start(); diff --git a/src/phases/attempt-capture-phase.ts b/src/phases/attempt-capture-phase.ts index 8592cd98508..6c2f5f4dd76 100644 --- a/src/phases/attempt-capture-phase.ts +++ b/src/phases/attempt-capture-phase.ts @@ -27,6 +27,7 @@ import { globalScene } from "#app/global-scene"; import { Gender } from "#app/data/gender"; export class AttemptCapturePhase extends PokemonPhase { + public readonly phaseName = "AttemptCapturePhase"; private pokeballType: PokeballType; private pokeball: Phaser.GameObjects.Sprite; private originalY: number; diff --git a/src/phases/attempt-run-phase.ts b/src/phases/attempt-run-phase.ts index 15c521c01fc..ced81567c43 100644 --- a/src/phases/attempt-run-phase.ts +++ b/src/phases/attempt-run-phase.ts @@ -17,6 +17,7 @@ import { globalScene } from "#app/global-scene"; import { SelectBiomePhase } from "./select-biome-phase"; export class AttemptRunPhase extends PokemonPhase { + public readonly phaseName = "AttemptRunPhase"; /** For testing purposes: this is to force the pokemon to fail and escape */ public forceFailEscape = false; diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index b4bb28fe55e..96f6d02b1fc 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -5,6 +5,7 @@ import { BattlePhase } from "./battle-phase"; import { GameOverPhase } from "./game-over-phase"; export class BattleEndPhase extends BattlePhase { + public readonly phaseName = "BattleEndPhase"; /** If true, will increment battles won */ isVictory: boolean; @@ -19,7 +20,7 @@ export class BattleEndPhase extends BattlePhase { // cull any extra `BattleEnd` phases from the queue. globalScene.phaseQueue = globalScene.phaseQueue.filter(phase => { - if (phase instanceof BattleEndPhase) { + if (phase.is("BattleEndPhase")) { this.isVictory ||= phase.isVictory; return false; } @@ -28,7 +29,7 @@ export class BattleEndPhase extends BattlePhase { // `phaseQueuePrepend` is private, so we have to use this inefficient loop. while ( globalScene.tryRemoveUnshiftedPhase(phase => { - if (phase instanceof BattleEndPhase) { + if (phase.is("BattleEndPhase")) { this.isVictory ||= phase.isVictory; return true; } diff --git a/src/phases/battle-phase.ts b/src/phases/battle-phase.ts index d70b3909639..7cefd0369d9 100644 --- a/src/phases/battle-phase.ts +++ b/src/phases/battle-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import { TrainerSlot } from "#enums/trainer-slot"; import { Phase } from "#app/phase"; -export class BattlePhase extends Phase { +export abstract class BattlePhase extends Phase { showEnemyTrainer(trainerSlot: TrainerSlot = TrainerSlot.NONE): void { if (!globalScene.currentBattle.trainer) { console.warn("Enemy trainer is missing!"); diff --git a/src/phases/berry-phase.ts b/src/phases/berry-phase.ts index 989f19c944f..8d66d498039 100644 --- a/src/phases/berry-phase.ts +++ b/src/phases/berry-phase.ts @@ -20,6 +20,7 @@ import type Pokemon from "#app/field/pokemon"; * Also triggers Cud Chew's "repeat berry use" effects */ export class BerryPhase extends FieldPhase { + public readonly phaseName = "BerryPhase"; start() { super.start(); diff --git a/src/phases/check-status-effect-phase.ts b/src/phases/check-status-effect-phase.ts index f59dfea9f02..0c5884bbac7 100644 --- a/src/phases/check-status-effect-phase.ts +++ b/src/phases/check-status-effect-phase.ts @@ -4,6 +4,7 @@ import type { BattlerIndex } from "#app/battle"; import { globalScene } from "#app/global-scene"; export class CheckStatusEffectPhase extends Phase { + public readonly phaseName = "CheckStatusEffectPhase"; private order: BattlerIndex[]; constructor(order: BattlerIndex[]) { super(); diff --git a/src/phases/check-switch-phase.ts b/src/phases/check-switch-phase.ts index 9d73411fd37..0506cd36c46 100644 --- a/src/phases/check-switch-phase.ts +++ b/src/phases/check-switch-phase.ts @@ -10,6 +10,7 @@ import { SwitchPhase } from "./switch-phase"; import { SwitchType } from "#enums/switch-type"; export class CheckSwitchPhase extends BattlePhase { + public readonly phaseName = "CheckSwitchPhase"; protected fieldIndex: number; protected useName: boolean; diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 3f18ea95777..209c1eefc85 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -25,6 +25,7 @@ import { ArenaTagSide } from "#app/data/arena-tag"; import { ArenaTagType } from "#app/enums/arena-tag-type"; export class CommandPhase extends FieldPhase { + public readonly phaseName = "CommandPhase"; protected fieldIndex: number; constructor(fieldIndex: number) { diff --git a/src/phases/common-anim-phase.ts b/src/phases/common-anim-phase.ts index 5be5e112389..4a27db3a651 100644 --- a/src/phases/common-anim-phase.ts +++ b/src/phases/common-anim-phase.ts @@ -5,6 +5,9 @@ import { CommonBattleAnim } from "#app/data/battle-anims"; import { PokemonPhase } from "./pokemon-phase"; export class CommonAnimPhase extends PokemonPhase { + // PokemonHealPhase extends CommonAnimPhase, and to make typescript happy, + // we need to allow phaseName to be a union of the two + public readonly phaseName: "CommonAnimPhase" | "PokemonHealPhase" | "WeatherEffectPhase" = "CommonAnimPhase"; private anim: CommonAnim | null; private targetIndex?: BattlerIndex; private playOnEmptyField: boolean; diff --git a/src/phases/damage-anim-phase.ts b/src/phases/damage-anim-phase.ts index b9581573f2e..85cb26e0a09 100644 --- a/src/phases/damage-anim-phase.ts +++ b/src/phases/damage-anim-phase.ts @@ -6,6 +6,7 @@ import { fixedInt } from "#app/utils/common"; import { PokemonPhase } from "#app/phases/pokemon-phase"; export class DamageAnimPhase extends PokemonPhase { + public readonly phaseName = "DamageAnimPhase"; private amount: number; private damageResult: DamageResult; private critical: boolean; diff --git a/src/phases/egg-hatch-phase.ts b/src/phases/egg-hatch-phase.ts index 69bcf741383..dfcdc05f9a2 100644 --- a/src/phases/egg-hatch-phase.ts +++ b/src/phases/egg-hatch-phase.ts @@ -20,6 +20,7 @@ import { doShinySparkleAnim } from "#app/field/anims"; * Class that represents egg hatching */ export class EggHatchPhase extends Phase { + public readonly phaseName = "EggHatchPhase"; /** The egg that is hatching */ private egg: Egg; /** The new EggHatchData for the egg/pokemon that hatches */ @@ -224,7 +225,7 @@ export class EggHatchPhase extends Phase { } end() { - if (globalScene.findPhase(p => p instanceof EggHatchPhase)) { + if (globalScene.findPhase(p => p.is("EggHatchPhase"))) { this.eggHatchHandler.clear(); } else { globalScene.time.delayedCall(250, () => globalScene.setModifiersVisible(true)); diff --git a/src/phases/egg-lapse-phase.ts b/src/phases/egg-lapse-phase.ts index 4632e264c1d..182d6f304d6 100644 --- a/src/phases/egg-lapse-phase.ts +++ b/src/phases/egg-lapse-phase.ts @@ -16,6 +16,7 @@ import { EggHatchData } from "#app/data/egg-hatch-data"; * Also handles prompts for skipping animation, and calling the egg summary phase */ export class EggLapsePhase extends Phase { + public readonly phaseName = "EggLapsePhase"; private eggHatchData: EggHatchData[] = []; private readonly minEggsToSkip: number = 2; diff --git a/src/phases/egg-summary-phase.ts b/src/phases/egg-summary-phase.ts index d16cafa7611..cc7857426bc 100644 --- a/src/phases/egg-summary-phase.ts +++ b/src/phases/egg-summary-phase.ts @@ -9,6 +9,7 @@ import type { EggHatchData } from "#app/data/egg-hatch-data"; * Phase is handled mostly by the egg-hatch-scene-handler UI */ export class EggSummaryPhase extends Phase { + public readonly phaseName = "EggSummaryPhase"; private eggHatchData: EggHatchData[]; constructor(eggHatchData: EggHatchData[]) { diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index c7308fc5a64..df84f8f8ff4 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -47,6 +47,8 @@ import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mys import { getNatureName } from "#app/data/nature"; export class EncounterPhase extends BattlePhase { + // Union type is necessary as this is subclassed, and typescript will otherwise complain + public readonly phaseName: "EncounterPhase" | "NextEncounterPhase" | "NewBiomeEncounterPhase" = "EncounterPhase"; private loaded: boolean; constructor(loaded = false) { diff --git a/src/phases/end-card-phase.ts b/src/phases/end-card-phase.ts index 41775248b67..bb64969514f 100644 --- a/src/phases/end-card-phase.ts +++ b/src/phases/end-card-phase.ts @@ -5,6 +5,7 @@ import { addTextObject, TextStyle } from "#app/ui/text"; import i18next from "i18next"; export class EndCardPhase extends Phase { + public readonly phaseName = "EndCardPhase"; public endCard: Phaser.GameObjects.Image; public text: Phaser.GameObjects.Text; start(): void { diff --git a/src/phases/end-evolution-phase.ts b/src/phases/end-evolution-phase.ts index 579920dde90..cfc0d89fc31 100644 --- a/src/phases/end-evolution-phase.ts +++ b/src/phases/end-evolution-phase.ts @@ -3,6 +3,7 @@ import { Phase } from "#app/phase"; import { UiMode } from "#enums/ui-mode"; export class EndEvolutionPhase extends Phase { + public readonly phaseName = "EndEvolutionPhase"; start() { super.start(); diff --git a/src/phases/enemy-command-phase.ts b/src/phases/enemy-command-phase.ts index 2a1719f9002..a81fc4d2107 100644 --- a/src/phases/enemy-command-phase.ts +++ b/src/phases/enemy-command-phase.ts @@ -15,6 +15,7 @@ import { BattlerTagType } from "#enums/battler-tag-type"; * @see {@linkcode EnemyPokemon.getNextMove} */ export class EnemyCommandPhase extends FieldPhase { + public readonly phaseName = "EnemyCommandPhase"; protected fieldIndex: number; protected skipTurn = false; diff --git a/src/phases/evolution-phase.ts b/src/phases/evolution-phase.ts index 8fc8a8be031..5e635f4cd82 100644 --- a/src/phases/evolution-phase.ts +++ b/src/phases/evolution-phase.ts @@ -19,6 +19,9 @@ import { EndEvolutionPhase } from "#app/phases/end-evolution-phase"; import { EVOLVE_MOVE } from "#app/data/balance/pokemon-level-moves"; export class EvolutionPhase extends Phase { + // FormChangePhase inherits from this, but EvolutionPhase is not abstract. + // We have to use the union here + public readonly phaseName: "EvolutionPhase" | "FormChangePhase" = "EvolutionPhase"; protected pokemon: PlayerPokemon; protected lastLevel: number; diff --git a/src/phases/exp-phase.ts b/src/phases/exp-phase.ts index 8841a90d5b1..14d7d7578f8 100644 --- a/src/phases/exp-phase.ts +++ b/src/phases/exp-phase.ts @@ -7,6 +7,7 @@ import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-pha import { LevelUpPhase } from "./level-up-phase"; export class ExpPhase extends PlayerPartyMemberPokemonPhase { + public readonly phaseName = "ExpPhase"; private expValue: number; constructor(partyMemberIndex: number, expValue: number) { diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index cd40026e55f..7332d6b9462 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -35,6 +35,7 @@ import { FRIENDSHIP_LOSS_FROM_FAINT } from "#app/data/balance/starters"; import { BattlerTagType } from "#enums/battler-tag-type"; export class FaintPhase extends PokemonPhase { + public readonly phaseName = "FaintPhase"; /** * Whether or not instant revive should be prevented */ diff --git a/src/phases/form-change-phase.ts b/src/phases/form-change-phase.ts index 5517fb0f402..f5e428c6d3d 100644 --- a/src/phases/form-change-phase.ts +++ b/src/phases/form-change-phase.ts @@ -13,6 +13,7 @@ import { BattlerTagType } from "#enums/battler-tag-type"; import { SpeciesFormKey } from "#enums/species-form-key"; export class FormChangePhase extends EvolutionPhase { + public readonly phaseName = "FormChangePhase"; private formChange: SpeciesFormChange; private modal: boolean; diff --git a/src/phases/game-over-modifier-reward-phase.ts b/src/phases/game-over-modifier-reward-phase.ts index ab6f6554c99..13c8f48abad 100644 --- a/src/phases/game-over-modifier-reward-phase.ts +++ b/src/phases/game-over-modifier-reward-phase.ts @@ -4,6 +4,7 @@ import i18next from "i18next"; import { ModifierRewardPhase } from "./modifier-reward-phase"; export class GameOverModifierRewardPhase extends ModifierRewardPhase { + public readonly phaseName = "GameOverModifierRewardPhase"; doReward(): Promise { return new Promise(resolve => { const newModifier = this.modifierType.newModifier(); diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index 3a3305fd45e..1d03739d610 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -34,6 +34,7 @@ import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; import { MessagePhase } from "./message-phase"; export class GameOverPhase extends BattlePhase { + public readonly phaseName = "GameOverPhase"; private isVictory: boolean; private firstRibbons: PokemonSpecies[] = []; diff --git a/src/phases/hide-ability-phase.ts b/src/phases/hide-ability-phase.ts index 142bb4b251d..b0a12da24b1 100644 --- a/src/phases/hide-ability-phase.ts +++ b/src/phases/hide-ability-phase.ts @@ -2,6 +2,7 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; export class HideAbilityPhase extends Phase { + public readonly phaseName = "HideAbilityPhase"; start() { super.start(); diff --git a/src/phases/hide-party-exp-bar-phase.ts b/src/phases/hide-party-exp-bar-phase.ts index 52cfd1f71d6..9ee08280cd4 100644 --- a/src/phases/hide-party-exp-bar-phase.ts +++ b/src/phases/hide-party-exp-bar-phase.ts @@ -2,6 +2,7 @@ import { globalScene } from "#app/global-scene"; import { BattlePhase } from "./battle-phase"; export class HidePartyExpBarPhase extends BattlePhase { + public readonly phaseName = "HidePartyExpBarPhase"; start() { super.start(); diff --git a/src/phases/learn-move-phase.ts b/src/phases/learn-move-phase.ts index 65679a7ade7..d455ed35591 100644 --- a/src/phases/learn-move-phase.ts +++ b/src/phases/learn-move-phase.ts @@ -12,7 +12,6 @@ import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import { PlayerPartyMemberPokemonPhase } from "#app/phases/player-party-member-pokemon-phase"; import type Pokemon from "#app/field/pokemon"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; export enum LearnMoveType { /** For learning a move via level-up, evolution, or other non-item-based event */ @@ -24,6 +23,7 @@ export enum LearnMoveType { } export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { + public readonly phaseName = "LearnMovePhase"; private moveId: MoveId; private messageMode: UiMode; private learnMoveType: LearnMoveType; @@ -195,7 +195,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { pokemon.usedTMs = []; } pokemon.usedTMs.push(this.moveId); - globalScene.tryRemovePhase(phase => phase instanceof SelectModifierPhase); + globalScene.tryRemovePhase(phase => phase.is("SelectModifierPhase")); } else if (this.learnMoveType === LearnMoveType.MEMORY) { if (this.cost !== -1) { if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { @@ -205,7 +205,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { } globalScene.playSound("se/buy"); } else { - globalScene.tryRemovePhase(phase => phase instanceof SelectModifierPhase); + globalScene.tryRemovePhase(phase => phase.is("SelectModifierPhase")); } } pokemon.setMove(index, this.moveId); diff --git a/src/phases/level-cap-phase.ts b/src/phases/level-cap-phase.ts index 6f3fa6fdb39..12d4d64e8e2 100644 --- a/src/phases/level-cap-phase.ts +++ b/src/phases/level-cap-phase.ts @@ -4,6 +4,7 @@ import i18next from "i18next"; import { FieldPhase } from "./field-phase"; export class LevelCapPhase extends FieldPhase { + public readonly phaseName = "LevelCapPhase"; start(): void { super.start(); diff --git a/src/phases/level-up-phase.ts b/src/phases/level-up-phase.ts index 8c4f4f58095..7cf86a313df 100644 --- a/src/phases/level-up-phase.ts +++ b/src/phases/level-up-phase.ts @@ -10,6 +10,7 @@ import { NumberHolder } from "#app/utils/common"; import i18next from "i18next"; export class LevelUpPhase extends PlayerPartyMemberPokemonPhase { + public readonly phaseName = "LevelUpPhase"; protected lastLevel: number; protected level: number; protected pokemon: PlayerPokemon = this.getPlayerPokemon(); diff --git a/src/phases/load-move-anim-phase.ts b/src/phases/load-move-anim-phase.ts index c0b6cd58c54..c9b78797407 100644 --- a/src/phases/load-move-anim-phase.ts +++ b/src/phases/load-move-anim-phase.ts @@ -8,6 +8,7 @@ import { Phase } from "#app/phase"; * isn't already loaded (e.g. for Metronome) */ export class LoadMoveAnimPhase extends Phase { + public readonly phaseName = "LoadMoveAnimPhase"; constructor(protected moveId: MoveId) { super(); } diff --git a/src/phases/login-phase.ts b/src/phases/login-phase.ts index 673b94b1148..ec12b5ddaa4 100644 --- a/src/phases/login-phase.ts +++ b/src/phases/login-phase.ts @@ -11,6 +11,7 @@ import { SelectGenderPhase } from "./select-gender-phase"; import { UnavailablePhase } from "./unavailable-phase"; export class LoginPhase extends Phase { + public readonly phaseName = "LoginPhase"; private showText: boolean; constructor(showText = true) { diff --git a/src/phases/message-phase.ts b/src/phases/message-phase.ts index b277d67de82..335258abe5c 100644 --- a/src/phases/message-phase.ts +++ b/src/phases/message-phase.ts @@ -2,6 +2,7 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; export class MessagePhase extends Phase { + public readonly phaseName = "MessagePhase"; private text: string; private callbackDelay?: number | null; private prompt?: boolean | null; diff --git a/src/phases/modifier-reward-phase.ts b/src/phases/modifier-reward-phase.ts index c94c4deb819..83bd8704f59 100644 --- a/src/phases/modifier-reward-phase.ts +++ b/src/phases/modifier-reward-phase.ts @@ -5,6 +5,10 @@ import i18next from "i18next"; import { BattlePhase } from "./battle-phase"; export class ModifierRewardPhase extends BattlePhase { + // RibbonModifierRewardPhase extends ModifierRewardPhase and to make typescript happy + // we need to use a union type here + public readonly phaseName: "ModifierRewardPhase" | "RibbonModifierRewardPhase" | "GameOverModifierRewardPhase" = + "ModifierRewardPhase"; protected modifierType: ModifierType; constructor(modifierTypeFunc: ModifierTypeFunc) { diff --git a/src/phases/money-reward-phase.ts b/src/phases/money-reward-phase.ts index 708bb3a2fa8..52cb9ecb3ff 100644 --- a/src/phases/money-reward-phase.ts +++ b/src/phases/money-reward-phase.ts @@ -6,6 +6,7 @@ import { NumberHolder } from "#app/utils/common"; import { BattlePhase } from "./battle-phase"; export class MoneyRewardPhase extends BattlePhase { + public readonly phaseName = "MoneyRewardPhase"; private moneyMultiplier: number; constructor(moneyMultiplier: number) { diff --git a/src/phases/move-anim-phase.ts b/src/phases/move-anim-phase.ts index 830e72cb8be..383841a0146 100644 --- a/src/phases/move-anim-phase.ts +++ b/src/phases/move-anim-phase.ts @@ -5,6 +5,8 @@ import { Phase } from "#app/phase"; * Plays the given {@linkcode MoveAnim} sequentially. */ export class MoveAnimPhase extends Phase { + public readonly phaseName = "MoveAnimPhase"; + constructor( protected anim: Anim, protected onSubstitute = false, diff --git a/src/phases/move-charge-phase.ts b/src/phases/move-charge-phase.ts index ea43f1ddb88..789651623fa 100644 --- a/src/phases/move-charge-phase.ts +++ b/src/phases/move-charge-phase.ts @@ -9,13 +9,13 @@ import { BooleanHolder } from "#app/utils/common"; import { MovePhase } from "#app/phases/move-phase"; import { PokemonPhase } from "#app/phases/pokemon-phase"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; /** * Phase for the "charging turn" of two-turn moves (e.g. Dig). * @extends {@linkcode PokemonPhase} */ export class MoveChargePhase extends PokemonPhase { + public readonly phaseName = "MoveChargePhase"; /** The move instance that this phase applies */ public move: PokemonMove; /** The field index targeted by the move (Charging moves assume single target) */ @@ -62,7 +62,7 @@ export class MoveChargePhase extends PokemonPhase { if (instantCharge.value) { // this MoveEndPhase will be duplicated by the queued MovePhase if not removed - globalScene.tryRemovePhase(phase => phase instanceof MoveEndPhase && phase.getPokemon() === user); + globalScene.tryRemovePhase(phase => phase.is("MoveEndPhase") && phase.getPokemon() === user); // queue a new MovePhase for this move's attack phase globalScene.unshiftPhase(new MovePhase(user, [this.targetIndex], this.move, false)); } else { diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index 636f85f0f82..3160d848624 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -82,6 +82,7 @@ import { DamageAchv } from "#app/system/achv"; type HitCheckEntry = [HitCheckResult, TypeDamageMultiplier]; export class MoveEffectPhase extends PokemonPhase { + public readonly phaseName = "MoveEffectPhase"; public move: Move; private virtual = false; protected targets: BattlerIndex[]; diff --git a/src/phases/move-end-phase.ts b/src/phases/move-end-phase.ts index 037596dca59..6642b97773b 100644 --- a/src/phases/move-end-phase.ts +++ b/src/phases/move-end-phase.ts @@ -6,6 +6,7 @@ import { applyPostSummonAbAttrs, PostSummonRemoveEffectAbAttr } from "#app/data/ import type Pokemon from "#app/field/pokemon"; export class MoveEndPhase extends PokemonPhase { + public readonly phaseName = "MoveEndPhase"; private wasFollowUp: boolean; /** Targets from the preceding MovePhase */ diff --git a/src/phases/move-header-phase.ts b/src/phases/move-header-phase.ts index c320df462d1..50100e827d6 100644 --- a/src/phases/move-header-phase.ts +++ b/src/phases/move-header-phase.ts @@ -4,6 +4,7 @@ import type Pokemon from "#app/field/pokemon"; import { BattlePhase } from "./battle-phase"; export class MoveHeaderPhase extends BattlePhase { + public readonly phaseName = "MoveHeaderPhase"; public pokemon: Pokemon; public move: PokemonMove; diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 1ccf5b7957e..300c27e01fc 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -52,6 +52,7 @@ import { StatusEffect } from "#enums/status-effect"; import i18next from "i18next"; export class MovePhase extends BattlePhase { + public readonly phaseName = "MovePhase"; protected _pokemon: Pokemon; protected _move: PokemonMove; protected _targets: BattlerIndex[]; diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index fd0c4ef7949..5365ab3da32 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -6,7 +6,6 @@ import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-d import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; import { GameOverPhase } from "#app/phases/game-over-phase"; import { NewBattlePhase } from "#app/phases/new-battle-phase"; -import { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase"; import { ReturnPhase } from "#app/phases/return-phase"; import { ScanIvsPhase } from "#app/phases/scan-ivs-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; @@ -39,6 +38,7 @@ import { SelectBiomePhase } from "./select-biome-phase"; * - Queuing of the {@linkcode MysteryEncounterOptionSelectedPhase} */ export class MysteryEncounterPhase extends Phase { + public readonly phaseName = "MysteryEncounterPhase"; private readonly FIRST_DIALOGUE_PROMPT_DELAY = 300; optionSelectSettings?: OptionSelectSettings; @@ -180,6 +180,7 @@ export class MysteryEncounterPhase extends Phase { * Any phase that is meant to follow this one MUST be queued via the onOptionSelect() logic of the selected option */ export class MysteryEncounterOptionSelectedPhase extends Phase { + public readonly phaseName = "MysteryEncounterOptionSelectedPhase"; onOptionSelect: OptionPhaseCallback; constructor() { @@ -221,6 +222,7 @@ export class MysteryEncounterOptionSelectedPhase extends Phase { * See {@linkcode TurnEndPhase} for more details */ export class MysteryEncounterBattleStartCleanupPhase extends Phase { + public readonly phaseName = "MysteryEncounterBattleStartCleanupPhase"; /** * Cleans up `TURN_END` tags, any {@linkcode PostTurnStatusEffectPhase}s, checks for Pokemon switches, then continues */ @@ -245,8 +247,8 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase { }); // Remove any status tick phases - while (globalScene.findPhase(p => p instanceof PostTurnStatusEffectPhase)) { - globalScene.tryRemovePhase(p => p instanceof PostTurnStatusEffectPhase); + while (globalScene.findPhase(p => p.is("PostTurnStatusEffectPhase"))) { + globalScene.tryRemovePhase(p => p.is("PostTurnStatusEffectPhase")); } // The total number of Pokemon in the player's party that can legally fight @@ -284,6 +286,7 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase { * - Queue the {@linkcode SummonPhase}s, {@linkcode PostSummonPhase}s, etc., required to initialize the phase queue for a battle */ export class MysteryEncounterBattlePhase extends Phase { + public readonly phaseName = "MysteryEncounterBattlePhase"; disableSwitch: boolean; constructor(disableSwitch = false) { @@ -513,6 +516,7 @@ export class MysteryEncounterBattlePhase extends Phase { * - Queuing of the {@linkcode PostMysteryEncounterPhase} */ export class MysteryEncounterRewardsPhase extends Phase { + public readonly phaseName = "MysteryEncounterRewardsPhase"; addHealPhase: boolean; constructor(addHealPhase = false) { @@ -558,7 +562,7 @@ export class MysteryEncounterRewardsPhase extends Phase { if (encounter.doEncounterRewards) { encounter.doEncounterRewards(); } else if (this.addHealPhase) { - globalScene.tryRemovePhase(p => p instanceof SelectModifierPhase); + globalScene.tryRemovePhase(p => p.is("SelectModifierPhase")); globalScene.unshiftPhase( new SelectModifierPhase(0, undefined, { fillRemaining: false, @@ -580,6 +584,7 @@ export class MysteryEncounterRewardsPhase extends Phase { * - Queuing of the next wave */ export class PostMysteryEncounterPhase extends Phase { + public readonly phaseName = "PostMysteryEncounterPhase"; private readonly FIRST_DIALOGUE_PROMPT_DELAY = 750; onPostOptionSelect?: OptionPhaseCallback; diff --git a/src/phases/new-battle-phase.ts b/src/phases/new-battle-phase.ts index 09b8ab1d335..c4cfc72fb53 100644 --- a/src/phases/new-battle-phase.ts +++ b/src/phases/new-battle-phase.ts @@ -2,13 +2,14 @@ import { globalScene } from "#app/global-scene"; import { BattlePhase } from "./battle-phase"; export class NewBattlePhase extends BattlePhase { + public readonly phaseName = "NewBattlePhase"; start() { super.start(); // cull any extra `NewBattle` phases from the queue. - globalScene.phaseQueue = globalScene.phaseQueue.filter(phase => !(phase instanceof NewBattlePhase)); + globalScene.phaseQueue = globalScene.phaseQueue.filter(phase => !phase.is("NewBattlePhase")); // `phaseQueuePrepend` is private, so we have to use this inefficient loop. - while (globalScene.tryRemoveUnshiftedPhase(phase => phase instanceof NewBattlePhase)) {} + while (globalScene.tryRemoveUnshiftedPhase(phase => phase.is("NewBattlePhase"))) {} globalScene.newBattle(); diff --git a/src/phases/new-biome-encounter-phase.ts b/src/phases/new-biome-encounter-phase.ts index ef027bfd77a..29ba67cb797 100644 --- a/src/phases/new-biome-encounter-phase.ts +++ b/src/phases/new-biome-encounter-phase.ts @@ -4,6 +4,7 @@ import { getRandomWeatherType } from "#app/data/weather"; import { NextEncounterPhase } from "./next-encounter-phase"; export class NewBiomeEncounterPhase extends NextEncounterPhase { + public readonly phaseName = "NewBiomeEncounterPhase"; doEncounter(): void { globalScene.playBgm(undefined, true); diff --git a/src/phases/next-encounter-phase.ts b/src/phases/next-encounter-phase.ts index 30b4004363c..c31b4b5bbc3 100644 --- a/src/phases/next-encounter-phase.ts +++ b/src/phases/next-encounter-phase.ts @@ -6,6 +6,7 @@ import { EncounterPhase } from "./encounter-phase"; * Handles generating, loading and preparing for it. */ export class NextEncounterPhase extends EncounterPhase { + public readonly phaseName: "NextEncounterPhase" | "NewBiomeEncounterPhase" = "NextEncounterPhase"; start() { super.start(); } diff --git a/src/phases/obtain-status-effect-phase.ts b/src/phases/obtain-status-effect-phase.ts index 47cae2dcbf6..820db910681 100644 --- a/src/phases/obtain-status-effect-phase.ts +++ b/src/phases/obtain-status-effect-phase.ts @@ -11,6 +11,7 @@ import { applyPostSetStatusAbAttrs, PostSetStatusAbAttr } from "#app/data/abilit import { isNullOrUndefined } from "#app/utils/common"; export class ObtainStatusEffectPhase extends PokemonPhase { + public readonly phaseName = "ObtainStatusEffectPhase"; private statusEffect?: StatusEffect; private turnsRemaining?: number; private sourceText?: string | null; diff --git a/src/phases/party-exp-phase.ts b/src/phases/party-exp-phase.ts index 8fd9e1cf0f6..30fc97d9105 100644 --- a/src/phases/party-exp-phase.ts +++ b/src/phases/party-exp-phase.ts @@ -6,6 +6,7 @@ import { Phase } from "#app/phase"; * Intended to be used as a more 1-off phase to provide exp to the party (such as during MEs), rather than cleanup a battle entirely */ export class PartyExpPhase extends Phase { + public readonly phaseName = "PartyExpPhase"; expValue: number; useWaveIndexMultiplier?: boolean; pokemonParticipantIds?: Set; diff --git a/src/phases/party-heal-phase.ts b/src/phases/party-heal-phase.ts index 4a9f8a0c888..765c7dbad8e 100644 --- a/src/phases/party-heal-phase.ts +++ b/src/phases/party-heal-phase.ts @@ -3,6 +3,7 @@ import { fixedInt } from "#app/utils/common"; import { BattlePhase } from "./battle-phase"; export class PartyHealPhase extends BattlePhase { + public readonly phaseName = "PartyHealPhase"; private resumeBgm: boolean; constructor(resumeBgm: boolean) { diff --git a/src/phases/pokemon-anim-phase.ts b/src/phases/pokemon-anim-phase.ts index e9f0097459a..b1a21446996 100644 --- a/src/phases/pokemon-anim-phase.ts +++ b/src/phases/pokemon-anim-phase.ts @@ -7,6 +7,7 @@ import { PokemonAnimType } from "#enums/pokemon-anim-type"; import { SpeciesId } from "#enums/species-id"; export class PokemonAnimPhase extends BattlePhase { + public readonly phaseName = "PokemonAnimPhase"; /** The type of animation to play in this phase */ protected key: PokemonAnimType; /** The Pokemon to which this animation applies */ diff --git a/src/phases/pokemon-heal-phase.ts b/src/phases/pokemon-heal-phase.ts index 7cb013251f6..60bbb17c30a 100644 --- a/src/phases/pokemon-heal-phase.ts +++ b/src/phases/pokemon-heal-phase.ts @@ -14,6 +14,7 @@ import { BattlerTagType } from "#app/enums/battler-tag-type"; import type { HealBlockTag } from "#app/data/battler-tags"; export class PokemonHealPhase extends CommonAnimPhase { + public readonly phaseName = "PokemonHealPhase"; private hpHealed: number; private message: string | null; private showFullHpMessage: boolean; diff --git a/src/phases/pokemon-transform-phase.ts b/src/phases/pokemon-transform-phase.ts index 23a9a983bae..c0f3b048003 100644 --- a/src/phases/pokemon-transform-phase.ts +++ b/src/phases/pokemon-transform-phase.ts @@ -13,6 +13,7 @@ import i18next from "i18next"; * Used for Transform (move) and Imposter (ability) */ export class PokemonTransformPhase extends PokemonPhase { + public readonly phaseName = "PokemonTransformPhase"; protected targetIndex: BattlerIndex; private playSound: boolean; diff --git a/src/phases/post-game-over-phase.ts b/src/phases/post-game-over-phase.ts index 753251e992f..f985419da7a 100644 --- a/src/phases/post-game-over-phase.ts +++ b/src/phases/post-game-over-phase.ts @@ -4,6 +4,7 @@ import type { EndCardPhase } from "./end-card-phase"; import { TitlePhase } from "./title-phase"; export class PostGameOverPhase extends Phase { + public readonly phaseName = "PostGameOverPhase"; private endCardPhase?: EndCardPhase; constructor(endCardPhase?: EndCardPhase) { diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts index 446d45bb2fa..a7faf614292 100644 --- a/src/phases/post-summon-phase.ts +++ b/src/phases/post-summon-phase.ts @@ -7,6 +7,7 @@ import { MysteryEncounterPostSummonTag } from "#app/data/battler-tags"; import { BattlerTagType } from "#enums/battler-tag-type"; export class PostSummonPhase extends PokemonPhase { + public readonly phaseName = "PostSummonPhase"; start() { super.start(); diff --git a/src/phases/post-turn-status-effect-phase.ts b/src/phases/post-turn-status-effect-phase.ts index 9b530d48196..47a84059745 100644 --- a/src/phases/post-turn-status-effect-phase.ts +++ b/src/phases/post-turn-status-effect-phase.ts @@ -17,6 +17,7 @@ import { BooleanHolder, NumberHolder } from "#app/utils/common"; import { PokemonPhase } from "./pokemon-phase"; export class PostTurnStatusEffectPhase extends PokemonPhase { + public readonly phaseName = "PostTurnStatusEffectPhase"; // biome-ignore lint/complexity/noUselessConstructor: Not unnecessary as it makes battlerIndex required constructor(battlerIndex: BattlerIndex) { super(battlerIndex); diff --git a/src/phases/quiet-form-change-phase.ts b/src/phases/quiet-form-change-phase.ts index 76411f62f77..9f6b5cb3361 100644 --- a/src/phases/quiet-form-change-phase.ts +++ b/src/phases/quiet-form-change-phase.ts @@ -9,7 +9,7 @@ import type Pokemon from "#app/field/pokemon"; import { EnemyPokemon } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { BattlePhase } from "./battle-phase"; -import { MovePhase } from "./move-phase"; +import type { MovePhase } from "./move-phase"; import { PokemonHealPhase } from "./pokemon-heal-phase"; import { applyAbAttrs, @@ -19,6 +19,7 @@ import { } from "#app/data/abilities/ability"; export class QuietFormChangePhase extends BattlePhase { + public readonly phaseName = "QuietFormChangePhase"; protected pokemon: Pokemon; protected formChange: SpeciesFormChange; @@ -168,7 +169,7 @@ export class QuietFormChangePhase extends BattlePhase { this.pokemon.initBattleInfo(); this.pokemon.cry(); - const movePhase = globalScene.findPhase(p => p instanceof MovePhase && p.pokemon === this.pokemon) as MovePhase; + const movePhase = globalScene.findPhase(p => p.is("MovePhase") && p.pokemon === this.pokemon) as MovePhase; if (movePhase) { movePhase.cancel(); } diff --git a/src/phases/reload-session-phase.ts b/src/phases/reload-session-phase.ts index 8cd5f67b43a..ac9337753c4 100644 --- a/src/phases/reload-session-phase.ts +++ b/src/phases/reload-session-phase.ts @@ -4,6 +4,7 @@ import { UiMode } from "#enums/ui-mode"; import { fixedInt } from "#app/utils/common"; export class ReloadSessionPhase extends Phase { + public readonly phaseName = "ReloadSessionPhase"; private systemDataStr?: string; constructor(systemDataStr?: string) { diff --git a/src/phases/reset-status-phase.ts b/src/phases/reset-status-phase.ts index 19bfc3027e2..779f375d7e2 100644 --- a/src/phases/reset-status-phase.ts +++ b/src/phases/reset-status-phase.ts @@ -7,6 +7,7 @@ import { BattlePhase } from "#app/phases/battle-phase"; * This is necessary to perform in a phase primarly to ensure that the status icon disappears at the correct time in the battle */ export class ResetStatusPhase extends BattlePhase { + public readonly phaseName = "ResetStatusPhase"; private readonly pokemon: Pokemon; private readonly affectConfusion: boolean; private readonly reloadAssets: boolean; diff --git a/src/phases/return-phase.ts b/src/phases/return-phase.ts index 6dee982a4f0..6365256d40a 100644 --- a/src/phases/return-phase.ts +++ b/src/phases/return-phase.ts @@ -4,6 +4,7 @@ import { SwitchType } from "#enums/switch-type"; import { SwitchSummonPhase } from "./switch-summon-phase"; export class ReturnPhase extends SwitchSummonPhase { + public readonly phaseName = "ReturnPhase"; constructor(fieldIndex: number) { super(SwitchType.SWITCH, fieldIndex, -1, true); } diff --git a/src/phases/revival-blessing-phase.ts b/src/phases/revival-blessing-phase.ts index 428acaf9ed4..3f70c93dd7a 100644 --- a/src/phases/revival-blessing-phase.ts +++ b/src/phases/revival-blessing-phase.ts @@ -15,6 +15,7 @@ import type { PlayerPokemon } from "#app/field/pokemon"; * when used by one of the player's Pokemon. */ export class RevivalBlessingPhase extends BattlePhase { + public readonly phaseName = "RevivalBlessingPhase"; constructor(protected user: PlayerPokemon) { super(); } diff --git a/src/phases/ribbon-modifier-reward-phase.ts b/src/phases/ribbon-modifier-reward-phase.ts index 21114ab3de9..949f7af0302 100644 --- a/src/phases/ribbon-modifier-reward-phase.ts +++ b/src/phases/ribbon-modifier-reward-phase.ts @@ -6,6 +6,7 @@ import i18next from "i18next"; import { ModifierRewardPhase } from "./modifier-reward-phase"; export class RibbonModifierRewardPhase extends ModifierRewardPhase { + public readonly phaseName = "RibbonModifierRewardPhase"; private species: PokemonSpecies; constructor(modifierTypeFunc: ModifierTypeFunc, species: PokemonSpecies) { diff --git a/src/phases/scan-ivs-phase.ts b/src/phases/scan-ivs-phase.ts index d79a32bd47e..df68a2d1cab 100644 --- a/src/phases/scan-ivs-phase.ts +++ b/src/phases/scan-ivs-phase.ts @@ -8,6 +8,7 @@ import i18next from "i18next"; import { PokemonPhase } from "./pokemon-phase"; export class ScanIvsPhase extends PokemonPhase { + public readonly phaseName = "ScanIvsPhase"; // biome-ignore lint/complexity/noUselessConstructor: This changes `battlerIndex` to be required constructor(battlerIndex: BattlerIndex) { super(battlerIndex); diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index a7736b16811..ef6b39e8b8f 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -10,6 +10,7 @@ import { PartyHealPhase } from "./party-heal-phase"; import { SwitchBiomePhase } from "./switch-biome-phase"; export class SelectBiomePhase extends BattlePhase { + public readonly phaseName = "SelectBiomePhase"; start() { super.start(); diff --git a/src/phases/select-challenge-phase.ts b/src/phases/select-challenge-phase.ts index 76ac8a60c4f..dcf72d1b441 100644 --- a/src/phases/select-challenge-phase.ts +++ b/src/phases/select-challenge-phase.ts @@ -3,6 +3,7 @@ import { Phase } from "#app/phase"; import { UiMode } from "#enums/ui-mode"; export class SelectChallengePhase extends Phase { + public readonly phaseName = "SelectChallengePhase"; start() { super.start(); diff --git a/src/phases/select-gender-phase.ts b/src/phases/select-gender-phase.ts index a1171c1a5db..ad8515e312e 100644 --- a/src/phases/select-gender-phase.ts +++ b/src/phases/select-gender-phase.ts @@ -6,6 +6,7 @@ import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; export class SelectGenderPhase extends Phase { + public readonly phaseName = "SelectGenderPhase"; start(): void { super.start(); diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts index 5f11441333b..6e429d9ad7f 100644 --- a/src/phases/select-modifier-phase.ts +++ b/src/phases/select-modifier-phase.ts @@ -32,6 +32,7 @@ import type { CustomModifierSettings } from "#app/modifier/modifier-type"; import { isNullOrUndefined, NumberHolder } from "#app/utils/common"; export class SelectModifierPhase extends BattlePhase { + public readonly phaseName = "SelectModifierPhase"; private rerollCount: number; private modifierTiers?: ModifierTier[]; private customModifierSettings?: CustomModifierSettings; diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts index 6d333f4001c..d25c2dd7211 100644 --- a/src/phases/select-starter-phase.ts +++ b/src/phases/select-starter-phase.ts @@ -15,6 +15,7 @@ import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import { isNullOrUndefined } from "#app/utils/common"; export class SelectStarterPhase extends Phase { + public readonly phaseName = "SelectStarterPhase"; start() { super.start(); diff --git a/src/phases/select-target-phase.ts b/src/phases/select-target-phase.ts index f8a8ecfbf18..46ba378a56c 100644 --- a/src/phases/select-target-phase.ts +++ b/src/phases/select-target-phase.ts @@ -8,6 +8,7 @@ import i18next from "#app/plugins/i18n"; import { allMoves } from "#app/data/data-lists"; export class SelectTargetPhase extends PokemonPhase { + public readonly phaseName = "SelectTargetPhase"; // biome-ignore lint/complexity/noUselessConstructor: This makes `fieldIndex` required constructor(fieldIndex: number) { super(fieldIndex); diff --git a/src/phases/shiny-sparkle-phase.ts b/src/phases/shiny-sparkle-phase.ts index 87a7db29cf6..93d7dd67209 100644 --- a/src/phases/shiny-sparkle-phase.ts +++ b/src/phases/shiny-sparkle-phase.ts @@ -3,6 +3,7 @@ import type { BattlerIndex } from "#app/battle"; import { PokemonPhase } from "./pokemon-phase"; export class ShinySparklePhase extends PokemonPhase { + public readonly phaseName = "ShinySparklePhase"; // biome-ignore lint/complexity/noUselessConstructor: This makes `battlerIndex` required constructor(battlerIndex: BattlerIndex) { super(battlerIndex); diff --git a/src/phases/show-ability-phase.ts b/src/phases/show-ability-phase.ts index d6193ac3946..81aa69537e5 100644 --- a/src/phases/show-ability-phase.ts +++ b/src/phases/show-ability-phase.ts @@ -5,6 +5,7 @@ import { getPokemonNameWithAffix } from "#app/messages"; import { HideAbilityPhase } from "#app/phases/hide-ability-phase"; export class ShowAbilityPhase extends PokemonPhase { + public readonly phaseName = "ShowAbilityPhase"; private passive: boolean; private pokemonName: string; private abilityName: string; diff --git a/src/phases/show-party-exp-bar-phase.ts b/src/phases/show-party-exp-bar-phase.ts index 89bec6d8fdd..6b4236f0868 100644 --- a/src/phases/show-party-exp-bar-phase.ts +++ b/src/phases/show-party-exp-bar-phase.ts @@ -8,6 +8,7 @@ import { LevelUpPhase } from "./level-up-phase"; import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase { + public readonly phaseName = "ShowPartyExpBarPhase"; private expValue: number; constructor(partyMemberIndex: number, expValue: number) { diff --git a/src/phases/show-trainer-phase.ts b/src/phases/show-trainer-phase.ts index b6c1e345c70..bae6ecd839c 100644 --- a/src/phases/show-trainer-phase.ts +++ b/src/phases/show-trainer-phase.ts @@ -3,6 +3,7 @@ import { PlayerGender } from "#app/enums/player-gender"; import { BattlePhase } from "./battle-phase"; export class ShowTrainerPhase extends BattlePhase { + public readonly phaseName = "ShowTrainerPhase"; start() { super.start(); diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index 6731e45025c..baa93c63099 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -31,6 +31,7 @@ export type StatStageChangeCallback = ( ) => void; export class StatStageChangePhase extends PokemonPhase { + public readonly phaseName = "StatStageChangePhase"; private stats: BattleStat[]; private selfTarget: boolean; private stages: number; @@ -235,9 +236,9 @@ export class StatStageChangePhase extends PokemonPhase { // Look for any other stat change phases; if this is the last one, do White Herb check const existingPhase = globalScene.findPhase( - p => p instanceof StatStageChangePhase && p.battlerIndex === this.battlerIndex, + p => p.is("StatStageChangePhase") && p.battlerIndex === this.battlerIndex, ); - if (!(existingPhase instanceof StatStageChangePhase)) { + if (!existingPhase?.is("StatStageChangePhase")) { // Apply White Herb if needed const whiteHerb = globalScene.applyModifier( ResetNegativeStatStageModifier, @@ -316,7 +317,7 @@ export class StatStageChangePhase extends PokemonPhase { while ( (existingPhase = globalScene.findPhase( p => - p instanceof StatStageChangePhase && + p.is("StatStageChangePhase") && p.battlerIndex === this.battlerIndex && p.stats.length === 1 && p.stats[0] === this.stats[0] && @@ -335,7 +336,7 @@ export class StatStageChangePhase extends PokemonPhase { while ( (existingPhase = globalScene.findPhase( p => - p instanceof StatStageChangePhase && + p.is("StatStageChangePhase") && p.battlerIndex === this.battlerIndex && p.selfTarget === this.selfTarget && accEva.some(s => p.stats.includes(s)) === isAccEva && diff --git a/src/phases/summon-missing-phase.ts b/src/phases/summon-missing-phase.ts index a692455ce47..ce3e982055e 100644 --- a/src/phases/summon-missing-phase.ts +++ b/src/phases/summon-missing-phase.ts @@ -4,6 +4,7 @@ import { SummonPhase } from "./summon-phase"; import { globalScene } from "#app/global-scene"; export class SummonMissingPhase extends SummonPhase { + public readonly phaseName = "SummonMissingPhase"; preSummon(): void { globalScene.ui.showText( i18next.t("battle:sendOutPokemon", { diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index c217583f163..2cd7b122bb3 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -17,6 +17,8 @@ import { applyPreSummonAbAttrs, PreSummonAbAttr } from "#app/data/abilities/abil import { globalScene } from "#app/global-scene"; export class SummonPhase extends PartyMemberPokemonPhase { + // The union type is needed to keep typescript happy as these phases extend from SummonPhase + public readonly phaseName: "SummonPhase" | "SummonMissingPhase" | "SwitchSummonPhase" | "ReturnPhase" = "SummonPhase"; private loaded: boolean; constructor(fieldIndex: number, player = true, loaded = false) { diff --git a/src/phases/switch-biome-phase.ts b/src/phases/switch-biome-phase.ts index 69a6c97cd9a..f84f1d517b4 100644 --- a/src/phases/switch-biome-phase.ts +++ b/src/phases/switch-biome-phase.ts @@ -4,6 +4,7 @@ import { getBiomeKey } from "#app/field/arena"; import { BattlePhase } from "./battle-phase"; export class SwitchBiomePhase extends BattlePhase { + public readonly phaseName = "SwitchBiomePhase"; private nextBiome: BiomeId; constructor(nextBiome: BiomeId) { diff --git a/src/phases/switch-phase.ts b/src/phases/switch-phase.ts index c056b186021..6017aa0fa70 100644 --- a/src/phases/switch-phase.ts +++ b/src/phases/switch-phase.ts @@ -3,7 +3,6 @@ import PartyUiHandler, { PartyOption, PartyUiMode } from "#app/ui/party-ui-handl import { UiMode } from "#enums/ui-mode"; import { SwitchType } from "#enums/switch-type"; import { BattlePhase } from "./battle-phase"; -import { PostSummonPhase } from "./post-summon-phase"; import { SwitchSummonPhase } from "./switch-summon-phase"; /** @@ -11,6 +10,7 @@ import { SwitchSummonPhase } from "./switch-summon-phase"; * for the player (if a switch would be valid for the current battle state). */ export class SwitchPhase extends BattlePhase { + public readonly phaseName = "SwitchPhase"; protected readonly fieldIndex: number; private readonly switchType: SwitchType; private readonly isModal: boolean; @@ -76,7 +76,7 @@ export class SwitchPhase extends BattlePhase { if (slotIndex >= globalScene.currentBattle.getBattlerCount() && slotIndex < 6) { // Remove any pre-existing PostSummonPhase under the same field index. // Pre-existing PostSummonPhases may occur when this phase is invoked during a prompt to switch at the start of a wave. - globalScene.tryRemovePhase(p => p instanceof PostSummonPhase && p.player && p.fieldIndex === this.fieldIndex); + globalScene.tryRemovePhase(p => p.is("PostSummonPhase") && p.player && p.fieldIndex === this.fieldIndex); const switchType = option === PartyOption.PASS_BATON ? SwitchType.BATON_PASS : this.switchType; globalScene.unshiftPhase(new SwitchSummonPhase(switchType, fieldIndex, slotIndex, this.doReturn)); } diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index 6bdbb66be14..d81ca6029c5 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -22,6 +22,7 @@ import { SubstituteTag } from "#app/data/battler-tags"; import { SwitchType } from "#enums/switch-type"; export class SwitchSummonPhase extends SummonPhase { + public readonly phaseName: "SwitchSummonPhase" | "ReturnPhase" = "SwitchSummonPhase"; private readonly switchType: SwitchType; private readonly slotIndex: number; private readonly doReturn: boolean; diff --git a/src/phases/tera-phase.ts b/src/phases/tera-phase.ts index c9320daf12f..5e4ea2fe54e 100644 --- a/src/phases/tera-phase.ts +++ b/src/phases/tera-phase.ts @@ -9,6 +9,7 @@ import { SpeciesFormChangeTeraTrigger } from "#app/data/pokemon-forms"; import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims"; export class TeraPhase extends BattlePhase { + public readonly phaseName = "TeraPhase"; public pokemon: Pokemon; constructor(pokemon: Pokemon) { diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index 56057c23372..aa9ae49ca8b 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -29,6 +29,7 @@ import { globalScene } from "#app/global-scene"; import Overrides from "#app/overrides"; export class TitlePhase extends Phase { + public readonly phaseName = "TitlePhase"; private loaded = false; private lastSessionData: SessionSaveData; public gameMode: GameModes; diff --git a/src/phases/toggle-double-position-phase.ts b/src/phases/toggle-double-position-phase.ts index 37f47d5cf95..a6b8705f580 100644 --- a/src/phases/toggle-double-position-phase.ts +++ b/src/phases/toggle-double-position-phase.ts @@ -3,6 +3,7 @@ import { FieldPosition } from "#app/field/pokemon"; import { BattlePhase } from "./battle-phase"; export class ToggleDoublePositionPhase extends BattlePhase { + public readonly phaseName = "ToggleDoublePositionPhase"; private double: boolean; constructor(double: boolean) { diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts index daf5c38e57b..bd035248530 100644 --- a/src/phases/trainer-victory-phase.ts +++ b/src/phases/trainer-victory-phase.ts @@ -14,6 +14,7 @@ import { achvs } from "#app/system/achv"; import { timedEventManager } from "#app/global-event-manager"; export class TrainerVictoryPhase extends BattlePhase { + public readonly phaseName = "TrainerVictoryPhase"; start() { globalScene.disableMenu = true; diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index 756c497802b..4d486c4bbd6 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -18,6 +18,7 @@ import { PokemonHealPhase } from "./pokemon-heal-phase"; import { globalScene } from "#app/global-scene"; export class TurnEndPhase extends FieldPhase { + public readonly phaseName = "TurnEndPhase"; start() { super.start(); diff --git a/src/phases/turn-init-phase.ts b/src/phases/turn-init-phase.ts index 0c110024af7..7f94acd3b32 100644 --- a/src/phases/turn-init-phase.ts +++ b/src/phases/turn-init-phase.ts @@ -15,6 +15,7 @@ import { TurnStartPhase } from "./turn-start-phase"; import { globalScene } from "#app/global-scene"; export class TurnInitPhase extends FieldPhase { + public readonly phaseName = "TurnInitPhase"; start() { super.start(); diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index a02d869af10..2d009b30bf3 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -25,6 +25,7 @@ import { globalScene } from "#app/global-scene"; import { TeraPhase } from "./tera-phase"; export class TurnStartPhase extends FieldPhase { + public readonly phaseName = "TurnStartPhase"; /** * This orders the active Pokemon on the field by speed into an BattlerIndex array and returns that array. * It also checks for Trick Room and reverses the array if it is present. diff --git a/src/phases/unavailable-phase.ts b/src/phases/unavailable-phase.ts index e5f1d899191..4c4333ceb90 100644 --- a/src/phases/unavailable-phase.ts +++ b/src/phases/unavailable-phase.ts @@ -4,6 +4,7 @@ import { UiMode } from "#enums/ui-mode"; import { LoginPhase } from "./login-phase"; export class UnavailablePhase extends Phase { + public readonly phaseName = "UnavailablePhase"; start(): void { globalScene.ui.setMode(UiMode.UNAVAILABLE, () => { globalScene.unshiftPhase(new LoginPhase(true)); diff --git a/src/phases/unlock-phase.ts b/src/phases/unlock-phase.ts index 7a69fc207bb..839ac31dc5d 100644 --- a/src/phases/unlock-phase.ts +++ b/src/phases/unlock-phase.ts @@ -6,6 +6,7 @@ import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; export class UnlockPhase extends Phase { + public readonly phaseName = "UnlockPhase"; private unlockable: Unlockables; constructor(unlockable: Unlockables) { diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index 1204877fec2..2d21f8abc08 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -18,6 +18,7 @@ import { timedEventManager } from "#app/global-event-manager"; import { SelectBiomePhase } from "./select-biome-phase"; export class VictoryPhase extends PokemonPhase { + public readonly phaseName = "VictoryPhase"; /** If true, indicates that the phase is intended for EXP purposes only, and not to continue a battle to next phase */ isExpOnly: boolean; diff --git a/src/phases/weather-effect-phase.ts b/src/phases/weather-effect-phase.ts index d89c78e96c7..cd91b89771c 100644 --- a/src/phases/weather-effect-phase.ts +++ b/src/phases/weather-effect-phase.ts @@ -19,6 +19,7 @@ import { BooleanHolder, toDmgValue } from "#app/utils/common"; import { CommonAnimPhase } from "./common-anim-phase"; export class WeatherEffectPhase extends CommonAnimPhase { + public readonly phaseName = "WeatherEffectPhase"; public weather: Weather | null; constructor() { diff --git a/src/ui/command-ui-handler.ts b/src/ui/command-ui-handler.ts index fbfd4d2623b..0d67fdea624 100644 --- a/src/ui/command-ui-handler.ts +++ b/src/ui/command-ui-handler.ts @@ -5,7 +5,7 @@ import UiHandler from "./ui-handler"; import i18next from "i18next"; import { Button } from "#enums/buttons"; import { getPokemonNameWithAffix } from "#app/messages"; -import { CommandPhase } from "#app/phases/command-phase"; +import type { CommandPhase } from "#app/phases/command-phase"; import { globalScene } from "#app/global-scene"; import { TerastallizeAccessModifier } from "#app/modifier/modifier"; import { PokemonType } from "#enums/pokemon-type"; @@ -75,7 +75,7 @@ export default class CommandUiHandler extends UiHandler { let commandPhase: CommandPhase; const currentPhase = globalScene.getCurrentPhase(); - if (currentPhase instanceof CommandPhase) { + if (currentPhase?.is("CommandPhase")) { commandPhase = currentPhase; } else { commandPhase = globalScene.getStandbyPhase() as CommandPhase; diff --git a/src/ui/egg-hatch-scene-handler.ts b/src/ui/egg-hatch-scene-handler.ts index 76e2c54f4b6..b2863a213f8 100644 --- a/src/ui/egg-hatch-scene-handler.ts +++ b/src/ui/egg-hatch-scene-handler.ts @@ -1,7 +1,6 @@ import { UiMode } from "#enums/ui-mode"; import UiHandler from "./ui-handler"; import { Button } from "#enums/buttons"; -import { EggHatchPhase } from "#app/phases/egg-hatch-phase"; import { globalScene } from "#app/global-scene"; export default class EggHatchSceneHandler extends UiHandler { @@ -46,7 +45,7 @@ export default class EggHatchSceneHandler extends UiHandler { processInput(button: Button): boolean { if (button === Button.ACTION || button === Button.CANCEL) { const phase = globalScene.getCurrentPhase(); - if (phase instanceof EggHatchPhase && phase.trySkip()) { + if (phase?.is("EggHatchPhase") && phase.trySkip()) { return true; } } diff --git a/src/ui/egg-summary-ui-handler.ts b/src/ui/egg-summary-ui-handler.ts index ddc536fe1ad..ed2506ba0c1 100644 --- a/src/ui/egg-summary-ui-handler.ts +++ b/src/ui/egg-summary-ui-handler.ts @@ -4,7 +4,6 @@ import MessageUiHandler from "./message-ui-handler"; import { getEggTierForSpecies } from "../data/egg"; import { Button } from "#enums/buttons"; import PokemonHatchInfoContainer from "./pokemon-hatch-info-container"; -import { EggSummaryPhase } from "#app/phases/egg-summary-phase"; import type { EggHatchData } from "#app/data/egg-hatch-data"; import ScrollableGridUiHandler from "./scrollable-grid-handler"; import { HatchedPokemonContainer } from "./hatched-pokemon-container"; @@ -223,7 +222,7 @@ export default class EggSummaryUiHandler extends MessageUiHandler { if (button === Button.CANCEL) { if (!this.blockExit) { const phase = globalScene.getCurrentPhase(); - if (phase instanceof EggSummaryPhase) { + if (phase?.is("EggSummaryPhase")) { phase.end(); } success = true; diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index cc684111617..b6c3a9d7b8e 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -15,7 +15,6 @@ import { Button } from "#enums/buttons"; import { GameDataType } from "#enums/game-data-type"; import BgmBar from "#app/ui/bgm-bar"; import type AwaitableUiHandler from "./awaitable-ui-handler"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import { AdminMode, getAdminModeName } from "./admin-ui-handler"; import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; @@ -126,7 +125,7 @@ export default class MenuUiHandler extends MessageUiHandler { const ui = this.getUi(); this.excludedMenus = () => [ { - condition: globalScene.getCurrentPhase() instanceof SelectModifierPhase, + condition: !!globalScene.getCurrentPhase()?.is("SelectModifierPhase"), options: [MenuOptions.EGG_GACHA], }, { condition: bypassLogin, options: [MenuOptions.LOG_OUT] }, diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 452ffcf5192..e26ecb531e9 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -29,7 +29,6 @@ import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { getPokemonNameWithAffix } from "#app/messages"; import type { CommandPhase } from "#app/phases/command-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import { globalScene } from "#app/global-scene"; const defaultMessage = i18next.t("partyUiHandler:choosePokemon"); @@ -751,7 +750,7 @@ export default class PartyUiHandler extends MessageUiHandler { // TODO: This risks hitting the other options (.MOVE_i and ALL) so does it? Do we need an extra check? if ( option >= PartyOption.FORM_CHANGE_ITEM && - globalScene.getCurrentPhase() instanceof SelectModifierPhase && + globalScene.getCurrentPhase()?.is("SelectModifierPhase") && this.partyUiMode === PartyUiMode.CHECK ) { const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); @@ -1338,7 +1337,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.addCommonOptions(pokemon); break; case PartyUiMode.CHECK: - if (globalScene.getCurrentPhase() instanceof SelectModifierPhase) { + if (globalScene.getCurrentPhase()?.is("SelectModifierPhase")) { const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); for (let i = 0; i < formChangeItemModifiers.length; i++) { this.options.push(PartyOption.FORM_CHANGE_ITEM + i); diff --git a/src/ui/pokedex-page-ui-handler.ts b/src/ui/pokedex-page-ui-handler.ts index 263842bd4f9..00e166f075d 100644 --- a/src/ui/pokedex-page-ui-handler.ts +++ b/src/ui/pokedex-page-ui-handler.ts @@ -665,7 +665,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { show(args: any[]): boolean { // Allow the use of candies if we are in one of the whitelisted phases this.canUseCandies = ["TitlePhase", "SelectStarterPhase", "CommandPhase"].includes( - globalScene.getCurrentPhase()?.constructor.name ?? "", + globalScene.getCurrentPhase()?.phaseName ?? "", ); if (args.length >= 1 && args[0] === "refresh") { diff --git a/test/testUtils/phaseInterceptor.ts b/test/testUtils/phaseInterceptor.ts index b1d76ecd4a6..b7577550568 100644 --- a/test/testUtils/phaseInterceptor.ts +++ b/test/testUtils/phaseInterceptor.ts @@ -63,6 +63,8 @@ import { UnlockPhase } from "#app/phases/unlock-phase"; import { PostGameOverPhase } from "#app/phases/post-game-over-phase"; import { RevivalBlessingPhase } from "#app/phases/revival-blessing-phase"; +import type { PhaseClass, PhaseString } from "#app/@types/phase-types"; + export interface PromptHandler { phaseTarget?: string; mode?: UiMode; @@ -71,126 +73,6 @@ export interface PromptHandler { awaitingActionInput?: boolean; } -type PhaseClass = - | typeof LoginPhase - | typeof TitlePhase - | typeof SelectGenderPhase - | typeof NewBiomeEncounterPhase - | typeof SelectStarterPhase - | typeof PostSummonPhase - | typeof SummonPhase - | typeof ToggleDoublePositionPhase - | typeof CheckSwitchPhase - | typeof ShowAbilityPhase - | typeof MessagePhase - | typeof TurnInitPhase - | typeof CommandPhase - | typeof EnemyCommandPhase - | typeof TurnStartPhase - | typeof MovePhase - | typeof MoveEffectPhase - | typeof DamageAnimPhase - | typeof FaintPhase - | typeof BerryPhase - | typeof TurnEndPhase - | typeof BattleEndPhase - | typeof EggLapsePhase - | typeof SelectModifierPhase - | typeof NextEncounterPhase - | typeof NewBattlePhase - | typeof VictoryPhase - | typeof LearnMovePhase - | typeof MoveEndPhase - | typeof StatStageChangePhase - | typeof ShinySparklePhase - | typeof SelectTargetPhase - | typeof UnavailablePhase - | typeof QuietFormChangePhase - | typeof SwitchPhase - | typeof SwitchSummonPhase - | typeof PartyHealPhase - | typeof FormChangePhase - | typeof EvolutionPhase - | typeof EndEvolutionPhase - | typeof LevelCapPhase - | typeof AttemptRunPhase - | typeof SelectBiomePhase - | typeof MysteryEncounterPhase - | typeof MysteryEncounterOptionSelectedPhase - | typeof MysteryEncounterBattlePhase - | typeof MysteryEncounterRewardsPhase - | typeof PostMysteryEncounterPhase - | typeof RibbonModifierRewardPhase - | typeof GameOverModifierRewardPhase - | typeof ModifierRewardPhase - | typeof PartyExpPhase - | typeof ExpPhase - | typeof EncounterPhase - | typeof GameOverPhase - | typeof UnlockPhase - | typeof PostGameOverPhase - | typeof RevivalBlessingPhase; - -type PhaseString = - | "LoginPhase" - | "TitlePhase" - | "SelectGenderPhase" - | "NewBiomeEncounterPhase" - | "SelectStarterPhase" - | "PostSummonPhase" - | "SummonPhase" - | "ToggleDoublePositionPhase" - | "CheckSwitchPhase" - | "ShowAbilityPhase" - | "MessagePhase" - | "TurnInitPhase" - | "CommandPhase" - | "EnemyCommandPhase" - | "TurnStartPhase" - | "MovePhase" - | "MoveEffectPhase" - | "DamageAnimPhase" - | "FaintPhase" - | "BerryPhase" - | "TurnEndPhase" - | "BattleEndPhase" - | "EggLapsePhase" - | "SelectModifierPhase" - | "NextEncounterPhase" - | "NewBattlePhase" - | "VictoryPhase" - | "LearnMovePhase" - | "MoveEndPhase" - | "StatStageChangePhase" - | "ShinySparklePhase" - | "SelectTargetPhase" - | "UnavailablePhase" - | "QuietFormChangePhase" - | "SwitchPhase" - | "SwitchSummonPhase" - | "PartyHealPhase" - | "FormChangePhase" - | "EvolutionPhase" - | "EndEvolutionPhase" - | "LevelCapPhase" - | "AttemptRunPhase" - | "SelectBiomePhase" - | "MysteryEncounterPhase" - | "MysteryEncounterOptionSelectedPhase" - | "MysteryEncounterBattlePhase" - | "MysteryEncounterRewardsPhase" - | "PostMysteryEncounterPhase" - | "RibbonModifierRewardPhase" - | "GameOverModifierRewardPhase" - | "ModifierRewardPhase" - | "PartyExpPhase" - | "ExpPhase" - | "EncounterPhase" - | "GameOverPhase" - | "UnlockPhase" - | "PostGameOverPhase" - | "RevivalBlessingPhase"; - type PhaseInterceptorPhase = PhaseClass | PhaseString; export default class PhaseInterceptor { From 855868bfea4206bda9a027d6945fb3bd0f9c78dd Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Fri, 6 Jun 2025 16:15:13 -0500 Subject: [PATCH 09/23] [Refactor] Cleanup achvs ui handler (#5919) --- src/system/game-data.ts | 4 +- src/ui/achvs-ui-handler.ts | 464 ++++++++++++++++++++----------------- 2 files changed, 254 insertions(+), 214 deletions(-) diff --git a/src/system/game-data.ts b/src/system/game-data.ts index bc74ab15930..ab907d2f768 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -179,11 +179,11 @@ interface Unlocks { [key: number]: boolean; } -interface AchvUnlocks { +export interface AchvUnlocks { [key: string]: number; } -interface VoucherUnlocks { +export interface VoucherUnlocks { [key: string]: number; } diff --git a/src/ui/achvs-ui-handler.ts b/src/ui/achvs-ui-handler.ts index d0c8b716c7a..8588530d370 100644 --- a/src/ui/achvs-ui-handler.ts +++ b/src/ui/achvs-ui-handler.ts @@ -11,20 +11,20 @@ import { addWindow } from "#app/ui/ui-theme"; import { ScrollBar } from "#app/ui/scroll-bar"; import { PlayerGender } from "#enums/player-gender"; import { globalScene } from "#app/global-scene"; +import type { AchvUnlocks, VoucherUnlocks } from "#app/system/game-data"; -enum Page { - ACHIEVEMENTS, - VOUCHERS, -} +const Page = { + ACHIEVEMENTS: 0, + VOUCHERS: 1, +} as const; +type Page = (typeof Page)[keyof typeof Page]; interface LanguageSetting { TextSize: string; } const languageSettings: { [key: string]: LanguageSetting } = { - de: { - TextSize: "80px", - }, + de: { TextSize: "80px" }, }; export default class AchvsUiHandler extends MessageUiHandler { @@ -70,44 +70,35 @@ export default class AchvsUiHandler extends MessageUiHandler { setup() { const ui = this.getUi(); - this.mainContainer = globalScene.add.container(1, -(globalScene.game.canvas.height / 6) + 1); + /** Width of the global canvas / 6 */ + const WIDTH = globalScene.game.canvas.width / 6; + /** Height of the global canvas / 6 */ + const HEIGHT = globalScene.game.canvas.height / 6; - this.mainContainer.setInteractive( - new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width / 6, globalScene.game.canvas.height / 6), - Phaser.Geom.Rectangle.Contains, - ); + this.mainContainer = globalScene.add.container(1, -HEIGHT + 1); - this.headerBg = addWindow(0, 0, globalScene.game.canvas.width / 6 - 2, 24); - this.headerBg.setOrigin(0, 0); + this.mainContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, WIDTH, HEIGHT), Phaser.Geom.Rectangle.Contains); - this.headerText = addTextObject(0, 0, "", TextStyle.SETTINGS_LABEL); - this.headerText.setOrigin(0, 0); - this.headerText.setPositionRelative(this.headerBg, 8, 4); - this.headerActionButton = new Phaser.GameObjects.Sprite(globalScene, 0, 0, "keyboard", "ACTION.png"); - this.headerActionButton.setOrigin(0, 0); - this.headerActionButton.setPositionRelative(this.headerBg, 236, 6); - this.headerActionText = addTextObject(0, 0, "", TextStyle.WINDOW, { - fontSize: "60px", - }); - this.headerActionText.setOrigin(0, 0); - this.headerActionText.setPositionRelative(this.headerBg, 264, 8); + this.headerBg = addWindow(0, 0, WIDTH - 2, 24); + + this.headerText = addTextObject(0, 0, "", TextStyle.SETTINGS_LABEL) + .setOrigin(0) + .setPositionRelative(this.headerBg, 8, 4); + this.headerActionButton = new Phaser.GameObjects.Sprite(globalScene, 0, 0, "keyboard", "ACTION.png") + .setOrigin(0) + .setPositionRelative(this.headerBg, 236, 6); + this.headerActionText = addTextObject(0, 0, "", TextStyle.WINDOW, { fontSize: "60px" }) + .setOrigin(0) + .setPositionRelative(this.headerBg, 264, 8); // We need to get the player gender from the game data to add the correct prefix to the achievement name const genderIndex = globalScene.gameData.gender ?? PlayerGender.MALE; const genderStr = PlayerGender[genderIndex].toLowerCase(); - this.achvsName = i18next.t("achv:Achievements.name", { - context: genderStr, - }); + this.achvsName = i18next.t("achv:Achievements.name", { context: genderStr }); this.vouchersName = i18next.t("voucher:vouchers"); - this.iconsBg = addWindow( - 0, - this.headerBg.height, - globalScene.game.canvas.width / 6 - 2, - globalScene.game.canvas.height / 6 - this.headerBg.height - 68, - ); - this.iconsBg.setOrigin(0, 0); + this.iconsBg = addWindow(0, this.headerBg.height, WIDTH - 2, HEIGHT - this.headerBg.height - 68).setOrigin(0); const yOffset = 6; this.scrollBar = new ScrollBar( @@ -126,68 +117,59 @@ export default class AchvsUiHandler extends MessageUiHandler { const x = (a % this.COLS) * 18; const y = Math.floor(a / this.COLS) * 18; - const icon = globalScene.add.sprite(x, y, "items", "unknown"); - icon.setOrigin(0, 0); - icon.setScale(0.5); + const icon = globalScene.add.sprite(x, y, "items", "unknown").setOrigin(0).setScale(0.5); this.icons.push(icon); this.iconsContainer.add(icon); } const titleBg = addWindow(0, this.headerBg.height + this.iconsBg.height, 174, 24); - titleBg.setOrigin(0, 0); this.titleBg = titleBg; - this.titleText = addTextObject(0, 0, "", TextStyle.WINDOW); + this.titleText = addTextObject(0, 0, "", TextStyle.WINDOW).setOrigin(); const textSize = languageSettings[i18next.language]?.TextSize ?? this.titleText.style.fontSize; this.titleText.setFontSize(textSize); const titleBgCenterX = titleBg.x + titleBg.width / 2; const titleBgCenterY = titleBg.y + titleBg.height / 2; - this.titleText.setOrigin(0.5, 0.5); this.titleText.setPosition(titleBgCenterX, titleBgCenterY); this.scoreContainer = globalScene.add.container(titleBg.x + titleBg.width, titleBg.y); const scoreBg = addWindow(0, 0, 46, 24); - scoreBg.setOrigin(0, 0); - this.scoreContainer.add(scoreBg); - this.scoreText = addTextObject(scoreBg.width / 2, scoreBg.height / 2, "", TextStyle.WINDOW); - this.scoreText.setOrigin(0.5, 0.5); - this.scoreContainer.add(this.scoreText); + this.scoreText = addTextObject(scoreBg.width / 2, scoreBg.height / 2, "", TextStyle.WINDOW).setOrigin(); + this.scoreContainer.add([scoreBg, this.scoreText]); const unlockBg = addWindow(this.scoreContainer.x + scoreBg.width, titleBg.y, 98, 24); - unlockBg.setOrigin(0, 0); - this.unlockText = addTextObject(0, 0, "", TextStyle.WINDOW); - this.unlockText.setOrigin(0.5, 0.5); - this.unlockText.setPositionRelative(unlockBg, unlockBg.width / 2, unlockBg.height / 2); + this.unlockText = addTextObject(0, 0, "", TextStyle.WINDOW) + .setPositionRelative(unlockBg, unlockBg.width / 2, unlockBg.height / 2) + .setOrigin(); - const descriptionBg = addWindow(0, titleBg.y + titleBg.height, globalScene.game.canvas.width / 6 - 2, 42); - descriptionBg.setOrigin(0, 0); + const descriptionBg = addWindow(0, titleBg.y + titleBg.height, WIDTH - 2, 42); - const descriptionText = addTextObject(0, 0, "", TextStyle.WINDOW, { - maxLines: 2, - }); - descriptionText.setWordWrapWidth(1870); - descriptionText.setOrigin(0, 0); - descriptionText.setPositionRelative(descriptionBg, 8, 4); + const descriptionText = addTextObject(0, 0, "", TextStyle.WINDOW, { maxLines: 2 }) + .setWordWrapWidth(1870) + .setOrigin(0) + .setPositionRelative(descriptionBg, 8, 4); this.message = descriptionText; - this.mainContainer.add(this.headerBg); - this.mainContainer.add(this.headerActionButton); - this.mainContainer.add(this.headerText); - this.mainContainer.add(this.headerActionText); - this.mainContainer.add(this.iconsBg); - this.mainContainer.add(this.scrollBar); - this.mainContainer.add(this.iconsContainer); - this.mainContainer.add(titleBg); - this.mainContainer.add(this.titleText); - this.mainContainer.add(this.scoreContainer); - this.mainContainer.add(unlockBg); - this.mainContainer.add(this.unlockText); - this.mainContainer.add(descriptionBg); - this.mainContainer.add(descriptionText); + this.mainContainer.add([ + this.headerBg, + this.headerActionButton, + this.headerText, + this.headerActionText, + this.iconsBg, + this.scrollBar, + this.iconsContainer, + titleBg, + this.titleText, + this.scoreContainer, + unlockBg, + this.unlockText, + descriptionBg, + descriptionText, + ]); ui.add(this.mainContainer); @@ -246,87 +228,132 @@ export default class AchvsUiHandler extends MessageUiHandler { ); } - processInput(button: Button): boolean { - const ui = this.getUi(); + // #region Input Processing + /** + * Submethod of {@linkcode processInput} that handles the action button input + * @returns Whether the success sound should be played + */ + private processActionInput(): true { + this.setScrollCursor(0); + if (this.currentPage === Page.ACHIEVEMENTS) { + this.currentPage = Page.VOUCHERS; + this.updateVoucherIcons(); + } else if (this.currentPage === Page.VOUCHERS) { + this.currentPage = Page.ACHIEVEMENTS; + this.updateAchvIcons(); + } + this.setCursor(0, true); + this.scrollBar.setTotalRows(Math.ceil(this.currentTotal / this.COLS)); + this.scrollBar.setScrollCursor(0); + this.mainContainer.update(); + return true; + } + /** + * Submethod of {@linkcode processInput} that handles the up button input + * @returns Whether the success sound should be played + */ + private processUpInput(): boolean { + if (this.cursor >= this.COLS) { + return this.setCursor(this.cursor - this.COLS); + } + if (this.scrollCursor) { + return this.setScrollCursor(this.scrollCursor - 1); + } + + // Wrap around to the last row + const success = this.setScrollCursor(Math.ceil(this.currentTotal / this.COLS) - this.ROWS); + let newCursorIndex = this.cursor + (this.ROWS - 1) * this.COLS; + if (newCursorIndex > this.currentTotal - this.scrollCursor * this.COLS - 1) { + newCursorIndex -= this.COLS; + } + return success && this.setCursor(newCursorIndex); + } + + /** + * Submethod of {@linkcode processInput} that handles the down button input + * @returns Whether the success sound should be played + */ + private processDownInput(): boolean { + const rowIndex = Math.floor(this.cursor / this.COLS); + const itemOffset = this.scrollCursor * this.COLS; + const canMoveDown = itemOffset + 1 < this.currentTotal; + + if (rowIndex >= this.ROWS - 1) { + if (this.scrollCursor < Math.ceil(this.currentTotal / this.COLS) - this.ROWS && canMoveDown) { + // scroll down one row + return this.setScrollCursor(this.scrollCursor + 1); + } + // wrap back to the first row + return this.setScrollCursor(0) && this.setCursor(this.cursor % this.COLS); + } + if (canMoveDown) { + return this.setCursor(Math.min(this.cursor + this.COLS, this.currentTotal - itemOffset - 1)); + } + return false; + } + + /** + * Submethod of {@linkcode processInput} that handles the left button input + * @returns Whether the success sound should be played + */ + private processLeftInput(): boolean { + const itemOffset = this.scrollCursor * this.COLS; + if (this.cursor % this.COLS === 0) { + return this.setCursor(Math.min(this.cursor + this.COLS - 1, this.currentTotal - itemOffset - 1)); + } + return this.setCursor(this.cursor - 1); + } + + /** + * Submethod of {@linkcode processInput} that handles the right button input + * @returns Whether the success sound should be played + */ + private processRightInput(): boolean { + const itemOffset = this.scrollCursor * this.COLS; + if ((this.cursor + 1) % this.COLS === 0 || this.cursor + itemOffset === this.currentTotal - 1) { + return this.setCursor(this.cursor - (this.cursor % this.COLS)); + } + return this.setCursor(this.cursor + 1); + } + + /** + * Process user input to navigate through the achievements and vouchers UI. + * @param button - The button that was pressed + * @returns Whether an action was successfully processed + */ + processInput(button: Button): boolean { let success = false; - if (button === Button.ACTION) { - success = true; - this.setScrollCursor(0); - if (this.currentPage === Page.ACHIEVEMENTS) { - this.currentPage = Page.VOUCHERS; - this.updateVoucherIcons(); - } else if (this.currentPage === Page.VOUCHERS) { - this.currentPage = Page.ACHIEVEMENTS; - this.updateAchvIcons(); - } - this.setCursor(0, true); - this.scrollBar.setTotalRows(Math.ceil(this.currentTotal / this.COLS)); - this.scrollBar.setScrollCursor(0); - this.mainContainer.update(); - } - if (button === Button.CANCEL) { - success = true; - globalScene.ui.revertMode(); - } else { - const rowIndex = Math.floor(this.cursor / this.COLS); - const itemOffset = this.scrollCursor * this.COLS; - switch (button) { - case Button.UP: - if (this.cursor < this.COLS) { - if (this.scrollCursor) { - success = this.setScrollCursor(this.scrollCursor - 1); - } else { - // Wrap around to the last row - success = this.setScrollCursor(Math.ceil(this.currentTotal / this.COLS) - this.ROWS); - let newCursorIndex = this.cursor + (this.ROWS - 1) * this.COLS; - if (newCursorIndex > this.currentTotal - this.scrollCursor * this.COLS - 1) { - newCursorIndex -= this.COLS; - } - success = success && this.setCursor(newCursorIndex); - } - } else { - success = this.setCursor(this.cursor - this.COLS); - } - break; - case Button.DOWN: - const canMoveDown = itemOffset + 1 < this.currentTotal; - if (rowIndex >= this.ROWS - 1) { - if (this.scrollCursor < Math.ceil(this.currentTotal / this.COLS) - this.ROWS && canMoveDown) { - // scroll down one row - success = this.setScrollCursor(this.scrollCursor + 1); - } else { - // wrap back to the first row - success = this.setScrollCursor(0) && this.setCursor(this.cursor % this.COLS); - } - } else if (canMoveDown) { - success = this.setCursor(Math.min(this.cursor + this.COLS, this.currentTotal - itemOffset - 1)); - } - break; - case Button.LEFT: - if (this.cursor % this.COLS === 0) { - success = this.setCursor(Math.min(this.cursor + this.COLS - 1, this.currentTotal - itemOffset - 1)); - } else { - success = this.setCursor(this.cursor - 1); - } - break; - case Button.RIGHT: - if ((this.cursor + 1) % this.COLS === 0 || this.cursor + itemOffset === this.currentTotal - 1) { - success = this.setCursor(this.cursor - (this.cursor % this.COLS)); - } else { - success = this.setCursor(this.cursor + 1); - } - break; - } + switch (button) { + case Button.ACTION: + success = this.processActionInput(); + break; + case Button.CANCEL: + success = true; + globalScene.ui.revertMode(); + break; + case Button.UP: + success = this.processUpInput(); + break; + case Button.DOWN: + success = this.processDownInput(); + break; + case Button.LEFT: + success = this.processLeftInput(); + break; + case Button.RIGHT: + success = this.processRightInput(); + break; } if (success) { - ui.playSelect(); + this.getUi().playSelect(); } return success; } + // #endregion Input Processing setCursor(cursor: number, pageChange?: boolean): boolean { const ret = super.setCursor(cursor); @@ -334,33 +361,35 @@ export default class AchvsUiHandler extends MessageUiHandler { let update = ret; if (!this.cursorObj) { - this.cursorObj = globalScene.add.nineslice(0, 0, "select_cursor_highlight", undefined, 16, 16, 1, 1, 1, 1); - this.cursorObj.setOrigin(0, 0); + this.cursorObj = globalScene.add + .nineslice(0, 0, "select_cursor_highlight", undefined, 16, 16, 1, 1, 1, 1) + .setOrigin(0); this.iconsContainer.add(this.cursorObj); update = true; } this.cursorObj.setPositionRelative(this.icons[this.cursor], 0, 0); + if (!update && !pageChange) { + return ret; + } - if (update || pageChange) { - switch (this.currentPage) { - case Page.ACHIEVEMENTS: - if (pageChange) { - this.titleBg.width = 174; - this.titleText.x = this.titleBg.width / 2; - this.scoreContainer.setVisible(true); - } - this.showAchv(achvs[Object.keys(achvs)[cursor + this.scrollCursor * this.COLS]]); - break; - case Page.VOUCHERS: - if (pageChange) { - this.titleBg.width = 220; - this.titleText.x = this.titleBg.width / 2; - this.scoreContainer.setVisible(false); - } - this.showVoucher(vouchers[Object.keys(vouchers)[cursor + this.scrollCursor * this.COLS]]); - break; - } + switch (this.currentPage) { + case Page.ACHIEVEMENTS: + if (pageChange) { + this.titleBg.width = 174; + this.titleText.x = this.titleBg.width / 2; + this.scoreContainer.setVisible(true); + } + this.showAchv(achvs[Object.keys(achvs)[cursor + this.scrollCursor * this.COLS]]); + break; + case Page.VOUCHERS: + if (pageChange) { + this.titleBg.width = 220; + this.titleText.x = this.titleBg.width / 2; + this.scoreContainer.setVisible(false); + } + this.showVoucher(vouchers[Object.keys(vouchers)[cursor + this.scrollCursor * this.COLS]]); + break; } return ret; } @@ -399,30 +428,50 @@ export default class AchvsUiHandler extends MessageUiHandler { } /** - * updateAchvIcons(): void - * Determines what data is to be displayed on the UI and updates it accordingly based on the current value of this.scrollCursor + * Updates the icons displayed on the UI based on the current page and scroll cursor. + * @param items - The items to display (achievements or vouchers). + * @param unlocks - The unlocks data for the items. + * @param getIconFrame - A function to determine the frame for each item. + * @param headerText - The text for the header. + * @param actionText - The text for the action button. + * @param totalItems - The total number of items. + * @param forAchievements - `True` when updating icons for the achievements page, `false` for the vouchers page. */ - updateAchvIcons(): void { - this.headerText.text = this.achvsName; - this.headerActionText.text = this.vouchersName; + private updateIcons( + items: T extends true ? Achv[] : Voucher[], + unlocks: T extends true ? AchvUnlocks : VoucherUnlocks, + headerText: string, + actionText: string, + totalItems: number, + forAchievements: T, + ): void { + // type ItemType = T extends true ? Achv : Voucher; + // type RangeType = ItemType[]; + this.headerText.text = headerText; + this.headerActionText.text = actionText; const textPosition = this.headerBgX - this.headerActionText.displayWidth - 8; this.headerActionText.setX(textPosition); this.headerActionButton.setX(textPosition - this.headerActionButton.displayWidth - 4); - const achvUnlocks = globalScene.gameData.achvUnlocks; - const itemOffset = this.scrollCursor * this.COLS; const itemLimit = this.ROWS * this.COLS; - const achvRange = Object.values(achvs).slice(itemOffset, itemLimit + itemOffset); + const itemRange = items.slice(itemOffset, itemLimit + itemOffset); - achvRange.forEach((achv: Achv, i: number) => { + itemRange.forEach((item: (typeof itemRange)[0], i: number) => { const icon = this.icons[i]; - const unlocked = achvUnlocks.hasOwnProperty(achv.id); - const hidden = !unlocked && achv.secret && (!achv.parentId || !achvUnlocks.hasOwnProperty(achv.parentId)); - const tinted = !hidden && !unlocked; + const unlocked = unlocks.hasOwnProperty(item.id); + let tinted = !unlocked; + if (forAchievements) { + // Typescript cannot properly infer the type of `item` here, so we need to cast it + const achv = item as Achv; + const hidden = !unlocked && achv.secret && (!achv.parentId || !unlocks.hasOwnProperty(achv.parentId)); + tinted &&= !hidden; + icon.setFrame(!hidden ? achv.iconImage : "unknown"); + } else { + icon.setFrame(getVoucherTypeIcon((item as Voucher).voucherType)); + } - icon.setFrame(!hidden ? achv.iconImage : "unknown"); icon.setVisible(true); if (tinted) { icon.setTintFill(0); @@ -431,48 +480,39 @@ export default class AchvsUiHandler extends MessageUiHandler { } }); - if (achvRange.length < this.icons.length) { - this.icons.slice(achvRange.length).map(i => i.setVisible(false)); + if (itemRange.length < this.icons.length) { + this.icons.slice(itemRange.length).forEach(i => i.setVisible(false)); } - this.currentTotal = this.achvsTotal; + this.currentTotal = totalItems; } /** - * updateVoucherIcons(): void - * Determines what data is to be displayed on the UI and updates it accordingly based on the current value of this.scrollCursor + * Update the achievement icons displayed on the UI based on the current scroll cursor. + */ + updateAchvIcons(): void { + this.updateIcons( + Object.values(achvs), + globalScene.gameData.achvUnlocks, + this.achvsName, + this.vouchersName, + this.achvsTotal, + true, + ); + } + + /** + * Update the voucher icons displayed on the UI based on the current scroll cursor. */ updateVoucherIcons(): void { - this.headerText.text = this.vouchersName; - this.headerActionText.text = this.achvsName; - const textPosition = this.headerBgX - this.headerActionText.displayWidth - 8; - this.headerActionText.setX(textPosition); - this.headerActionButton.setX(textPosition - this.headerActionButton.displayWidth - 4); - - const voucherUnlocks = globalScene.gameData.voucherUnlocks; - - const itemOffset = this.scrollCursor * this.COLS; - const itemLimit = this.ROWS * this.COLS; - - const voucherRange = Object.values(vouchers).slice(itemOffset, itemLimit + itemOffset); - - voucherRange.forEach((voucher: Voucher, i: number) => { - const icon = this.icons[i]; - const unlocked = voucherUnlocks.hasOwnProperty(voucher.id); - - icon.setFrame(getVoucherTypeIcon(voucher.voucherType)); - icon.setVisible(true); - if (!unlocked) { - icon.setTintFill(0); - } else { - icon.clearTint(); - } - }); - - if (voucherRange.length < this.icons.length) { - this.icons.slice(voucherRange.length).map(i => i.setVisible(false)); - } - this.currentTotal = this.vouchersTotal; + this.updateIcons( + Object.values(vouchers), + globalScene.gameData.voucherUnlocks, + this.vouchersName, + this.achvsName, + this.vouchersTotal, + false, + ); } clear() { From 03368587083ccf7583923d1d35f07a5cd347f535 Mon Sep 17 00:00:00 2001 From: SmhMyHead <191356399+SmhMyHead@users.noreply.github.com> Date: Fri, 6 Jun 2025 23:20:02 +0200 Subject: [PATCH 10/23] [UI/UX] Legendary UP Gacha timer (#5921) * [UI/UIX] Legendary UP Gacha timer * Update egg-gacha-ui-handler.ts Seems "fixedInt" was needed on the delay of the playTimeTimer so the game speed doesn't affect it. * New timer container by damocleas. * gacha_legendary.png second version from @damocleas * Use phaser object chaining methods --------- Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> --- public/images/egg/gacha_legendary.png | Bin 2015 -> 3810 bytes src/ui/egg-gacha-ui-handler.ts | 46 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/public/images/egg/gacha_legendary.png b/public/images/egg/gacha_legendary.png index 8cd6fa38e295fcfb306a6ffbdb017bb1f101eeba..6eb41e55099a0ef71e946b7f89f0213b6e0996d7 100644 GIT binary patch literal 3810 zcmX|Ec{o+u`+xTt9COAT(?x?2AyY{lWV(jT66HvyG9)q&+tA>Wp^!+9i*S^VlFS`M zD$1=$TtjsoB10%Mzuotb-+rFG*1Mnmyu(`W`}wSQC0kpW@NkN80sxPh=`mXXgo4RQ z>}=Th`R+m!HX*^bCI(RTM&cU)Z-Ut|ef#j!GXF!}4ofUJ^HZOIjK^Yf1K z4te>#dxRD;e$~e;nX6v0?Xok>luT63N-(wYvk@?5&r0CvNt9Y2VH0ELDE(mleyWxC zfv};%{+%*BH$23LFV6P3vZ{W0crACoxV<@Nu3Hnu>fY8{{M!9yc0~R%o|A&e_1Y2d zMGnsSHN?1w1~G?k7jrG%mkIPA2}u3;;htq`r?Ji*#-rx=!~f}jw3cx{EmwN9#&Euo z5fUyDG520_#{=oJHu2wfU+vsJ@41;JPj}%GZ zxvEixyIeY)5`ySOXyVZY9dQF|>fooc)Tp=xMK&0ZxRP1~LR73E&I$;)(?BSr3_$L! z+F5QQ4Q}E<`ep_@o=8J=h(L+BE8`&t#9SeO+eydBnE0POz^g@WQA0$ISpv|tXHOfJ zI_cvGSl{*TB4c@!#q?xl_}MTqf)g7$gE?}<5BXUAITjCg!&zkdVHox4!Lx*#YW1G=f zb8Rh4qxDwKt$knc*G`YW(LDsY%^k7YrelqdpOkLR{~C%FW2DmK+4)X#UU@HznNpR$ zBSahBeOO1UEZQm3%1pjAHgYQK?2>t|eD~Xczypg}~_m7u=`fMYh&m}=hYlS_$BN6Q)8qnF1R>#ogsa?Qu3T+I_ z5sdw?R;ag8wzmF6JUcmh?z!sEcJF6j@A|wHMx|3yok<8hu6^noQHkY*MtbhtPoTDpgieXJ&W%6z?z7ux74lbcQCSho7P+W+bvtM#FY@OX zBMRejN(6@L#ib{~4-X;bMNUc54l!%AbG@!x0vm^N+JQPzlyM0_<$TmoSehWZHd5H~ z)TtKvTTMgeWsou-`025&UDyi)q+2danc}FDEw!EiXERlYq=8!7A39Bj0ZTnsB||L9 zSB|$nCP1Qj8ryC;0As#W1~-95xRv4IL`%f9iS1a$@6lKa+xYdd ziz&J|ROwjz-Y?i^hx1NZcTh=vd0f z&NLYu397v=n1hj%Zlgi=D485%4e&kjwbB|p)bjD%x`vgMkoPA!_caI2LOmeB;12H5 z(*xcKYNvz&P6?ZjMdw|Zcw1oi@P1X~y0AJAW$M5S6B~~%W4DB&_6d@WVK~2GV@nMO zaE6>buKnk*2mxWT;`!UIvJ@{Ei(sA)Oc~YX$PT}?Ds<%H;%e_lnCAqeolsfO($eai zQ+E61m}x>j*X$zzLSDh#@TV)=`IS{y93E|xJ#N+jG?g7Gu;f6@Gp8zLW<|xzZbT!! zNm5VZIjskdttp%W>)b#wG^_SB+d4`Wg}wF(Mk}j872PP8RE)oDDO)P^8={_V73})M z1y$)?_J~T~IRLy1{{Mk(r5)iar^jlm=`UJyI+F?n?bWq=*&#P10^H9)VuBa!egK74^LF(X5Wpd zu3Brr^&ggTWmbU#CDg2);79MJQ+@Iaob?A?4bxdkI+L;auWg8GLu8}L+aH4ztNj<5 z5lt9+n(P6r>hiuU*K(e7r=4E0a+J4AZw!!yTsvcde2 zn<_k;d?(f~;KKB6!FI0tTa{J4A@QYA$Cuqcb3SR{tg_T4=xV2J{mg6F=-A1!5$6}I zW9!^YH5d|NN0np!y~0;7mzE~0{7dnB^$+2mGC8mJE89uCZT0!3h-aTpN^MPxZ^r^? zCf}MLMVLuFzJK-*s6SiHY3l ztzGl&VnDB=;{{DIp&}9tmdj2{fOU?W;HNmq$$oOb#SX*i z?S!^*<(#NGm%-#b^fDyh^4KeMBjq>oAa!W}`Tu=P*^vI`2MLe0_daGxr+FwW& zvX7A9+~n;uIMmN@|2b(;p>GU}DdVM$T;n2xR2EmuZHqi6Mw$b7%c*R&_8U=2mm)3MW+NDKrxYao=a9;V`C4#ml&XV5#!F|MPQSVa;igxas0_g%?DnYI)7!V2cXee{8Pr836f0H-3cuCbClJ2FR$)7ej^S zKE6FF;)yS*BW(4Nq%K@~tIZ2ON)GS#;ZU<7yFyn5C{c{j+p@Jch!fBFBRQFa85W@M z1nWg2_JsV=dZ?(=W>6L?b~!gp#qG)ymL8u^@p$oJu+-cJ>Y`bxk(-^@<#?w!vCZD4 z=TZ#P`B|Uwr3A`w)Mz$s)xif;HgaD-Fam2vu{>mG8ZMPF$_2QC_*N!q$Io0I+34Lg zB$C^FxBUU#()iVnL*{$-dG*DGhFuMmeSQLGH?ia*SJc8${)|ZGiv8dXy+aIMF*0x~ zXa#zRc!`iECX64p4eQj1^Pmk*tm*KkqXnru1*6L$Mq6B06Ue87Yj;|LmDJ%9-ln*| z#=;IuzS=kJTuGePx$eT>A`4X_y0W~Kff?Qc=#QESKT-J4UhTqtGW>fuHhRPOrFZAt z)o8Ei>#_`Pn>;)ecfA>rgRTRaDQugRtuE4 z!N_z)e)M5rV5N&&>^IB&;&felZ1ja1)&CX&V(8)ru;8rD|H?U|96uDo4t~XARX<7r zCe1%V5W?KtHROoeadCZ3A?4@42`Iva0-^78dsw^C#C_k>A3<9rGDJkMcI49`kK}ymmhswn>idPK-{wA$6ysx# zh|1~$+1Ok80if%iPY`N>Xl^dnno43Z`HzCB zqwhd=Yx>qK9`)pVE^uQ0xvDJ*!+Y+2cuhb0=w-B2-3gv^;xPz=U4KL2&R8-YSnj9O z;6D9(SM~NtV;Qf-@i`aH{D4}7I=t{P)&Ia{gsH>gQ=qi!vVbxa6{XqdHndj-V-0~2 z3rYAcN9}$cjL;$i-?~n{JNWbjzoSxmqhdV5%q>0?F!cLWKJw%)9yI1(g@(_~&dj(h zI4WgokgEx^>Lrn3svtM8;ED{?{Hq~zg0Ks-N-@Efk}9q3bh93M@8hP&?p;ztoLzPC zsB3YhR1jcsOc0oK$q~BcjYs3vbC!I6Jq%0uAwe5U_9)>6&R?n|=ywS2;K_W%bK^_1 zVWF@zX7!Q%|Nhx8EW_*B#d=-n`KfSc=D_b|JOXs}_C9{}lDLG!k)|4*dtdX@bsoKa zzDwQE39bAX);F$PYi-J2a9YKxJSeMH9W-ZV^YUdpUk`t|V)98q6ohnWyD{s=dMC|q2FiN>oAJjv)>RuuN*efmogpKt|OqGPQ7FORc?k1P3wte zq^QDKf=!RtOBV|QaG$Z%#P~hoLANA&*3vGUj7QrwrTloe`Z}>Vh{`mv_V=%hD1QFqs{De?RZ+ZvLLkjI54T8Bi1c54iYxQUCw| delta 2002 zcmV;@2QB#G9p4X-BYy{`Nkl#N^;PkIUgU`*94LA6~!_kVIb>+4*o&u4ml+l`%p ze`b>F7>0KqKXf0eut`f$?f6~4INSNW_4$)*uDJDOw!i4y^x!`;$#oQD;{54vKY0+^ zB3NA$mS8mE$B=G^i?g{*;RqmUhPJGxKdiM)U@G@W7T+ubOn?9;z!CJ(FMb|=V;Ra} zA%w8?&^ibpMSoaXS!ut0?V5!sm-g1ymZQNH;|8m6{Oa@b7Ni6kJI|k^`0kAxK?JMi zdzPSj@vG0zhch!X7NjK3%*=!{Gc#f7(xvdTM^r&*Td}GoDgCYn0&$)Ldb05+qiW%tpD~` zM}D4G?0@%a?>3v>pa>Hn*~KsG@NL|>d+2-7*9QqyN9Mk$eN?TdTU%ST z-Db0Wb$vZF8jVn|*ETP_<@W8wmoNIMIca{k*}1te=m87U-e~fuRbf|_mpfBnKk*o*QJ`MUqv zpP&Fzo)AXjLCktOJT91{vcD`Iz3%l5=l#tIAe9JW_jH&<5z!px1b31L6$FqGm=2Rj z8_lAFnKo!*h5(Ma>B-}7?xzO_Cw~gYNv6!AqYeTne2Fd_g;0BC%^di&iZ73U;I)`cL*RAc7O3--;Lnu zsj1Z05s&6iKmD}w@e?-$aLghY|NNs@UP*jL2~;`<;44FRN7`bABU7*`I0 zzyKDerY(X1GHkV3YXAZWAb6RV;8^?NX-7do009#6L}*fp zZ*K3!&JeR#@GC(;0LR=yeSbcL)|W&3`DbIN-8!4|iBrWE0i^Q&^!Rbu{_3mz9u!`D zG5pd&Ari6=u)?+*I~D;G1zqNY3^MMHvMoPbZet)880)(;DD}I6~ zfB*sr!?M@ojoJ4O4tjn)tLYB`gi=N@`gh(h!4pRTgrQG+QRt$n@9gAcA@Msdg4_F^ zdLw|4WBek|TCH5BJ`cfFz(eeJXT{hpUO z>WDB1AR{voo|>3A6o0?&P2~852m(kYr@(m7dWEiED8&x}q|%sn&p*{$CC5Qk*()jg@SFafE9UO$0rl!KFiHRepJ%3Xm0!Zma6JNi{?d;@a z_-uba@e$rZr@*u?gY&XE0fgMA#O_cY7fm<`e>s#ce9`Bp zN1Z4E1dz(VD_s0(EoD5yI}W#j079BSnDeJIyRIaWGX)}mjHR;1E`B7q%1=f|02$*5 zdP)ldNGh$*pG#N-1d!pg`So3&JT3lZX%2!2Ab { + this.legendaryExpiration.setText(this.getLegendaryGachaTimeLeft()); + }, + }); + } + + getLegendaryGachaTimeLeft(): string { + // 86400000 is the number of miliseconds in one day + const msUntilMidnight = 86400000 - (Date.now() % 86400000); + const hours = `${Math.floor(msUntilMidnight / 3600000)}`; + const minutes = `${Math.floor((msUntilMidnight % 3600000) / 60000)}`; + const seconds = `${Math.floor((msUntilMidnight % 60000) / 1000)}`; + + return `${hours.padStart(2, "0")}:${minutes.padStart(2, "0")}:${seconds.padStart(2, "0")}`; + } + clear(): void { super.clear(); this.setGachaCursor(-1); this.eggGachaContainer.setVisible(false); + if (this.playTimeTimer) { + this.playTimeTimer.destroy(); + this.playTimeTimer = null; + } } } From 3ca11e83a6591801b592587d75963fba6dc96e98 Mon Sep 17 00:00:00 2001 From: Bertie690 <136088738+Bertie690@users.noreply.github.com> Date: Fri, 6 Jun 2025 19:27:58 -0400 Subject: [PATCH 11/23] [Dev] Add lefthook script to update submodules post-checkout (#5941) --- lefthook.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lefthook.yml b/lefthook.yml index ff0ac00f9e5..0f91f658171 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -9,6 +9,11 @@ pre-commit: - rebase post-merge: + commands: + update-submodules: + run: git submodule update --init --recursive + +post-checkout: commands: update-submodules: run: git submodule update --init --recursive \ No newline at end of file From 88e4ab978bc4ad98e6a90ec1419ef15d2984a2ac Mon Sep 17 00:00:00 2001 From: Bertie690 <136088738+Bertie690@users.noreply.github.com> Date: Fri, 6 Jun 2025 20:00:09 -0400 Subject: [PATCH 12/23] [Misc] Removed cases of `a ? true : false` and useless `super` calls from subclasses (#5943) * Removed cases of `if (a) {return true}' return false` * Removed useless `super.xyz` calls from functions * Fixde missing issur * Use early return in `Pokemon#isOffsetBySubstitute` --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/battle-scene.ts | 34 ++++++-------- src/configs/inputs/configHandler.ts | 5 +- src/data/abilities/ability.ts | 46 ++++--------------- src/data/battler-tags.ts | 25 ++-------- src/data/moves/move.ts | 15 +----- .../mystery-encounter-requirements.ts | 31 ++++--------- src/data/pokemon-forms.ts | 6 +-- src/data/pokemon-species.ts | 4 -- src/field/pokemon.ts | 32 ++++--------- src/modifier/modifier.ts | 8 ---- src/sprites/sprite-utils.ts | 5 +- 11 files changed, 50 insertions(+), 161 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index f2f952ea301..9a46baba899 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -3566,21 +3566,18 @@ export default class BattleScene extends SceneBase { gameMode: this.currentBattle ? this.gameMode.getName() : "Title", biome: this.currentBattle ? getBiomeName(this.arena.biomeType) : "", wave: this.currentBattle?.waveIndex ?? 0, - party: this.party - ? this.party.map(p => { - return { - name: p.name, - form: p.getFormKey(), - types: p.getTypes().map(type => PokemonType[type]), - teraType: PokemonType[p.getTeraType()], - isTerastallized: p.isTerastallized, - level: p.level, - currentHP: p.hp, - maxHP: p.getMaxHp(), - status: p.status?.effect ? StatusEffect[p.status.effect] : "", - }; - }) - : [], + party: + this.party?.map(p => ({ + name: p.name, + form: p.getFormKey(), + types: p.getTypes().map(type => PokemonType[type]), + teraType: PokemonType[p.getTeraType()], + isTerastallized: p.isTerastallized, + level: p.level, + currentHP: p.hp, + maxHP: p.getMaxHp(), + status: p.status?.effect ? StatusEffect[p.status.effect] : "", + })) ?? [], // TODO: review if this can be nullish modeChain: this.ui?.getModeChain() ?? [], }; (window as any).gameInfo = gameInfo; @@ -3963,16 +3960,13 @@ export default class BattleScene extends SceneBase { if (previousEncounter !== null && encounterType === previousEncounter) { return false; } - if ( + return !( this.mysteryEncounterSaveData.encounteredEvents.length > 0 && encounterCandidate.maxAllowedEncounters && encounterCandidate.maxAllowedEncounters > 0 && this.mysteryEncounterSaveData.encounteredEvents.filter(e => e.type === encounterType).length >= encounterCandidate.maxAllowedEncounters - ) { - return false; - } - return true; + ); }) .map(m => allMysteryEncounters[m]); // Decrement tier diff --git a/src/configs/inputs/configHandler.ts b/src/configs/inputs/configHandler.ts index b896f303cb3..227c2b964b9 100644 --- a/src/configs/inputs/configHandler.ts +++ b/src/configs/inputs/configHandler.ts @@ -197,10 +197,7 @@ export function canIAssignThisKey(config, key) { export function canIOverrideThisSetting(config, settingName) { const key = getKeyWithSettingName(config, settingName); // || isTheLatestBind(config, settingName) no longer needed since action and cancel are protected - if (config.blacklist?.includes(key)) { - return false; - } - return true; + return !config.blacklist?.includes(key); } export function canIDeleteThisKey(config, key) { diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 1bd71df32e0..6e6c00b8a43 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -1246,7 +1246,7 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr { /** * Determine if the move type change attribute can be applied - * + * * Can be applied if: * - The ability's condition is met, e.g. pixilate only boosts normal moves, * - The move is not forbidden from having its type changed by an ability, e.g. {@linkcode MoveId.MULTI_ATTACK} @@ -1262,7 +1262,7 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr { */ override canApplyPreAttack(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _defender: Pokemon | null, move: Move, _args: [NumberHolder?, NumberHolder?, ...any]): boolean { return (!this.condition || this.condition(pokemon, _defender, move)) && - !noAbilityTypeOverrideMoves.has(move.id) && + !noAbilityTypeOverrideMoves.has(move.id) && (!pokemon.isTerastallized || (move.id !== MoveId.TERA_BLAST && (move.id !== MoveId.TERA_STARSTORM || pokemon.getTeraType() !== PokemonType.STELLAR || !pokemon.hasSpecies(SpeciesId.TERAPAGOS)))); @@ -2653,11 +2653,7 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { } const ally = pokemon.getAlly(); - if (isNullOrUndefined(ally) || ally.getStatStages().every(s => s === 0)) { - return false; - } - - return true; + return !(isNullOrUndefined(ally) || ally.getStatStages().every(s => s === 0)); } override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { @@ -2723,11 +2719,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { } // transforming from or into fusion pokemon causes various problems (including crashes and save corruption) - if (this.getTarget(targets).fusionSpecies || pokemon.fusionSpecies) { - return false; - } - - return true; + return !(this.getTarget(targets).fusionSpecies || pokemon.fusionSpecies); } override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { @@ -3544,10 +3536,7 @@ export class BlockStatusDamageAbAttr extends AbAttr { } override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - if (pokemon.status && this.effects.includes(pokemon.status.effect)) { - return true; - } - return false; + return !!pokemon.status?.effect && this.effects.includes(pokemon.status.effect); } /** @@ -4803,11 +4792,7 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { const diedToDirectDamage = move !== undefined && attacker !== undefined && move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}); const cancelled = new BooleanHolder(false); globalScene.getField(true).map(p => applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled, simulated)); - if (!diedToDirectDamage || cancelled.value || attacker!.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { - return false; - } - - return true; + return !(!diedToDirectDamage || cancelled.value || attacker!.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)); } override applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): void { @@ -6508,12 +6493,7 @@ export function initAbilities() { new Ability(AbilityId.INTIMIDATE, 3) .attr(PostSummonStatStageChangeAbAttr, [ Stat.ATK ], -1, false, true), new Ability(AbilityId.SHADOW_TAG, 3) - .attr(ArenaTrapAbAttr, (user, target) => { - if (target.hasAbility(AbilityId.SHADOW_TAG)) { - return false; - } - return true; - }), + .attr(ArenaTrapAbAttr, (_user, target) => !target.hasAbility(AbilityId.SHADOW_TAG)), new Ability(AbilityId.ROUGH_SKIN, 3) .attr(PostDefendContactDamageAbAttr, 8) .bypassFaint(), @@ -6573,10 +6553,7 @@ export function initAbilities() { .ignorable(), new Ability(AbilityId.MAGNET_PULL, 3) .attr(ArenaTrapAbAttr, (user, target) => { - if (target.getTypes(true).includes(PokemonType.STEEL) || (target.getTypes(true).includes(PokemonType.STELLAR) && target.getTypes().includes(PokemonType.STEEL))) { - return true; - } - return false; + return target.getTypes(true).includes(PokemonType.STEEL) || (target.getTypes(true).includes(PokemonType.STELLAR) && target.getTypes().includes(PokemonType.STEEL)); }), new Ability(AbilityId.SOUNDPROOF, 3) .attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.SOUND_BASED)) @@ -6654,12 +6631,7 @@ export function initAbilities() { .attr(PostSummonWeatherChangeAbAttr, WeatherType.SUNNY) .attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.SUNNY), new Ability(AbilityId.ARENA_TRAP, 3) - .attr(ArenaTrapAbAttr, (user, target) => { - if (target.isGrounded()) { - return true; - } - return false; - }) + .attr(ArenaTrapAbAttr, (user, target) => target.isGrounded()) .attr(DoubleBattleChanceAbAttr), new Ability(AbilityId.VITAL_SPIRIT, 3) .attr(StatusEffectImmunityAbAttr, StatusEffect.SLEEP) diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index c047f424591..4d99fd18ac0 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1203,10 +1203,7 @@ export class EncoreTag extends MoveRestrictionBattlerTag { override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { if (lapseType === BattlerTagLapseType.CUSTOM) { const encoredMove = pokemon.getMoveset().find(m => m.moveId === this.moveId); - if (encoredMove && encoredMove?.getPpRatio() > 0) { - return true; - } - return false; + return !isNullOrUndefined(encoredMove) && encoredMove.getPpRatio() > 0; } return super.lapse(pokemon, lapseType); } @@ -1218,10 +1215,7 @@ export class EncoreTag extends MoveRestrictionBattlerTag { * @returns `true` if the move does not match with the moveId stored and as a result, restricted */ override isMoveRestricted(move: MoveId, _user?: Pokemon): boolean { - if (move !== this.moveId) { - return true; - } - return false; + return move !== this.moveId; } override selectionDeniedText(_pokemon: Pokemon, move: MoveId): string { @@ -2768,10 +2762,7 @@ export class HealBlockTag extends MoveRestrictionBattlerTag { * @returns `true` if the move has a TRIAGE_MOVE flag and is a status move */ override isMoveRestricted(move: MoveId): boolean { - if (allMoves[move].hasFlag(MoveFlags.TRIAGE_MOVE) && allMoves[move].category === MoveCategory.STATUS) { - return true; - } - return false; + return allMoves[move].hasFlag(MoveFlags.TRIAGE_MOVE) && allMoves[move].category === MoveCategory.STATUS; } /** @@ -2785,10 +2776,7 @@ export class HealBlockTag extends MoveRestrictionBattlerTag { override isMoveTargetRestricted(move: MoveId, user: Pokemon, target: Pokemon) { const moveCategory = new NumberHolder(allMoves[move].category); applyMoveAttrs(StatusCategoryOnAllyAttr, user, target, allMoves[move], moveCategory); - if (allMoves[move].hasAttr(HealOnAllyAttr) && moveCategory.value === MoveCategory.STATUS) { - return true; - } - return false; + return allMoves[move].hasAttr(HealOnAllyAttr) && moveCategory.value === MoveCategory.STATUS; } /** @@ -3126,10 +3114,7 @@ export class TormentTag extends MoveRestrictionBattlerTag { const moveObj = allMoves[lastMove.move]; const isUnaffected = moveObj.hasAttr(ConsecutiveUseDoublePowerAttr) || user.getTag(BattlerTagType.FRENZY); const validLastMoveResult = lastMove.result === MoveResult.SUCCESS || lastMove.result === MoveResult.MISS; - if (lastMove.move === move && validLastMoveResult && lastMove.move !== MoveId.STRUGGLE && !isUnaffected) { - return true; - } - return false; + return lastMove.move === move && validLastMoveResult && lastMove.move !== MoveId.STRUGGLE && !isUnaffected; } override selectionDeniedText(pokemon: Pokemon, _move: MoveId): string { diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 91cc6350daa..e98b28e2a48 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -5472,13 +5472,6 @@ export class AddBattlerTagAttr extends MoveEffectAttr { this.failOnOverlap = !!failOnOverlap; } - canApply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - if (!super.canApply(user, target, move, args)) { - return false; - } - return true; - } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!super.apply(user, target, move, args)) { return false; @@ -6832,7 +6825,7 @@ export class RandomMovesetMoveAttr extends CallMoveAttr { return false; } - this.moveId = moves[user.randBattleSeedInt(moves.length)]!.moveId; + this.moveId = moves[user.randBattleSeedInt(moves.length)].moveId; return true; }; } @@ -7367,11 +7360,7 @@ export class SketchAttr extends MoveEffectAttr { return false; } - if (user.getMoveset().find(m => m.moveId === targetMove.move)) { - return false; - } - - return true; + return !user.getMoveset().some(m => m.moveId === targetMove.move); }; } } diff --git a/src/data/mystery-encounters/mystery-encounter-requirements.ts b/src/data/mystery-encounters/mystery-encounter-requirements.ts index ec78408ea83..88d9ba402a9 100644 --- a/src/data/mystery-encounters/mystery-encounter-requirements.ts +++ b/src/data/mystery-encounters/mystery-encounter-requirements.ts @@ -275,15 +275,11 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement { override meetsRequirement(): boolean { const timeOfDay = globalScene.arena?.getTimeOfDay(); - if ( + return !( !isNullOrUndefined(timeOfDay) && this.requiredTimeOfDay?.length > 0 && !this.requiredTimeOfDay.includes(timeOfDay) - ) { - return false; - } - - return true; + ); } override getDialogueToken(_pokemon?: PlayerPokemon): [string, string] { @@ -301,15 +297,11 @@ export class WeatherRequirement extends EncounterSceneRequirement { override meetsRequirement(): boolean { const currentWeather = globalScene.arena.weather?.weatherType; - if ( + return !( !isNullOrUndefined(currentWeather) && this.requiredWeather?.length > 0 && !this.requiredWeather.includes(currentWeather!) - ) { - return false; - } - - return true; + ); } override getDialogueToken(_pokemon?: PlayerPokemon): [string, string] { @@ -803,7 +795,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen } filterByForm(pokemon, formChangeItem) { - if ( + return ( pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) && // Get all form changes for this species with an item trigger, including any compound triggers pokemonFormChanges[pokemon.species.speciesId] @@ -812,10 +804,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen .flatMap(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger) .flatMap(fc => fc.item) .includes(formChangeItem) - ) { - return true; - } - return false; + ); } override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] { @@ -873,17 +862,15 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement { ) { return true; } - if ( + + return ( pokemon.isFusion() && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) && pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter( e => e.item === evolutionItem && (!e.condition || e.condition.predicate(pokemon)), ).length && pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX - ) { - return true; - } - return false; + ); } override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] { diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index 1098ddc4eeb..47eb355c8b6 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -186,11 +186,7 @@ export class SpeciesFormChange { } } - if (!this.trigger.canChange(pokemon)) { - return false; - } - - return true; + return this.trigger.canChange(pokemon); } findTrigger(triggerType: Constructor): SpeciesFormChangeTrigger | nil { diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 66ed0b09eeb..df7a8c118d5 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -1281,10 +1281,6 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali }; } - isObtainable() { - return super.isObtainable(); - } - hasVariants() { let variantDataIndex: string | number = this.speciesId; if (this.forms.length > 0) { diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index d06553f2227..df953f06834 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1292,19 +1292,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ isOffsetBySubstitute(): boolean { const substitute = this.getTag(SubstituteTag); - if (substitute) { - if (substitute.sprite === undefined) { - return false; - } - - // During the Pokemon's MoveEffect phase, the offset is removed to put the Pokemon "in focus" - const currentPhase = globalScene.getCurrentPhase(); - if (currentPhase?.is("MoveEffectPhase") && currentPhase.getPokemon() === this) { - return false; - } - return true; + if (!substitute || substitute.sprite === undefined) { + return false; } - return false; + // During the Pokemon's MoveEffect phase, the offset is removed to put the Pokemon "in focus" + const currentPhase = globalScene.getCurrentPhase(); + return !(currentPhase?.is("MoveEffectPhase") && currentPhase.getPokemon() === this); } /** If this Pokemon has a Substitute on the field, removes its sprite from the field. */ @@ -2241,10 +2234,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.getAbility(ignoreOverride).id === ability && (!canApply || this.canApplyAbility())) { return true; } - if (this.getPassiveAbility().id === ability && this.hasPassive() && (!canApply || this.canApplyAbility(true))) { - return true; - } - return false; + return this.getPassiveAbility().id === ability && this.hasPassive() && (!canApply || this.canApplyAbility(true)); } /** @@ -2261,10 +2251,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).hasAttr(attrType)) { return true; } - if (this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().hasAttr(attrType)) { - return true; - } - return false; + return this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().hasAttr(attrType); } /** @@ -5464,10 +5451,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if ((ownedAbilityAttrs & 2) > 0 && this.hasSameAbilityInRootForm(1)) { return true; } - if ((ownedAbilityAttrs & 4) > 0 && this.hasSameAbilityInRootForm(2)) { - return true; - } - return false; + return (ownedAbilityAttrs & 4) > 0 && this.hasSameAbilityInRootForm(2); } /** diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 2bebbcae90c..f8c71b2c891 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1090,10 +1090,6 @@ export class PokemonIncrementingStatModifier extends PokemonHeldItemModifier { return new PokemonIncrementingStatModifier(this.type, this.pokemonId, this.stackCount); } - getArgs(): any[] { - return super.getArgs(); - } - /** * Checks if the {@linkcode PokemonIncrementingStatModifier} should be applied to the {@linkcode Pokemon}. * @param pokemon The {@linkcode Pokemon} that holds the item @@ -1216,10 +1212,6 @@ export class StatBoosterModifier extends PokemonHeldItemModifier { * @see {@linkcode apply} */ export class EvolutionStatBoosterModifier extends StatBoosterModifier { - clone() { - return super.clone() as EvolutionStatBoosterModifier; - } - matchType(modifier: Modifier): boolean { return modifier instanceof EvolutionStatBoosterModifier; } diff --git a/src/sprites/sprite-utils.ts b/src/sprites/sprite-utils.ts index 8a352de3d55..0f4adf7882f 100644 --- a/src/sprites/sprite-utils.ts +++ b/src/sprites/sprite-utils.ts @@ -21,8 +21,5 @@ export function hasExpSprite(key: string): boolean { if (keyMatch[5]) { k += keyMatch[5]; } - if (!expSpriteKeys.has(k)) { - return false; - } - return true; + return expSpriteKeys.has(k); } From a818c2b33f5981381615d29da92c850f5131b1c7 Mon Sep 17 00:00:00 2001 From: Bertie690 <136088738+Bertie690@users.noreply.github.com> Date: Fri, 6 Jun 2025 23:50:16 -0400 Subject: [PATCH 13/23] [Bug] Dancer no longer breaks "last hit only" moves, respects flinch + steadfast (#5945) * WIP * Fixed Dancer last hit, flinch move interaction * Fixed steadfast interaction * Fixed comment + flaky test --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/abilities/ability.ts | 1 + src/data/battler-tags.ts | 18 ++++++--------- test/abilities/dancer.test.ts | 41 +++++++++++++++++++++++++++++++++++ test/moves/instruct.test.ts | 30 +++++++++++++++++++++++-- 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 6e6c00b8a43..d20eddcb3cb 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -4438,6 +4438,7 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { simulated: boolean, args: any[]): void { if (!simulated) { + dancer.turnData.extraTurns++; // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) { const target = this.getTarget(dancer, source, targets); diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 4d99fd18ac0..456f519a34c 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -649,20 +649,14 @@ class NoRetreatTag extends TrappedTag { */ export class FlinchedTag extends BattlerTag { constructor(sourceMove: MoveId) { - super(BattlerTagType.FLINCHED, [BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.TURN_END], 0, sourceMove); - } - - onAdd(pokemon: Pokemon): void { - super.onAdd(pokemon); - - applyAbAttrs(FlinchEffectAbAttr, pokemon, null); + super(BattlerTagType.FLINCHED, [BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.TURN_END], 1, sourceMove); } /** - * Cancels the Pokemon's next Move on the turn this tag is applied - * @param pokemon The {@linkcode Pokemon} with this tag - * @param lapseType The {@linkcode BattlerTagLapseType lapse type} used for this function call - * @returns `false` (This tag is always removed after applying its effects) + * Cancels the flinched Pokemon's currently used move this turn if called mid-execution, or removes the tag at end of turn. + * @param pokemon - The {@linkcode Pokemon} with this tag. + * @param lapseType - The {@linkcode BattlerTagLapseType | lapse type} used for this function call. + * @returns Whether the tag should remain active. */ lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { if (lapseType === BattlerTagLapseType.PRE_MOVE) { @@ -672,6 +666,8 @@ export class FlinchedTag extends BattlerTag { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); + applyAbAttrs(FlinchEffectAbAttr, pokemon, null); + return true; } return super.lapse(pokemon, lapseType); diff --git a/test/abilities/dancer.test.ts b/test/abilities/dancer.test.ts index 7b4edb84789..086c69300b4 100644 --- a/test/abilities/dancer.test.ts +++ b/test/abilities/dancer.test.ts @@ -1,8 +1,10 @@ import { BattlerIndex } from "#app/battle"; +import { MoveResult } from "#app/field/pokemon"; import type { MovePhase } from "#app/phases/move-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -99,4 +101,43 @@ describe("Abilities - Dancer", () => { expect(currentPhase.pokemon).toBe(oricorio); expect(currentPhase.move.moveId).toBe(MoveId.REVELATION_DANCE); }); + + it("should not break subsequent last hit only moves", async () => { + game.override.battleStyle("single"); + await game.classicMode.startBattle([SpeciesId.ORICORIO, SpeciesId.FEEBAS]); + + const [oricorio, feebas] = game.scene.getPlayerParty(); + + game.move.use(MoveId.BATON_PASS); + game.doSelectPartyPokemon(1); + await game.move.forceEnemyMove(MoveId.SWORDS_DANCE); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase"); + expect(game.field.getPlayerPokemon()).toBe(feebas); + expect(feebas.getStatStage(Stat.ATK)).toBe(2); + expect(oricorio.isOnField()).toBe(false); + expect(oricorio.visible).toBe(false); + }); + + it("should not trigger while flinched", async () => { + game.override.battleStyle("double").moveset(MoveId.SPLASH).enemyMoveset([MoveId.SWORDS_DANCE, MoveId.FAKE_OUT]); + await game.classicMode.startBattle([SpeciesId.ORICORIO]); + + const oricorio = game.scene.getPlayerPokemon()!; + expect(oricorio).toBeDefined(); + + // get faked out and copy swords dance + game.move.select(MoveId.SPLASH); + await game.move.forceEnemyMove(MoveId.SWORDS_DANCE); + await game.move.forceEnemyMove(MoveId.FAKE_OUT, BattlerIndex.PLAYER); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(oricorio.getLastXMoves(-1)[0]).toMatchObject({ + move: MoveId.NONE, + result: MoveResult.FAIL, + }); + expect(oricorio.getStatStage(Stat.ATK)).toBe(0); + }); }); diff --git a/test/moves/instruct.test.ts b/test/moves/instruct.test.ts index 56ac8d9d04d..4a701ed6ac5 100644 --- a/test/moves/instruct.test.ts +++ b/test/moves/instruct.test.ts @@ -1,13 +1,15 @@ import { BattlerIndex } from "#app/battle"; +import { allMoves } from "#app/data/data-lists"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import type { MovePhase } from "#app/phases/move-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { Stat } from "#enums/stat"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Instruct", () => { let phaserGame: Phaser.Game; @@ -34,7 +36,8 @@ describe("Moves - Instruct", () => { game.override .battleStyle("single") .enemySpecies(SpeciesId.SHUCKLE) - .enemyAbility(AbilityId.NO_GUARD) + .enemyAbility(AbilityId.BALL_FETCH) + .passiveAbility(AbilityId.NO_GUARD) .enemyLevel(100) .startingLevel(100) .disableCrits(); @@ -536,4 +539,27 @@ describe("Moves - Instruct", () => { expect(ivysaur.turnData.attacksReceived.length).toBe(15); }); + + it("should respect prior flinches and trigger Steadfast", async () => { + game.override.battleStyle("double"); + vi.spyOn(allMoves[MoveId.AIR_SLASH], "chance", "get").mockReturnValue(100); + await game.classicMode.startBattle([SpeciesId.AUDINO, SpeciesId.ABRA]); + + // Fake enemy 1 having attacked prior + const [, player2, enemy1, enemy2] = game.scene.getField(); + enemy1.pushMoveHistory({ move: MoveId.ABSORB, targets: [BattlerIndex.PLAYER] }); + game.field.mockAbility(enemy1, AbilityId.STEADFAST); + + game.move.use(MoveId.AIR_SLASH, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.use(MoveId.INSTRUCT, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY); + await game.move.forceEnemyMove(MoveId.ABSORB); + await game.move.forceEnemyMove(MoveId.INSTRUCT, BattlerIndex.ENEMY); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY_2]); + await game.toEndOfTurn(); + + expect(enemy1.getLastXMoves(-1).map(m => m.move)).toEqual([MoveId.NONE, MoveId.NONE, MoveId.NONE, MoveId.ABSORB]); + expect(enemy1.getStatStage(Stat.SPD)).toBe(3); + expect(player2.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + expect(enemy2.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + }); }); From c5db8273815b012fbe4d2990cc8a31480c219b94 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Fri, 6 Jun 2025 23:09:23 -0700 Subject: [PATCH 14/23] [Dev] Enable Biome linting of `move-effect-phase.ts` (#5947) --- biome.jsonc | 3 --- src/phases/move-effect-phase.ts | 6 ++++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/biome.jsonc b/biome.jsonc index 40301b3e0bc..141c44dc87d 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -42,9 +42,6 @@ // TODO: Remove if we ever get down to 0 circular imports "organizeImports": { "enabled": false }, "linter": { - "ignore": [ - "src/phases/move-effect-phase.ts" // TODO: unignore after move-effect-phase refactor - ], "enabled": true, "rules": { "recommended": true, diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index 3160d848624..d80fdc89e6f 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -219,6 +219,7 @@ export class MoveEffectPhase extends PokemonPhase { return; } break; + // biome-ignore lint/suspicious/noFallthroughSwitchClause: The fallthrough is intentional case HitCheckResult.NO_EFFECT: globalScene.queueMessage( i18next.t(this.move.id === MoveId.SHEER_COLD ? "battle:hitResultImmune" : "battle:hitResultNoEffect", { @@ -294,7 +295,8 @@ export class MoveEffectPhase extends PokemonPhase { // If other effects were overriden, stop this phase before they can be applied if (overridden.value) { - return this.end(); + this.end(); + return; } // Lapse `MOVE_EFFECT` effects (i.e. semi-invulnerability) when applicable @@ -743,7 +745,7 @@ export class MoveEffectPhase extends PokemonPhase { firstTarget?: boolean | null, selfTarget?: boolean, ): void { - return applyFilteredMoveAttrs( + applyFilteredMoveAttrs( (attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === triggerType && From 1ff45687c5259dfc109bf2bf96d64a61da78b22e Mon Sep 17 00:00:00 2001 From: Bertie690 <136088738+Bertie690@users.noreply.github.com> Date: Sat, 7 Jun 2025 20:28:01 -0400 Subject: [PATCH 15/23] [Refactor] Merged `interfaces/` into `@types/`; removed runtime orphan modules https://github.com/pagefaultgames/pokerogue/pull/5951 --- src/@types/{DexData.ts => dex-data.ts} | 11 ++++------ .../held-modifier-config.ts | 0 src/{interfaces => @types}/locales.ts | 3 --- .../typedefs.ts => @types/trainer-funcs.ts} | 4 ++-- src/battle-scene.ts | 4 ++-- src/data/abilities/ability-class.ts | 2 +- src/data/egg-hatch-data.ts | 3 ++- src/data/moves/move.ts | 2 +- .../encounters/absolute-avarice-encounter.ts | 2 +- .../encounters/training-session-encounter.ts | 2 +- .../encounters/weird-dream-encounter.ts | 2 +- .../utils/encounter-phase-utils.ts | 2 +- src/data/pokemon-species.ts | 2 +- src/data/trainers/evil-admin-trainer-pools.ts | 2 +- src/data/trainers/trainer-config.ts | 2 +- src/debug.js | 17 -------------- src/global-vars/starter-colors.ts | 5 ++--- src/starter-colors.ts | 4 ---- src/system/game-data.ts | 16 ++------------ src/system/session-history.ts | 22 ------------------- src/ui/pokedex-page-ui-handler.ts | 3 ++- src/ui/pokedex-ui-handler.ts | 3 ++- src/ui/pokemon-info-container.ts | 3 ++- src/ui/starter-select-ui-handler.ts | 9 ++------ 24 files changed, 31 insertions(+), 94 deletions(-) rename src/@types/{DexData.ts => dex-data.ts} (82%) rename src/{interfaces => @types}/held-modifier-config.ts (100%) rename src/{interfaces => @types}/locales.ts (95%) rename src/{data/trainers/typedefs.ts => @types/trainer-funcs.ts} (83%) delete mode 100644 src/debug.js delete mode 100644 src/starter-colors.ts delete mode 100644 src/system/session-history.ts diff --git a/src/@types/DexData.ts b/src/@types/dex-data.ts similarity index 82% rename from src/@types/DexData.ts rename to src/@types/dex-data.ts index 19bb0357471..88cc16886bd 100644 --- a/src/@types/DexData.ts +++ b/src/@types/dex-data.ts @@ -1,6 +1,7 @@ -/** - * Dex entry for a single Pokemon Species - */ +export interface DexData { + [key: number]: DexEntry; +} + export interface DexEntry { seenAttr: bigint; caughtAttr: bigint; @@ -10,7 +11,3 @@ export interface DexEntry { hatchedCount: number; ivs: number[]; } - -export interface DexData { - [key: number]: DexEntry; -} diff --git a/src/interfaces/held-modifier-config.ts b/src/@types/held-modifier-config.ts similarity index 100% rename from src/interfaces/held-modifier-config.ts rename to src/@types/held-modifier-config.ts diff --git a/src/interfaces/locales.ts b/src/@types/locales.ts similarity index 95% rename from src/interfaces/locales.ts rename to src/@types/locales.ts index 2d26911f82f..3b5a1477e19 100644 --- a/src/interfaces/locales.ts +++ b/src/@types/locales.ts @@ -2,9 +2,6 @@ export interface Localizable { localize(): void; } -export interface TranslationEntries { - [key: string]: string | { [key: string]: string }; -} export interface SimpleTranslationEntries { [key: string]: string; } diff --git a/src/data/trainers/typedefs.ts b/src/@types/trainer-funcs.ts similarity index 83% rename from src/data/trainers/typedefs.ts rename to src/@types/trainer-funcs.ts index 3df2ba3f5f8..0546dd53024 100644 --- a/src/data/trainers/typedefs.ts +++ b/src/@types/trainer-funcs.ts @@ -2,8 +2,8 @@ import type { EnemyPokemon } from "#app/field/pokemon"; import type { PersistentModifier } from "#app/modifier/modifier"; import type { PartyMemberStrength } from "#enums/party-member-strength"; import type { SpeciesId } from "#enums/species-id"; -import type { TrainerConfig } from "./trainer-config"; -import type { TrainerPartyTemplate } from "./TrainerPartyTemplate"; +import type { TrainerConfig } from "../data/trainers/trainer-config"; +import type { TrainerPartyTemplate } from "../data/trainers/TrainerPartyTemplate"; export type PartyTemplateFunc = () => TrainerPartyTemplate; export type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon; diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 9a46baba899..598ab64881a 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -120,7 +120,7 @@ import { SceneBase } from "#app/scene-base"; import CandyBar from "#app/ui/candy-bar"; import type { Variant } from "#app/sprites/variant"; import { variantData, clearVariantData } from "#app/sprites/variant"; -import type { Localizable } from "#app/interfaces/locales"; +import type { Localizable } from "#app/@types/locales"; import Overrides from "#app/overrides"; import { InputsController } from "#app/inputs-controller"; import { UiInputs } from "#app/ui-inputs"; @@ -169,7 +169,7 @@ import { import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import type HeldModifierConfig from "#app/interfaces/held-modifier-config"; +import type HeldModifierConfig from "#app/@types/held-modifier-config"; import { ExpPhase } from "#app/phases/exp-phase"; import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; diff --git a/src/data/abilities/ability-class.ts b/src/data/abilities/ability-class.ts index 9da83a32c4d..10bd01f3987 100644 --- a/src/data/abilities/ability-class.ts +++ b/src/data/abilities/ability-class.ts @@ -2,7 +2,7 @@ import { AbilityId } from "#enums/ability-id"; import type { AbAttrCondition } from "#app/@types/ability-types"; import type { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr"; import i18next from "i18next"; -import type { Localizable } from "#app/interfaces/locales"; +import type { Localizable } from "#app/@types/locales"; import type { Constructor } from "#app/utils/common"; export class Ability implements Localizable { diff --git a/src/data/egg-hatch-data.ts b/src/data/egg-hatch-data.ts index 949ed1af063..e81ae69515c 100644 --- a/src/data/egg-hatch-data.ts +++ b/src/data/egg-hatch-data.ts @@ -1,6 +1,7 @@ import { globalScene } from "#app/global-scene"; import type { PlayerPokemon } from "#app/field/pokemon"; -import type { DexEntry, StarterDataEntry } from "#app/system/game-data"; +import type { StarterDataEntry } from "#app/system/game-data"; +import type { DexEntry } from "#app/@types/dex-data"; /** * Stores data associated with a specific egg and the hatched pokemon diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index e98b28e2a48..1bd6db97005 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -81,7 +81,7 @@ import { TerrainType } from "../terrain"; import { ModifierPoolType } from "#app/modifier/modifier-type"; import { Command } from "../../ui/command-ui-handler"; import i18next from "i18next"; -import type { Localizable } from "#app/interfaces/locales"; +import type { Localizable } from "#app/@types/locales"; import { getBerryEffectFunc } from "../berry"; import { AbilityId } from "#enums/ability-id"; import { ArenaTagType } from "#enums/arena-tag-type"; diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index f46e1360b0d..b14caa4e2c3 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -33,7 +33,7 @@ import { } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { TrainerSlot } from "#enums/trainer-slot"; import { PokeballType } from "#enums/pokeball"; -import type HeldModifierConfig from "#app/interfaces/held-modifier-config"; +import type HeldModifierConfig from "#app/@types/held-modifier-config"; import type { BerryType } from "#enums/berry-type"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { Stat } from "#enums/stat"; diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index 597a6b009b3..b17a39ae694 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -25,7 +25,7 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import type HeldModifierConfig from "#app/interfaces/held-modifier-config"; +import type HeldModifierConfig from "#app/@types/held-modifier-config"; import i18next from "i18next"; import { getStatKey } from "#enums/stat"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 25be75b9d5a..2b1f775b78e 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -39,7 +39,7 @@ import { PlayerGender } from "#enums/player-gender"; import { TrainerType } from "#enums/trainer-type"; import PokemonData from "#app/system/pokemon-data"; import { Nature } from "#enums/nature"; -import type HeldModifierConfig from "#app/interfaces/held-modifier-config"; +import type HeldModifierConfig from "#app/@types/held-modifier-config"; import { trainerConfigs } from "#app/data/trainers/trainer-config"; import { TrainerPartyTemplate } from "#app/data/trainers/TrainerPartyTemplate"; import { PartyMemberStrength } from "#enums/party-member-strength"; diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index c9eaa2e6968..5736835d98a 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -50,7 +50,7 @@ import type PokemonSpecies from "#app/data/pokemon-species"; import type { IEggOptions } from "#app/data/egg"; import { Egg } from "#app/data/egg"; import type { CustomPokemonData } from "#app/data/custom-pokemon-data"; -import type HeldModifierConfig from "#app/interfaces/held-modifier-config"; +import type HeldModifierConfig from "#app/@types/held-modifier-config"; import { MovePhase } from "#app/phases/move-phase"; import { EggLapsePhase } from "#app/phases/egg-lapse-phase"; import { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase"; diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index df7a8c118d5..e946b526960 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -1,4 +1,4 @@ -import type { Localizable } from "#app/interfaces/locales"; +import type { Localizable } from "#app/@types/locales"; import { AbilityId } from "#enums/ability-id"; import { PartyMemberStrength } from "#enums/party-member-strength"; import { SpeciesId } from "#enums/species-id"; diff --git a/src/data/trainers/evil-admin-trainer-pools.ts b/src/data/trainers/evil-admin-trainer-pools.ts index 7c0336e784e..74ee3e8cb3d 100644 --- a/src/data/trainers/evil-admin-trainer-pools.ts +++ b/src/data/trainers/evil-admin-trainer-pools.ts @@ -1,4 +1,4 @@ -import type { TrainerTierPools } from "#app/data/trainers/typedefs"; +import type { TrainerTierPools } from "#app/@types/trainer-funcs"; import { TrainerPoolTier } from "#enums/trainer-pool-tier"; import { SpeciesId } from "#enums/species-id"; diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index 6408bf94eac..8e704b0b301 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -48,7 +48,7 @@ import type { TrainerTierPools, TrainerConfigs, PartyMemberFuncs, -} from "./typedefs"; +} from "../../@types/trainer-funcs"; /** Minimum BST for Pokemon generated onto the Elite Four's teams */ const ELITE_FOUR_MINIMUM_BST = 460; diff --git a/src/debug.js b/src/debug.js deleted file mode 100644 index 6ddf6046c7a..00000000000 --- a/src/debug.js +++ /dev/null @@ -1,17 +0,0 @@ -export function getData() { - const dataStr = localStorage.getItem("data"); - if (!dataStr) { - return null; - } - return JSON.parse(atob(dataStr), (k, v) => - k.endsWith("Attr") && !["natureAttr", "abilityAttr", "passiveAttr"].includes(k) ? BigInt(v) : v, - ); -} - -export function getSession() { - const sessionStr = localStorage.getItem("sessionData"); - if (!sessionStr) { - return null; - } - return JSON.parse(atob(sessionStr)); -} diff --git a/src/global-vars/starter-colors.ts b/src/global-vars/starter-colors.ts index 6abe028be99..6b019bd5c34 100644 --- a/src/global-vars/starter-colors.ts +++ b/src/global-vars/starter-colors.ts @@ -1,4 +1,3 @@ -export const starterColors: StarterColors = {}; -interface StarterColors { +export const starterColors: { [key: string]: [string, string]; -} +} = {}; diff --git a/src/starter-colors.ts b/src/starter-colors.ts deleted file mode 100644 index 6abe028be99..00000000000 --- a/src/starter-colors.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const starterColors: StarterColors = {}; -interface StarterColors { - [key: string]: [string, string]; -} diff --git a/src/system/game-data.ts b/src/system/game-data.ts index ab907d2f768..d9b8d73d3c3 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -62,6 +62,7 @@ import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; import { ArenaTrapTag } from "#app/data/arena-tag"; import { pokemonFormChanges } from "#app/data/pokemon-forms"; import type { PokemonType } from "#enums/pokemon-type"; +import type { DexData, DexEntry } from "../@types/dex-data"; export const defaultStarterSpecies: SpeciesId[] = [ SpeciesId.BULBASAUR, @@ -131,6 +132,7 @@ export function decrypt(data: string, bypassLogin: boolean): string { )(data); } +// TODO: Move all these exported interfaces to @types export interface SystemSaveData { trainerId: number; secretId: number; @@ -191,20 +193,6 @@ export interface VoucherCounts { [type: string]: number; } -export interface DexData { - [key: number]: DexEntry; -} - -export interface DexEntry { - seenAttr: bigint; - caughtAttr: bigint; - natureAttr: number; - seenCount: number; - caughtCount: number; - hatchedCount: number; - ivs: number[]; -} - export type StarterMoveset = [MoveId] | [MoveId, MoveId] | [MoveId, MoveId, MoveId] | [MoveId, MoveId, MoveId, MoveId]; export interface StarterFormMoveData { diff --git a/src/system/session-history.ts b/src/system/session-history.ts deleted file mode 100644 index 8eb81cb6efe..00000000000 --- a/src/system/session-history.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { GameModes } from "../game-mode"; -import type PokemonData from "./pokemon-data"; -import type PersistentModifierData from "./modifier-data"; - -export enum SessionHistoryResult { - ACTIVE, - WIN, - LOSS, -} - -export interface SessionHistory { - seed: string; - playTime: number; - result: SessionHistoryResult; - gameMode: GameModes; - party: PokemonData[]; - modifiers: PersistentModifierData[]; - money: number; - waveIndex: number; - gameVersion: string; - timestamp: number; -} diff --git a/src/ui/pokedex-page-ui-handler.ts b/src/ui/pokedex-page-ui-handler.ts index 00e166f075d..5875e3394a2 100644 --- a/src/ui/pokedex-page-ui-handler.ts +++ b/src/ui/pokedex-page-ui-handler.ts @@ -20,7 +20,8 @@ import { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, normalForm } from import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters"; import { starterPassiveAbilities } from "#app/data/balance/passives"; import { PokemonType } from "#enums/pokemon-type"; -import type { DexEntry, StarterAttributes } from "#app/system/game-data"; +import type { StarterAttributes } from "#app/system/game-data"; +import type { DexEntry } from "#app/@types/dex-data"; import { AbilityAttr, DexAttr } from "#app/system/game-data"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import MessageUiHandler from "#app/ui/message-ui-handler"; diff --git a/src/ui/pokedex-ui-handler.ts b/src/ui/pokedex-ui-handler.ts index 8b3633d7422..96451041306 100644 --- a/src/ui/pokedex-ui-handler.ts +++ b/src/ui/pokedex-ui-handler.ts @@ -11,7 +11,8 @@ import { allSpecies, getPokemonSpeciesForm, getPokerusStarters, normalForm } fro import { getStarterValueFriendshipCap, speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters"; import { catchableSpecies } from "#app/data/balance/biomes"; import { PokemonType } from "#enums/pokemon-type"; -import type { DexAttrProps, DexEntry, StarterAttributes, StarterPreferences } from "#app/system/game-data"; +import type { DexAttrProps, StarterAttributes, StarterPreferences } from "#app/system/game-data"; +import type { DexEntry } from "#app/@types/dex-data"; import { AbilityAttr, DexAttr, loadStarterPreferences } from "#app/system/game-data"; import MessageUiHandler from "#app/ui/message-ui-handler"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "#app/ui/pokemon-icon-anim-handler"; diff --git a/src/ui/pokemon-info-container.ts b/src/ui/pokemon-info-container.ts index d8012a58875..3dbe3b7af7d 100644 --- a/src/ui/pokemon-info-container.ts +++ b/src/ui/pokemon-info-container.ts @@ -6,7 +6,8 @@ import { getNatureName } from "../data/nature"; import { PokemonType } from "#enums/pokemon-type"; import type Pokemon from "../field/pokemon"; import i18next from "i18next"; -import type { DexEntry, StarterDataEntry } from "../system/game-data"; +import type { StarterDataEntry } from "../system/game-data"; +import type { DexEntry } from "#app/@types/dex-data"; import { DexAttr } from "../system/game-data"; import { fixedInt, getShinyDescriptor } from "#app/utils/common"; import ConfirmUiHandler from "./confirm-ui-handler"; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 3bea0b08698..b7a7e0f3e6e 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -23,13 +23,8 @@ import { allSpecies, getPokemonSpeciesForm, getPokerusStarters } from "#app/data import { getStarterValueFriendshipCap, speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters"; import { PokemonType } from "#enums/pokemon-type"; import { GameModes } from "#app/game-mode"; -import type { - DexAttrProps, - DexEntry, - StarterMoveset, - StarterAttributes, - StarterPreferences, -} from "#app/system/game-data"; +import type { DexAttrProps, StarterMoveset, StarterAttributes, StarterPreferences } from "#app/system/game-data"; +import type { DexEntry } from "#app/@types/dex-data"; import { AbilityAttr, DexAttr, loadStarterPreferences, saveStarterPreferences } from "#app/system/game-data"; import { Tutorial, handleTutorial } from "#app/tutorial"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; From d3bc33cd4ea8a315ce32623a526530c1af928a48 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sat, 7 Jun 2025 17:37:09 -0700 Subject: [PATCH 16/23] [Misc] Remove `debug.js` reference from `index.html` --- index.html | 1 - 1 file changed, 1 deletion(-) diff --git a/index.html b/index.html index 111464b5e5c..d503617c13c 100644 --- a/index.html +++ b/index.html @@ -145,6 +145,5 @@ - \ No newline at end of file From ef6029ae4bf2f3b6517837d1d7b282e511049f69 Mon Sep 17 00:00:00 2001 From: Bertie690 <136088738+Bertie690@users.noreply.github.com> Date: Sat, 7 Jun 2025 20:44:58 -0400 Subject: [PATCH 17/23] [Refactor] Add methods `isPlayer` and `isEnemy` to reduce circular imports https://github.com/pagefaultgames/pokerogue/pull/5902 * Added functions `isPlayer` and `isEnemy` for type checking * Apply suggestions from Kev code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Fix merge issue * Split imports --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/battle-scene.ts | 13 ++--- src/data/abilities/ability.ts | 8 +-- src/data/moves/move.ts | 22 ++++---- src/field/pokemon.ts | 52 ++++++++++++------- src/messages.ts | 4 +- src/phases/faint-phase.ts | 5 +- src/phases/move-effect-phase.ts | 2 +- src/phases/quiet-form-change-phase.ts | 3 +- src/phases/switch-summon-phase.ts | 1 - ...an-offer-you-cant-refuse-encounter.test.ts | 4 +- .../the-pokemon-salesman-encounter.test.ts | 3 +- 11 files changed, 63 insertions(+), 54 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 598ab64881a..762e11ec9f5 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -811,6 +811,7 @@ export default class BattleScene extends SceneBase { } } + // TODO: Add a `getPartyOnSide` function for getting the party of a pokemon public getPlayerParty(): PlayerPokemon[] { return this.party; } @@ -3086,9 +3087,9 @@ export default class BattleScene extends SceneBase { const removeOld = itemModifier.stackCount === 0; - if (!removeOld || !source || this.removeModifier(itemModifier, !source.isPlayer())) { + if (!removeOld || !source || this.removeModifier(itemModifier, source.isEnemy())) { const addModifier = () => { - if (!matchingModifier || this.removeModifier(matchingModifier, !target.isPlayer())) { + if (!matchingModifier || this.removeModifier(matchingModifier, target.isEnemy())) { if (target.isPlayer()) { this.addModifier(newItemModifier, ignoreUpdate, playSound, false, instant); if (source && itemLost) { @@ -3492,12 +3493,12 @@ export default class BattleScene extends SceneBase { } if (matchingFormChange) { let phase: Phase; - if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet) { + if (pokemon.isPlayer() && !matchingFormChange.quiet) { phase = new FormChangePhase(pokemon, matchingFormChange, modal); } else { phase = new QuietFormChangePhase(pokemon, matchingFormChange); } - if (pokemon instanceof PlayerPokemon && !matchingFormChange.quiet && modal) { + if (pokemon.isPlayer() && !matchingFormChange.quiet && modal) { this.overridePhase(phase); } else if (delayed) { this.pushPhase(phase); @@ -3595,7 +3596,7 @@ export default class BattleScene extends SceneBase { activePokemon = activePokemon.concat(this.getEnemyParty()); for (const p of activePokemon) { keys.push(p.getSpriteKey(true)); - if (p instanceof PlayerPokemon) { + if (p.isPlayer()) { keys.push(p.getBattleSpriteKey(true, true)); } keys.push(p.species.getCryKey(p.formIndex)); @@ -3611,7 +3612,7 @@ export default class BattleScene extends SceneBase { * @param pokemon The (enemy) pokemon */ initFinalBossPhaseTwo(pokemon: Pokemon): void { - if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) { + if (pokemon.isEnemy() && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) { this.fadeOutBgm(fixedInt(2000), false); this.ui.showDialogue( battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin, diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index d20eddcb3cb..4d307b2ce6f 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -2617,7 +2617,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt } override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const party = pokemon instanceof PlayerPokemon ? globalScene.getPlayerField() : globalScene.getEnemyField(); + const party = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); return party.filter(p => p.isAllowedInBattle()).length > 0; } @@ -2629,7 +2629,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt * @param args - n/a */ override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { - const party = pokemon instanceof PlayerPokemon ? globalScene.getPlayerField() : globalScene.getEnemyField(); + const party = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); const allowedParty = party.filter(p => p.isAllowedInBattle()); if (!simulated) { @@ -5539,7 +5539,7 @@ class ForceSwitchOutHelper { * - Whether there are available party members to switch in. * - If the Pokémon is still alive (hp > 0), and if so, it leaves the field and a new SwitchPhase is initiated. */ - if (switchOutTarget instanceof PlayerPokemon) { + if (switchOutTarget.isPlayer()) { if (globalScene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) { return false; } @@ -5608,7 +5608,7 @@ class ForceSwitchOutHelper { */ public getSwitchOutCondition(pokemon: Pokemon, opponent: Pokemon): boolean { const switchOutTarget = pokemon; - const player = switchOutTarget instanceof PlayerPokemon; + const player = switchOutTarget.isPlayer(); if (player) { const blockedByAbility = new BooleanHolder(false); diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 1bd6db97005..f610c745fd2 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -837,7 +837,7 @@ export default class Move implements Localizable { aura.applyPreAttack(source, null, simulated, target, this, [ power ]); } - const alliedField: Pokemon[] = source instanceof PlayerPokemon ? globalScene.getPlayerField() : globalScene.getEnemyField(); + const alliedField: Pokemon[] = source.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, target, this, simulated, power)); power.value *= typeChangeMovePowerMultiplier.value; @@ -4125,7 +4125,7 @@ export class FriendshipPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const power = args[0] as NumberHolder; - const friendshipPower = Math.floor(Math.min(user instanceof PlayerPokemon ? user.friendship : user.species.baseFriendship, 255) / 2.5); + const friendshipPower = Math.floor(Math.min(user.isPlayer() ? user.friendship : user.species.baseFriendship, 255) / 2.5); power.value = Math.max(!this.invert ? friendshipPower : 102 - friendshipPower, 1); return true; @@ -6149,14 +6149,14 @@ export class RevivalBlessingAttr extends MoveEffectAttr { * @param target {@linkcode Pokemon} target of this move * @param move {@linkcode Move} being used * @param args N/A - * @returns Promise, true if function succeeds. + * @returns `true` if function succeeds. */ override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { // If user is player, checks if the user has fainted pokemon - if (user instanceof PlayerPokemon) { + if (user.isPlayer()) { globalScene.unshiftPhase(new RevivalBlessingPhase(user)); return true; - } else if (user instanceof EnemyPokemon && user.hasTrainer() && globalScene.getEnemyParty().findIndex((p) => p.isFainted() && !p.isBoss()) > -1) { + } else if (user.isEnemy() && user.hasTrainer() && globalScene.getEnemyParty().findIndex((p) => p.isFainted() && !p.isBoss()) > -1) { // If used by an enemy trainer with at least one fainted non-boss Pokemon, this // revives one of said Pokemon selected at random. const faintedPokemon = globalScene.getEnemyParty().filter((p) => p.isFainted() && !p.isBoss()); @@ -6187,10 +6187,8 @@ export class RevivalBlessingAttr extends MoveEffectAttr { getCondition(): MoveConditionFunc { return (user, target, move) => - (user instanceof PlayerPokemon && globalScene.getPlayerParty().some((p) => p.isFainted())) || - (user instanceof EnemyPokemon && - user.hasTrainer() && - globalScene.getEnemyParty().some((p) => p.isFainted() && !p.isBoss())); + user.hasTrainer() && + (user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).some((p: Pokemon) => p.isFainted() && !p.isBoss()); } override getUserBenefitScore(user: Pokemon, _target: Pokemon, _move: Move): number { @@ -6228,7 +6226,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { // (e.g. when it uses Flip Turn), make it spit out the Tatsugiri before switching out. switchOutTarget.lapseTag(BattlerTagType.COMMANDED); - if (switchOutTarget instanceof PlayerPokemon) { + if (switchOutTarget.isPlayer()) { /** * Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch * If it did, the user of U-turn or Volt Switch will not be switched out. @@ -6382,7 +6380,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { getSwitchOutCondition(): MoveConditionFunc { return (user, target, move) => { const switchOutTarget = (this.selfSwitch ? user : target); - const player = switchOutTarget instanceof PlayerPokemon; + const player = switchOutTarget.isPlayer(); const forceSwitchAttr = move.getAttrs(ForceSwitchOutAttr).find(attr => attr.switchType === SwitchType.FORCE_SWITCH); if (!this.selfSwitch) { @@ -9857,7 +9855,7 @@ export function initMoves() { const lastEnemyFaint = globalScene.currentBattle.enemyFaintsHistory[globalScene.currentBattle.enemyFaintsHistory.length - 1]; return ( (lastPlayerFaint !== undefined && turn - lastPlayerFaint.turn === 1 && user.isPlayer()) || - (lastEnemyFaint !== undefined && turn - lastEnemyFaint.turn === 1 && !user.isPlayer()) + (lastEnemyFaint !== undefined && turn - lastEnemyFaint.turn === 1 && user.isEnemy()) ) ? 2 : 1; }), new AttackMove(MoveId.FINAL_GAMBIT, PokemonType.FIGHTING, MoveCategory.SPECIAL, -1, 100, 5, -1, 0, 5) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index df953f06834..d26dfd193f9 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -503,7 +503,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (level > 1) { const fused = new BooleanHolder(globalScene.gameMode.isSplicedOnly); - if (!fused.value && !this.isPlayer() && !this.hasTrainer()) { + if (!fused.value && this.isEnemy() && !this.hasTrainer()) { globalScene.applyModifier(EnemyFusionChanceModifier, false, fused); } @@ -788,7 +788,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return true; } - abstract isPlayer(): boolean; + abstract isPlayer(): this is PlayerPokemon; + + abstract isEnemy(): this is EnemyPokemon; abstract hasTrainer(): boolean; @@ -2050,7 +2052,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (Overrides.ABILITY_OVERRIDE && this.isPlayer()) { return allAbilities[Overrides.ABILITY_OVERRIDE]; } - if (Overrides.OPP_ABILITY_OVERRIDE && !this.isPlayer()) { + if (Overrides.OPP_ABILITY_OVERRIDE && this.isEnemy()) { return allAbilities[Overrides.OPP_ABILITY_OVERRIDE]; } if (this.isFusion()) { @@ -2080,7 +2082,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (Overrides.PASSIVE_ABILITY_OVERRIDE && this.isPlayer()) { return allAbilities[Overrides.PASSIVE_ABILITY_OVERRIDE]; } - if (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE && !this.isPlayer()) { + if (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE && this.isEnemy()) { return allAbilities[Overrides.OPP_PASSIVE_ABILITY_OVERRIDE]; } if (!isNullOrUndefined(this.customPokemonData.passive) && this.customPokemonData.passive !== -1) { @@ -2152,7 +2154,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // returns override if valid for current case if ( (Overrides.HAS_PASSIVE_ABILITY_OVERRIDE === false && this.isPlayer()) || - (Overrides.OPP_HAS_PASSIVE_ABILITY_OVERRIDE === false && !this.isPlayer()) + (Overrides.OPP_HAS_PASSIVE_ABILITY_OVERRIDE === false && this.isEnemy()) ) { return false; } @@ -2160,7 +2162,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ((Overrides.PASSIVE_ABILITY_OVERRIDE !== AbilityId.NONE || Overrides.HAS_PASSIVE_ABILITY_OVERRIDE) && this.isPlayer()) || ((Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== AbilityId.NONE || Overrides.OPP_HAS_PASSIVE_ABILITY_OVERRIDE) && - !this.isPlayer()) + this.isEnemy()) ) { return true; } @@ -2169,7 +2171,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const { currentBattle, gameMode } = globalScene; const waveIndex = currentBattle?.waveIndex; if ( - this instanceof EnemyPokemon && + this.isEnemy() && (currentBattle?.battleSpec === BattleSpec.FINAL_BOSS || gameMode.isEndlessMinorBoss(waveIndex) || gameMode.isEndlessMajorBoss(waveIndex)) @@ -2979,9 +2981,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let fusionOverride: PokemonSpecies | undefined = undefined; - if (forStarter && this instanceof PlayerPokemon && Overrides.STARTER_FUSION_SPECIES_OVERRIDE) { + if (forStarter && this.isPlayer() && Overrides.STARTER_FUSION_SPECIES_OVERRIDE) { fusionOverride = getPokemonSpecies(Overrides.STARTER_FUSION_SPECIES_OVERRIDE); - } else if (this instanceof EnemyPokemon && Overrides.OPP_FUSION_SPECIES_OVERRIDE) { + } else if (this.isEnemy() && Overrides.OPP_FUSION_SPECIES_OVERRIDE) { fusionOverride = getPokemonSpecies(Overrides.OPP_FUSION_SPECIES_OVERRIDE); } @@ -3292,7 +3294,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.battleInfo.setX(this.battleInfo.x + (this.isPlayer() ? 150 : !this.isBoss() ? -150 : -198)); this.battleInfo.setVisible(true); if (this.isPlayer()) { - this.battleInfo.expMaskRect.x += 150; + // TODO: How do you get this to not require a private property access? + this["battleInfo"].expMaskRect.x += 150; } globalScene.tweens.add({ targets: [this.battleInfo, this.battleInfo.expMaskRect], @@ -3313,7 +3316,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ease: "Cubic.easeIn", onComplete: () => { if (this.isPlayer()) { - this.battleInfo.expMaskRect.x -= 150; + // TODO: How do you get this to not require a private property access? + this["battleInfo"].expMaskRect.x -= 150; } this.battleInfo.setVisible(false); this.battleInfo.setX(this.battleInfo.x - (this.isPlayer() ? 150 : !this.isBoss() ? -150 : -198)); @@ -3408,7 +3412,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns An array of Pokémon on the allied field. */ getAlliedField(): Pokemon[] { - return this instanceof PlayerPokemon ? globalScene.getPlayerField() : globalScene.getEnemyField(); + return this.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); } /** @@ -4260,7 +4264,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Copy all stat stages for (const s of BATTLE_STATS) { const sourceStage = source.getStatStage(s); - if (this instanceof PlayerPokemon && sourceStage === 6) { + if (this.isPlayer() && sourceStage === 6) { globalScene.validateAchv(achvs.TRANSFER_MAX_STAT_STAGE); } this.setStatStage(s, sourceStage); @@ -5468,7 +5472,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { heldItem.stackCount--; if (heldItem.stackCount <= 0) { - globalScene.removeModifier(heldItem, !this.isPlayer()); + globalScene.removeModifier(heldItem, this.isEnemy()); } if (forBattle) { applyPostItemLostAbAttrs(PostItemLostAbAttr, this, false); @@ -5541,15 +5545,19 @@ export class PlayerPokemon extends Pokemon { this.battleInfo.initInfo(this); } - isPlayer(): boolean { + override isPlayer(): this is PlayerPokemon { return true; } - hasTrainer(): boolean { + override isEnemy(): this is EnemyPokemon { + return false; + } + + override hasTrainer(): boolean { return true; } - isBoss(): boolean { + override isBoss(): boolean { return false; } @@ -6494,15 +6502,19 @@ export class EnemyPokemon extends Pokemon { return [sortedBenefitScores[targetIndex][0]]; } - isPlayer() { + override isPlayer(): this is PlayerPokemon { return false; } - hasTrainer(): boolean { + override isEnemy(): this is EnemyPokemon { + return true; + } + + override hasTrainer(): boolean { return !!this.trainerSlot; } - isBoss(): boolean { + override isBoss(): boolean { return !!this.bossSegments; } diff --git a/src/messages.ts b/src/messages.ts index c29151a98b3..21473eb8361 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -16,7 +16,7 @@ export function getPokemonNameWithAffix(pokemon: Pokemon | undefined, useIllusio switch (globalScene.currentBattle.battleSpec) { case BattleSpec.DEFAULT: - return !pokemon.isPlayer() + return pokemon.isEnemy() ? pokemon.hasTrainer() ? i18next.t("battle:foePokemonWithAffix", { pokemonName: pokemon.getNameToRender(useIllusion), @@ -26,7 +26,7 @@ export function getPokemonNameWithAffix(pokemon: Pokemon | undefined, useIllusio }) : pokemon.getNameToRender(useIllusion); case BattleSpec.FINAL_BOSS: - return !pokemon.isPlayer() + return pokemon.isEnemy() ? i18next.t("battle:foePokemonWithAffix", { pokemonName: pokemon.getNameToRender(useIllusion) }) : pokemon.getNameToRender(useIllusion); default: diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 7332d6b9462..fac0947c4e7 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -18,7 +18,8 @@ import { BattleSpec } from "#app/enums/battle-spec"; import { StatusEffect } from "#app/enums/status-effect"; import type { EnemyPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { HitResult, PlayerPokemon, PokemonMove } from "#app/field/pokemon"; +import { HitResult, PokemonMove } from "#app/field/pokemon"; +import type { PlayerPokemon } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { PokemonInstantReviveModifier } from "#app/modifier/modifier"; import { SwitchType } from "#enums/switch-type"; @@ -203,7 +204,7 @@ export class FaintPhase extends PokemonPhase { } pokemon.faintCry(() => { - if (pokemon instanceof PlayerPokemon) { + if (pokemon.isPlayer()) { pokemon.addFriendship(-FRIENDSHIP_LOSS_FROM_FAINT); } pokemon.hideInfo(); diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index d80fdc89e6f..e62f65b029b 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -887,7 +887,7 @@ export class MoveEffectPhase extends PokemonPhase { sourceBattlerIndex: user.getBattlerIndex(), }); - if (user.isPlayer() && !target.isPlayer()) { + if (user.isPlayer() && target.isEnemy()) { globalScene.applyModifiers(DamageMoneyRewardModifier, true, user, new NumberHolder(damage)); } diff --git a/src/phases/quiet-form-change-phase.ts b/src/phases/quiet-form-change-phase.ts index 9f6b5cb3361..f677dcc48df 100644 --- a/src/phases/quiet-form-change-phase.ts +++ b/src/phases/quiet-form-change-phase.ts @@ -6,7 +6,6 @@ import { getTypeRgb } from "#app/data/type"; import { BattleSpec } from "#app/enums/battle-spec"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { BattlePhase } from "./battle-phase"; import type { MovePhase } from "./move-phase"; @@ -158,7 +157,7 @@ export class QuietFormChangePhase extends BattlePhase { end(): void { this.pokemon.findAndRemoveTags(t => t.tagType === BattlerTagType.AUTOTOMIZED); - if (globalScene?.currentBattle.battleSpec === BattleSpec.FINAL_BOSS && this.pokemon instanceof EnemyPokemon) { + if (globalScene?.currentBattle.battleSpec === BattleSpec.FINAL_BOSS && this.pokemon.isEnemy()) { globalScene.playBgm(); globalScene.unshiftPhase( new PokemonHealPhase(this.pokemon.getBattlerIndex(), this.pokemon.getMaxHp(), null, false, false, false, true), diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index d81ca6029c5..1872dae1f1f 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -139,7 +139,6 @@ export class SwitchSummonPhase extends SummonPhase { return; } - if (this.switchType === SwitchType.BATON_PASS) { // If switching via baton pass, update opposing tags coming from the prior pokemon (this.player ? globalScene.getEnemyField() : globalScene.getPlayerField()).forEach((enemyPokemon: Pokemon) => diff --git a/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts b/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts index f39ce753b10..786090aa8d6 100644 --- a/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts +++ b/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts @@ -8,7 +8,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils"; import type BattleScene from "#app/battle-scene"; -import { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/field/pokemon"; import { AnOfferYouCantRefuseEncounter } from "#app/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; @@ -105,7 +105,7 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => { i18next.t("ability:intimidate.name"), ); expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.moveOrAbility).toBe(i18next.t("ability:intimidate.name")); - expect(AnOfferYouCantRefuseEncounter.misc.pokemon instanceof PlayerPokemon).toBeTruthy(); + expect(AnOfferYouCantRefuseEncounter.misc.pokemon.isPlayer()).toBeTruthy(); expect(AnOfferYouCantRefuseEncounter.misc?.price?.toString()).toBe( AnOfferYouCantRefuseEncounter.dialogueTokens?.price, ); diff --git a/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts b/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts index 15fc3ffb00b..73632990dbf 100644 --- a/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts @@ -10,7 +10,6 @@ import { runSelectMysteryEncounterOption, } from "#test/mystery-encounter/encounter-test-utils"; import type BattleScene from "#app/battle-scene"; -import { PlayerPokemon } from "#app/field/pokemon"; import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; import { getSalesmanSpeciesOffer, @@ -99,7 +98,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => { expect(ThePokemonSalesmanEncounter.dialogueTokens?.purchasePokemon).toBeDefined(); expect(ThePokemonSalesmanEncounter.dialogueTokens?.price).toBeDefined(); - expect(ThePokemonSalesmanEncounter.misc.pokemon instanceof PlayerPokemon).toBeTruthy(); + expect(ThePokemonSalesmanEncounter.misc.pokemon.isPlayer()).toBeTruthy(); expect(ThePokemonSalesmanEncounter.misc?.price?.toString()).toBe(ThePokemonSalesmanEncounter.dialogueTokens?.price); expect(onInitResult).toBe(true); }); From 31140356733b1eda36546d1089165e24a7e0f150 Mon Sep 17 00:00:00 2001 From: Jimmybald1 <122436263+Jimmybald1@users.noreply.github.com> Date: Sun, 8 Jun 2025 02:54:33 +0200 Subject: [PATCH 18/23] [Balance] Update catch rates to Gen 9 (#5954) Updated catch rates to gen 9w Co-authored-by: Jimmybald1 <147992650+IBBCalc@users.noreply.github.com> --- src/data/pokemon-species.ts | 48 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index e946b526960..c5d798b5841 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -2022,9 +2022,9 @@ export function initSpecies() { new PokemonForm("Normal", "", PokemonType.GROUND, null, 3.5, 950, AbilityId.DROUGHT, AbilityId.NONE, AbilityId.NONE, 670, 100, 150, 140, 100, 90, 90, 3, 0, 335, false, null, true), new PokemonForm("Primal", "primal", PokemonType.GROUND, PokemonType.FIRE, 5, 999.7, AbilityId.DESOLATE_LAND, AbilityId.NONE, AbilityId.NONE, 770, 100, 180, 160, 150, 90, 90, 3, 0, 335), ), - new PokemonSpecies(SpeciesId.RAYQUAZA, 3, false, true, false, "Sky High Pokémon", PokemonType.DRAGON, PokemonType.FLYING, 7, 206.5, AbilityId.AIR_LOCK, AbilityId.NONE, AbilityId.NONE, 680, 105, 150, 90, 150, 90, 95, 45, 0, 340, GrowthRate.SLOW, null, false, true, - new PokemonForm("Normal", "", PokemonType.DRAGON, PokemonType.FLYING, 7, 206.5, AbilityId.AIR_LOCK, AbilityId.NONE, AbilityId.NONE, 680, 105, 150, 90, 150, 90, 95, 45, 0, 340, false, null, true), - new PokemonForm("Mega", SpeciesFormKey.MEGA, PokemonType.DRAGON, PokemonType.FLYING, 10.8, 392, AbilityId.DELTA_STREAM, AbilityId.NONE, AbilityId.NONE, 780, 105, 180, 100, 180, 100, 115, 45, 0, 340), + new PokemonSpecies(SpeciesId.RAYQUAZA, 3, false, true, false, "Sky High Pokémon", PokemonType.DRAGON, PokemonType.FLYING, 7, 206.5, AbilityId.AIR_LOCK, AbilityId.NONE, AbilityId.NONE, 680, 105, 150, 90, 150, 90, 95, 3, 0, 340, GrowthRate.SLOW, null, false, true, + new PokemonForm("Normal", "", PokemonType.DRAGON, PokemonType.FLYING, 7, 206.5, AbilityId.AIR_LOCK, AbilityId.NONE, AbilityId.NONE, 680, 105, 150, 90, 150, 90, 95, 3, 0, 340, false, null, true), + new PokemonForm("Mega", SpeciesFormKey.MEGA, PokemonType.DRAGON, PokemonType.FLYING, 10.8, 392, AbilityId.DELTA_STREAM, AbilityId.NONE, AbilityId.NONE, 780, 105, 180, 100, 180, 100, 115, 3, 0, 340), ), new PokemonSpecies(SpeciesId.JIRACHI, 3, false, false, true, "Wish Pokémon", PokemonType.STEEL, PokemonType.PSYCHIC, 0.3, 1.1, AbilityId.SERENE_GRACE, AbilityId.NONE, AbilityId.NONE, 600, 100, 100, 100, 100, 100, 100, 3, 100, 300, GrowthRate.SLOW, null, false), new PokemonSpecies(SpeciesId.DEOXYS, 3, false, false, true, "DNA Pokémon", PokemonType.PSYCHIC, null, 1.7, 60.8, AbilityId.PRESSURE, AbilityId.NONE, AbilityId.NONE, 600, 50, 150, 50, 150, 50, 150, 3, 0, 300, GrowthRate.SLOW, null, false, true, @@ -2270,10 +2270,10 @@ export function initSpecies() { new PokemonSpecies(SpeciesId.WHIMSICOTT, 5, false, false, false, "Windveiled Pokémon", PokemonType.GRASS, PokemonType.FAIRY, 0.7, 6.6, AbilityId.PRANKSTER, AbilityId.INFILTRATOR, AbilityId.CHLOROPHYLL, 480, 60, 67, 85, 77, 75, 116, 75, 50, 168, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(SpeciesId.PETILIL, 5, false, false, false, "Bulb Pokémon", PokemonType.GRASS, null, 0.5, 6.6, AbilityId.CHLOROPHYLL, AbilityId.OWN_TEMPO, AbilityId.LEAF_GUARD, 280, 45, 35, 50, 70, 50, 30, 190, 50, 56, GrowthRate.MEDIUM_FAST, 0, false), new PokemonSpecies(SpeciesId.LILLIGANT, 5, false, false, false, "Flowering Pokémon", PokemonType.GRASS, null, 1.1, 16.3, AbilityId.CHLOROPHYLL, AbilityId.OWN_TEMPO, AbilityId.LEAF_GUARD, 480, 70, 60, 75, 110, 75, 90, 75, 50, 168, GrowthRate.MEDIUM_FAST, 0, false), - new PokemonSpecies(SpeciesId.BASCULIN, 5, false, false, false, "Hostile Pokémon", PokemonType.WATER, null, 1, 18, AbilityId.RECKLESS, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 25, 50, 161, GrowthRate.MEDIUM_FAST, 50, false, false, - new PokemonForm("Red-Striped Form", "red-striped", PokemonType.WATER, null, 1, 18, AbilityId.RECKLESS, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 25, 50, 161, false, null, true), - new PokemonForm("Blue-Striped Form", "blue-striped", PokemonType.WATER, null, 1, 18, AbilityId.ROCK_HEAD, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 25, 50, 161, false, null, true), - new PokemonForm("White-Striped Form", "white-striped", PokemonType.WATER, null, 1, 18, AbilityId.RATTLED, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 25, 50, 161, false, null, true), + new PokemonSpecies(SpeciesId.BASCULIN, 5, false, false, false, "Hostile Pokémon", PokemonType.WATER, null, 1, 18, AbilityId.RECKLESS, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 190, 50, 161, GrowthRate.MEDIUM_FAST, 50, false, false, + new PokemonForm("Red-Striped Form", "red-striped", PokemonType.WATER, null, 1, 18, AbilityId.RECKLESS, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 190, 50, 161, false, null, true), + new PokemonForm("Blue-Striped Form", "blue-striped", PokemonType.WATER, null, 1, 18, AbilityId.ROCK_HEAD, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 190, 50, 161, false, null, true), + new PokemonForm("White-Striped Form", "white-striped", PokemonType.WATER, null, 1, 18, AbilityId.RATTLED, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 460, 70, 92, 65, 80, 55, 98, 190, 50, 161, false, null, true), ), new PokemonSpecies(SpeciesId.SANDILE, 5, false, false, false, "Desert Croc Pokémon", PokemonType.GROUND, PokemonType.DARK, 0.7, 15.2, AbilityId.INTIMIDATE, AbilityId.MOXIE, AbilityId.ANGER_POINT, 292, 50, 72, 35, 35, 35, 65, 180, 50, 58, GrowthRate.MEDIUM_SLOW, 50, false), new PokemonSpecies(SpeciesId.KROKOROK, 5, false, false, false, "Desert Croc Pokémon", PokemonType.GROUND, PokemonType.DARK, 1, 33.4, AbilityId.INTIMIDATE, AbilityId.MOXIE, AbilityId.ANGER_POINT, 351, 60, 82, 45, 45, 45, 74, 90, 50, 123, GrowthRate.MEDIUM_SLOW, 50, false), @@ -2740,10 +2740,10 @@ export function initSpecies() { new PokemonSpecies(SpeciesId.TAPU_LELE, 7, true, false, false, "Land Spirit Pokémon", PokemonType.PSYCHIC, PokemonType.FAIRY, 1.2, 18.6, AbilityId.PSYCHIC_SURGE, AbilityId.NONE, AbilityId.TELEPATHY, 570, 70, 85, 75, 130, 115, 95, 3, 50, 285, GrowthRate.SLOW, null, false), new PokemonSpecies(SpeciesId.TAPU_BULU, 7, true, false, false, "Land Spirit Pokémon", PokemonType.GRASS, PokemonType.FAIRY, 1.9, 45.5, AbilityId.GRASSY_SURGE, AbilityId.NONE, AbilityId.TELEPATHY, 570, 70, 130, 115, 85, 95, 75, 3, 50, 285, GrowthRate.SLOW, null, false), new PokemonSpecies(SpeciesId.TAPU_FINI, 7, true, false, false, "Land Spirit Pokémon", PokemonType.WATER, PokemonType.FAIRY, 1.3, 21.2, AbilityId.MISTY_SURGE, AbilityId.NONE, AbilityId.TELEPATHY, 570, 70, 75, 115, 95, 130, 85, 3, 50, 285, GrowthRate.SLOW, null, false), - new PokemonSpecies(SpeciesId.COSMOG, 7, true, false, false, "Nebula Pokémon", PokemonType.PSYCHIC, null, 0.2, 0.1, AbilityId.UNAWARE, AbilityId.NONE, AbilityId.NONE, 200, 43, 29, 31, 29, 31, 37, 45, 0, 40, GrowthRate.SLOW, null, false), - new PokemonSpecies(SpeciesId.COSMOEM, 7, true, false, false, "Protostar Pokémon", PokemonType.PSYCHIC, null, 0.1, 999.9, AbilityId.STURDY, AbilityId.NONE, AbilityId.NONE, 400, 43, 29, 131, 29, 131, 37, 45, 0, 140, GrowthRate.SLOW, null, false), - new PokemonSpecies(SpeciesId.SOLGALEO, 7, false, true, false, "Sunne Pokémon", PokemonType.PSYCHIC, PokemonType.STEEL, 3.4, 230, AbilityId.FULL_METAL_BODY, AbilityId.NONE, AbilityId.NONE, 680, 137, 137, 107, 113, 89, 97, 45, 0, 340, GrowthRate.SLOW, null, false), - new PokemonSpecies(SpeciesId.LUNALA, 7, false, true, false, "Moone Pokémon", PokemonType.PSYCHIC, PokemonType.GHOST, 4, 120, AbilityId.SHADOW_SHIELD, AbilityId.NONE, AbilityId.NONE, 680, 137, 113, 89, 137, 107, 97, 45, 0, 340, GrowthRate.SLOW, null, false), + new PokemonSpecies(SpeciesId.COSMOG, 7, true, false, false, "Nebula Pokémon", PokemonType.PSYCHIC, null, 0.2, 0.1, AbilityId.UNAWARE, AbilityId.NONE, AbilityId.NONE, 200, 43, 29, 31, 29, 31, 37, 3, 0, 40, GrowthRate.SLOW, null, false), + new PokemonSpecies(SpeciesId.COSMOEM, 7, true, false, false, "Protostar Pokémon", PokemonType.PSYCHIC, null, 0.1, 999.9, AbilityId.STURDY, AbilityId.NONE, AbilityId.NONE, 400, 43, 29, 131, 29, 131, 37, 3, 0, 140, GrowthRate.SLOW, null, false), + new PokemonSpecies(SpeciesId.SOLGALEO, 7, false, true, false, "Sunne Pokémon", PokemonType.PSYCHIC, PokemonType.STEEL, 3.4, 230, AbilityId.FULL_METAL_BODY, AbilityId.NONE, AbilityId.NONE, 680, 137, 137, 107, 113, 89, 97, 3, 0, 340, GrowthRate.SLOW, null, false), + new PokemonSpecies(SpeciesId.LUNALA, 7, false, true, false, "Moone Pokémon", PokemonType.PSYCHIC, PokemonType.GHOST, 4, 120, AbilityId.SHADOW_SHIELD, AbilityId.NONE, AbilityId.NONE, 680, 137, 113, 89, 137, 107, 97, 3, 0, 340, GrowthRate.SLOW, null, false), new PokemonSpecies(SpeciesId.NIHILEGO, 7, true, false, false, "Parasite Pokémon", PokemonType.ROCK, PokemonType.POISON, 1.2, 55.5, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 109, 53, 47, 127, 131, 103, 45, 0, 285, GrowthRate.SLOW, null, false), new PokemonSpecies(SpeciesId.BUZZWOLE, 7, true, false, false, "Swollen Pokémon", PokemonType.BUG, PokemonType.FIGHTING, 2.4, 333.6, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 107, 139, 139, 53, 53, 79, 45, 0, 285, GrowthRate.SLOW, null, false), new PokemonSpecies(SpeciesId.PHEROMOSA, 7, true, false, false, "Lissome Pokémon", PokemonType.BUG, PokemonType.FIGHTING, 1.8, 25, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 71, 137, 37, 137, 37, 151, 45, 0, 285, GrowthRate.SLOW, null, false), @@ -2751,11 +2751,11 @@ export function initSpecies() { new PokemonSpecies(SpeciesId.CELESTEELA, 7, true, false, false, "Launch Pokémon", PokemonType.STEEL, PokemonType.FLYING, 9.2, 999.9, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 97, 101, 103, 107, 101, 61, 45, 0, 285, GrowthRate.SLOW, null, false), new PokemonSpecies(SpeciesId.KARTANA, 7, true, false, false, "Drawn Sword Pokémon", PokemonType.GRASS, PokemonType.STEEL, 0.3, 0.1, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 59, 181, 131, 59, 31, 109, 45, 0, 285, GrowthRate.SLOW, null, false), new PokemonSpecies(SpeciesId.GUZZLORD, 7, true, false, false, "Junkivore Pokémon", PokemonType.DARK, PokemonType.DRAGON, 5.5, 888, AbilityId.BEAST_BOOST, AbilityId.NONE, AbilityId.NONE, 570, 223, 101, 53, 97, 53, 43, 45, 0, 285, GrowthRate.SLOW, null, false), - new PokemonSpecies(SpeciesId.NECROZMA, 7, false, true, false, "Prism Pokémon", PokemonType.PSYCHIC, null, 2.4, 230, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 600, 97, 107, 101, 127, 89, 79, 255, 0, 300, GrowthRate.SLOW, null, false, false, - new PokemonForm("Normal", "", PokemonType.PSYCHIC, null, 2.4, 230, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 600, 97, 107, 101, 127, 89, 79, 255, 0, 300, false, null, true), - new PokemonForm("Dusk Mane", "dusk-mane", PokemonType.PSYCHIC, PokemonType.STEEL, 3.8, 460, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 680, 97, 157, 127, 113, 109, 77, 255, 0, 340), - new PokemonForm("Dawn Wings", "dawn-wings", PokemonType.PSYCHIC, PokemonType.GHOST, 4.2, 350, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 680, 97, 113, 109, 157, 127, 77, 255, 0, 340), - new PokemonForm("Ultra", "ultra", PokemonType.PSYCHIC, PokemonType.DRAGON, 7.5, 230, AbilityId.NEUROFORCE, AbilityId.NONE, AbilityId.NONE, 754, 97, 167, 97, 167, 97, 129, 255, 0, 377), + new PokemonSpecies(SpeciesId.NECROZMA, 7, false, true, false, "Prism Pokémon", PokemonType.PSYCHIC, null, 2.4, 230, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 600, 97, 107, 101, 127, 89, 79, 3, 0, 300, GrowthRate.SLOW, null, false, false, + new PokemonForm("Normal", "", PokemonType.PSYCHIC, null, 2.4, 230, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 600, 97, 107, 101, 127, 89, 79, 3, 0, 300, false, null, true), + new PokemonForm("Dusk Mane", "dusk-mane", PokemonType.PSYCHIC, PokemonType.STEEL, 3.8, 460, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 680, 97, 157, 127, 113, 109, 77, 3, 0, 340), + new PokemonForm("Dawn Wings", "dawn-wings", PokemonType.PSYCHIC, PokemonType.GHOST, 4.2, 350, AbilityId.PRISM_ARMOR, AbilityId.NONE, AbilityId.NONE, 680, 97, 113, 109, 157, 127, 77, 3, 0, 340), + new PokemonForm("Ultra", "ultra", PokemonType.PSYCHIC, PokemonType.DRAGON, 7.5, 230, AbilityId.NEUROFORCE, AbilityId.NONE, AbilityId.NONE, 754, 97, 167, 97, 167, 97, 129, 3, 0, 377), ), new PokemonSpecies(SpeciesId.MAGEARNA, 7, false, false, true, "Artificial Pokémon", PokemonType.STEEL, PokemonType.FAIRY, 1, 80.5, AbilityId.SOUL_HEART, AbilityId.NONE, AbilityId.NONE, 600, 80, 95, 115, 130, 115, 65, 3, 0, 300, GrowthRate.SLOW, null, false, false, new PokemonForm("Normal", "", PokemonType.STEEL, PokemonType.FAIRY, 1, 80.5, AbilityId.SOUL_HEART, AbilityId.NONE, AbilityId.NONE, 600, 80, 95, 115, 130, 115, 65, 3, 0, 300, false, null, true), @@ -2964,15 +2964,15 @@ export function initSpecies() { new PokemonForm("Ice", "ice", PokemonType.PSYCHIC, PokemonType.ICE, 2.4, 809.1, AbilityId.AS_ONE_GLASTRIER, AbilityId.NONE, AbilityId.NONE, 680, 100, 165, 150, 85, 130, 50, 3, 100, 340), new PokemonForm("Shadow", "shadow", PokemonType.PSYCHIC, PokemonType.GHOST, 2.4, 53.6, AbilityId.AS_ONE_SPECTRIER, AbilityId.NONE, AbilityId.NONE, 680, 100, 85, 80, 165, 100, 150, 3, 100, 340), ), - new PokemonSpecies(SpeciesId.WYRDEER, 8, false, false, false, "Big Horn Pokémon", PokemonType.NORMAL, PokemonType.PSYCHIC, 1.8, 95.1, AbilityId.INTIMIDATE, AbilityId.FRISK, AbilityId.SAP_SIPPER, 525, 103, 105, 72, 105, 75, 65, 135, 50, 263, GrowthRate.SLOW, 50, false), - new PokemonSpecies(SpeciesId.KLEAVOR, 8, false, false, false, "Axe Pokémon", PokemonType.BUG, PokemonType.ROCK, 1.8, 89, AbilityId.SWARM, AbilityId.SHEER_FORCE, AbilityId.SHARPNESS, 500, 70, 135, 95, 45, 70, 85, 115, 50, 175, GrowthRate.MEDIUM_FAST, 50, false), - new PokemonSpecies(SpeciesId.URSALUNA, 8, false, false, false, "Peat Pokémon", PokemonType.GROUND, PokemonType.NORMAL, 2.4, 290, AbilityId.GUTS, AbilityId.BULLETPROOF, AbilityId.UNNERVE, 550, 130, 140, 105, 45, 80, 50, 75, 50, 275, GrowthRate.MEDIUM_FAST, 50, false), - new PokemonSpecies(SpeciesId.BASCULEGION, 8, false, false, false, "Big Fish Pokémon", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 112, 65, 80, 75, 78, 135, 50, 265, GrowthRate.MEDIUM_FAST, 50, false, false, - new PokemonForm("Male", "male", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 112, 65, 80, 75, 78, 135, 50, 265, false, "", true), - new PokemonForm("Female", "female", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 92, 65, 100, 75, 78, 135, 50, 265, false, null, true), + new PokemonSpecies(SpeciesId.WYRDEER, 8, false, false, false, "Big Horn Pokémon", PokemonType.NORMAL, PokemonType.PSYCHIC, 1.8, 95.1, AbilityId.INTIMIDATE, AbilityId.FRISK, AbilityId.SAP_SIPPER, 525, 103, 105, 72, 105, 75, 65, 45, 50, 263, GrowthRate.SLOW, 50, false), + new PokemonSpecies(SpeciesId.KLEAVOR, 8, false, false, false, "Axe Pokémon", PokemonType.BUG, PokemonType.ROCK, 1.8, 89, AbilityId.SWARM, AbilityId.SHEER_FORCE, AbilityId.SHARPNESS, 500, 70, 135, 95, 45, 70, 85, 15, 50, 175, GrowthRate.MEDIUM_FAST, 50, false), + new PokemonSpecies(SpeciesId.URSALUNA, 8, false, false, false, "Peat Pokémon", PokemonType.GROUND, PokemonType.NORMAL, 2.4, 290, AbilityId.GUTS, AbilityId.BULLETPROOF, AbilityId.UNNERVE, 550, 130, 140, 105, 45, 80, 50, 20, 50, 275, GrowthRate.MEDIUM_FAST, 50, false), + new PokemonSpecies(SpeciesId.BASCULEGION, 8, false, false, false, "Big Fish Pokémon", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 112, 65, 80, 75, 78, 45, 50, 265, GrowthRate.MEDIUM_FAST, 50, false, false, + new PokemonForm("Male", "male", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 112, 65, 80, 75, 78, 45, 50, 265, false, "", true), + new PokemonForm("Female", "female", PokemonType.WATER, PokemonType.GHOST, 3, 110, AbilityId.SWIFT_SWIM, AbilityId.ADAPTABILITY, AbilityId.MOLD_BREAKER, 530, 120, 92, 65, 100, 75, 78, 45, 50, 265, false, null, true), ), - new PokemonSpecies(SpeciesId.SNEASLER, 8, false, false, false, "Free Climb Pokémon", PokemonType.FIGHTING, PokemonType.POISON, 1.3, 43, AbilityId.PRESSURE, AbilityId.UNBURDEN, AbilityId.POISON_TOUCH, 510, 80, 130, 60, 40, 80, 120, 135, 50, 102, GrowthRate.MEDIUM_SLOW, 50, false), - new PokemonSpecies(SpeciesId.OVERQWIL, 8, false, false, false, "Pin Cluster Pokémon", PokemonType.DARK, PokemonType.POISON, 2.5, 60.5, AbilityId.POISON_POINT, AbilityId.SWIFT_SWIM, AbilityId.INTIMIDATE, 510, 85, 115, 95, 65, 65, 85, 135, 50, 179, GrowthRate.MEDIUM_FAST, 50, false), + new PokemonSpecies(SpeciesId.SNEASLER, 8, false, false, false, "Free Climb Pokémon", PokemonType.FIGHTING, PokemonType.POISON, 1.3, 43, AbilityId.PRESSURE, AbilityId.UNBURDEN, AbilityId.POISON_TOUCH, 510, 80, 130, 60, 40, 80, 120, 20, 50, 102, GrowthRate.MEDIUM_SLOW, 50, false), + new PokemonSpecies(SpeciesId.OVERQWIL, 8, false, false, false, "Pin Cluster Pokémon", PokemonType.DARK, PokemonType.POISON, 2.5, 60.5, AbilityId.POISON_POINT, AbilityId.SWIFT_SWIM, AbilityId.INTIMIDATE, 510, 85, 115, 95, 65, 65, 85, 45, 50, 179, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(SpeciesId.ENAMORUS, 8, true, false, false, "Love-Hate Pokémon", PokemonType.FAIRY, PokemonType.FLYING, 1.6, 48, AbilityId.CUTE_CHARM, AbilityId.NONE, AbilityId.CONTRARY, 580, 74, 115, 70, 135, 80, 106, 3, 50, 116, GrowthRate.SLOW, 0, false, true, new PokemonForm("Incarnate Forme", "incarnate", PokemonType.FAIRY, PokemonType.FLYING, 1.6, 48, AbilityId.CUTE_CHARM, AbilityId.NONE, AbilityId.CONTRARY, 580, 74, 115, 70, 135, 80, 106, 3, 50, 116, false, null, true), new PokemonForm("Therian Forme", "therian", PokemonType.FAIRY, PokemonType.FLYING, 1.6, 48, AbilityId.OVERCOAT, AbilityId.NONE, AbilityId.OVERCOAT, 580, 74, 115, 110, 135, 100, 46, 3, 50, 116), From 93745f14b76309e8d34887b868a6fd561e28f10d Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Sat, 7 Jun 2025 19:59:30 -0500 Subject: [PATCH 19/23] [Refactor] Decouple phase system from battle-scene (#5953) * Move phase logic into its own class * Move ts ignore comment --- src/battle-scene.ts | 353 ++---------------- src/battle.ts | 2 +- src/data/abilities/ability.ts | 122 +++--- src/data/arena-tag.ts | 108 +++--- src/data/battler-tags.ts | 246 ++++++------ src/data/berry.ts | 16 +- src/data/moves/move.ts | 224 +++++------ .../encounters/a-trainers-test-encounter.ts | 2 +- .../encounters/absolute-avarice-encounter.ts | 2 +- .../an-offer-you-cant-refuse-encounter.ts | 2 +- .../encounters/berries-abound-encounter.ts | 2 +- .../encounters/bug-type-superfan-encounter.ts | 2 +- .../encounters/dancing-lessons-encounter.ts | 4 +- .../encounters/dark-deal-encounter.ts | 2 +- .../encounters/delibirdy-encounter.ts | 12 +- .../encounters/fiery-fallout-encounter.ts | 4 +- .../encounters/fight-or-flight-encounter.ts | 4 +- .../encounters/fun-and-games-encounter.ts | 4 +- .../encounters/mysterious-chest-encounter.ts | 4 +- .../encounters/safari-zone-encounter.ts | 8 +- .../slumbering-snorlax-encounter.ts | 2 +- .../teleporting-hijinks-encounter.ts | 4 +- .../the-expert-pokemon-breeder-encounter.ts | 4 +- .../encounters/the-strong-stuff-encounter.ts | 2 +- .../the-winstrate-challenge-encounter.ts | 6 +- .../encounters/uncommon-breed-encounter.ts | 2 +- .../utils/encounter-dialogue-utils.ts | 2 +- .../utils/encounter-phase-utils.ts | 52 +-- .../utils/encounter-pokemon-utils.ts | 2 +- src/field/arena.ts | 24 +- src/field/pokemon.ts | 30 +- src/modifier/modifier.ts | 30 +- src/phase-manager.ts | 311 +++++++++++++++ src/phase.ts | 2 +- src/phases/attempt-capture-phase.ts | 2 +- src/phases/attempt-run-phase.ts | 10 +- src/phases/battle-end-phase.ts | 8 +- src/phases/berry-phase.ts | 4 +- src/phases/check-status-effect-phase.ts | 2 +- src/phases/check-switch-phase.ts | 6 +- src/phases/command-phase.ts | 8 +- src/phases/egg-hatch-phase.ts | 2 +- src/phases/egg-lapse-phase.ts | 8 +- src/phases/encounter-phase.ts | 28 +- src/phases/evolution-phase.ts | 8 +- src/phases/exp-phase.ts | 2 +- src/phases/faint-phase.ts | 16 +- src/phases/form-change-phase.ts | 2 +- src/phases/game-over-phase.ts | 42 ++- src/phases/learn-move-phase.ts | 4 +- src/phases/level-up-phase.ts | 4 +- src/phases/login-phase.ts | 6 +- src/phases/message-phase.ts | 2 +- src/phases/move-charge-phase.ts | 4 +- src/phases/move-effect-phase.ts | 18 +- src/phases/move-phase.ts | 22 +- src/phases/mystery-encounter-phases.ts | 50 +-- src/phases/new-battle-phase.ts | 6 +- src/phases/obtain-status-effect-phase.ts | 4 +- src/phases/pokemon-heal-phase.ts | 8 +- src/phases/pokemon-transform-phase.ts | 2 +- src/phases/post-game-over-phase.ts | 2 +- src/phases/post-turn-status-effect-phase.ts | 2 +- src/phases/quiet-form-change-phase.ts | 6 +- src/phases/revival-blessing-phase.ts | 10 +- src/phases/select-biome-phase.ts | 4 +- src/phases/select-modifier-phase.ts | 4 +- src/phases/select-starter-phase.ts | 4 +- src/phases/select-target-phase.ts | 4 +- src/phases/show-ability-phase.ts | 4 +- src/phases/show-party-exp-bar-phase.ts | 4 +- src/phases/stat-stage-change-phase.ts | 14 +- src/phases/summon-phase.ts | 8 +- src/phases/switch-phase.ts | 8 +- src/phases/switch-summon-phase.ts | 2 +- src/phases/tera-phase.ts | 2 +- src/phases/title-phase.ts | 22 +- src/phases/trainer-victory-phase.ts | 10 +- src/phases/turn-end-phase.ts | 4 +- src/phases/turn-init-phase.ts | 18 +- src/phases/turn-start-phase.ts | 28 +- src/phases/unavailable-phase.ts | 2 +- src/phases/victory-phase.ts | 34 +- src/phases/weather-effect-phase.ts | 2 +- src/system/game-data.ts | 24 +- src/ui/ball-ui-handler.ts | 2 +- src/ui/challenges-select-ui-handler.ts | 10 +- src/ui/command-ui-handler.ts | 18 +- src/ui/egg-hatch-scene-handler.ts | 2 +- src/ui/egg-summary-ui-handler.ts | 2 +- src/ui/fight-ui-handler.ts | 12 +- src/ui/menu-ui-handler.ts | 2 +- src/ui/mystery-encounter-ui-handler.ts | 4 +- src/ui/party-ui-handler.ts | 6 +- src/ui/pokedex-page-ui-handler.ts | 2 +- src/ui/starter-select-ui-handler.ts | 10 +- test/abilities/cud_chew.test.ts | 4 +- test/abilities/dancer.test.ts | 8 +- test/abilities/desolate-land.test.ts | 2 +- test/abilities/disguise.test.ts | 2 +- test/abilities/honey_gather.test.ts | 2 +- test/abilities/imposter.test.ts | 4 +- test/abilities/mycelium_might.test.ts | 6 +- test/abilities/neutralizing_gas.test.ts | 2 +- test/abilities/no_guard.test.ts | 2 +- test/abilities/shield_dust.test.ts | 2 +- test/abilities/speed_boost.test.ts | 4 +- test/abilities/stall.test.ts | 6 +- test/battle/battle-order.test.ts | 10 +- test/battle/battle.test.ts | 12 +- test/battle/special_battle.test.ts | 18 +- test/battlerTags/octolock.test.ts | 4 +- test/battlerTags/stockpiling.test.ts | 32 +- test/battlerTags/substitute.test.ts | 31 +- test/escape-calculations.test.ts | 16 +- test/items/lock_capsule.test.ts | 4 +- test/moves/after_you.test.ts | 2 +- test/moves/dynamax_cannon.test.ts | 16 +- test/moves/focus_punch.test.ts | 4 +- test/moves/fusion_flare_bolt.test.ts | 36 +- test/moves/instruct.test.ts | 4 +- test/moves/round.test.ts | 4 +- test/moves/shell_trap.test.ts | 8 +- test/moves/substitute.test.ts | 2 +- test/moves/transform.test.ts | 4 +- test/moves/whirlwind.test.ts | 2 +- .../mystery-encounter/encounter-test-utils.ts | 12 +- .../a-trainers-test-encounter.test.ts | 8 +- .../absolute-avarice-encounter.test.ts | 6 +- .../berries-abound-encounter.test.ts | 12 +- .../bug-type-superfan-encounter.test.ts | 36 +- .../clowning-around-encounter.test.ts | 8 +- .../dancing-lessons-encounter.test.ts | 12 +- .../encounters/delibirdy-encounter.test.ts | 12 +- .../department-store-sale-encounter.test.ts | 8 +- .../fiery-fallout-encounter.test.ts | 12 +- .../fight-or-flight-encounter.test.ts | 10 +- .../fun-and-games-encounter.test.ts | 38 +- .../global-trade-system-encounter.test.ts | 2 +- .../encounters/lost-at-sea-encounter.test.ts | 8 +- .../mysterious-challengers-encounter.test.ts | 12 +- .../encounters/part-timer-encounter.test.ts | 4 +- .../encounters/safari-zone.test.ts | 4 +- .../teleporting-hijinks-encounter.test.ts | 14 +- .../the-expert-breeder-encounter.test.ts | 12 +- .../the-pokemon-salesman-encounter.test.ts | 4 +- .../the-strong-stuff-encounter.test.ts | 6 +- .../the-winstrate-challenge-encounter.test.ts | 14 +- .../trash-to-treasure-encounter.test.ts | 8 +- .../uncommon-breed-encounter.test.ts | 20 +- .../encounters/weird-dream-encounter.test.ts | 8 +- .../mystery-encounter-utils.test.ts | 4 +- .../mystery-encounter.test.ts | 2 +- test/phases/mystery-encounter-phase.test.ts | 6 +- test/phases/phases.test.ts | 6 +- test/phases/select-modifier-phase.test.ts | 12 +- test/testUtils/gameManager.ts | 22 +- test/testUtils/helpers/challengeModeHelper.ts | 2 +- test/testUtils/helpers/classicModeHelper.ts | 2 +- test/testUtils/helpers/moveHelper.ts | 27 +- test/testUtils/helpers/reloadHelper.ts | 4 +- test/testUtils/phaseInterceptor.ts | 12 +- test/ui/starter-select.test.ts | 18 +- 163 files changed, 1465 insertions(+), 1361 deletions(-) create mode 100644 src/phase-manager.ts diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 762e11ec9f5..23601910e4d 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -144,7 +144,6 @@ import { battleSpecDialogue } from "#app/data/dialogue"; import { LoadingScene } from "#app/loading-scene"; import { LevelCapPhase } from "#app/phases/level-cap-phase"; import { LoginPhase } from "#app/phases/login-phase"; -import { MessagePhase } from "#app/phases/message-phase"; import type { MovePhase } from "#app/phases/move-phase"; import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase"; import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; @@ -155,7 +154,6 @@ import { ShowTrainerPhase } from "#app/phases/show-trainer-phase"; import { SummonPhase } from "#app/phases/summon-phase"; import { TitlePhase } from "#app/phases/title-phase"; import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; -import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import { @@ -178,13 +176,12 @@ import { BattlerTagType } from "#enums/battler-tag-type"; import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters"; import { StatusEffect } from "#enums/status-effect"; import { initGlobalScene } from "#app/global-scene"; -import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; -import { HideAbilityPhase } from "#app/phases/hide-ability-phase"; import { expSpriteKeys } from "./sprites/sprite-keys"; import { hasExpSprite } from "./sprites/sprite-utils"; import { timedEventManager } from "./global-event-manager"; import { starterColors } from "./global-vars/starter-colors"; import { startingWave } from "./starting-wave"; +import { PhaseManager } from "./phase-manager"; const DEBUG_RNG = false; @@ -297,18 +294,8 @@ export default class BattleScene extends SceneBase { public gameData: GameData; public sessionSlotId: number; - /** PhaseQueue: dequeue/remove the first element to get the next phase */ - public phaseQueue: Phase[]; - public conditionalQueue: Array<[() => boolean, Phase]>; - /** PhaseQueuePrepend: is a temp storage of what will be added to PhaseQueue */ - private phaseQueuePrepend: Phase[]; - - /** overrides default of inserting phases to end of phaseQueuePrepend array, useful or inserting Phases "out of order" */ - private phaseQueuePrependSpliceIndex: number; - private nextCommandPhaseQueue: Phase[]; - - private currentPhase: Phase | null; - private standbyPhase: Phase | null; + /** Manager for the phases active in the battle scene */ + public readonly phaseManager: PhaseManager; public field: Phaser.GameObjects.Container; public fieldUI: Phaser.GameObjects.Container; public charSprite: CharSprite; @@ -396,11 +383,7 @@ export default class BattleScene extends SceneBase { constructor() { super("battle"); - this.phaseQueue = []; - this.phaseQueuePrepend = []; - this.conditionalQueue = []; - this.phaseQueuePrependSpliceIndex = -1; - this.nextCommandPhaseQueue = []; + this.phaseManager = new PhaseManager(); this.eventManager = new TimedEventManager(); this.updateGameInfo(); initGlobalScene(this); @@ -716,10 +699,10 @@ export default class BattleScene extends SceneBase { ).then(() => loadMoveAnimAssets(defaultMoves, true)), this.initStarterColors(), ]).then(() => { - this.pushPhase(new LoginPhase()); - this.pushPhase(new TitlePhase()); + this.phaseManager.pushPhase(new LoginPhase()); + this.phaseManager.pushPhase(new TitlePhase()); - this.shiftPhase(); + this.phaseManager.shiftPhase(); }); } @@ -899,7 +882,7 @@ export default class BattleScene extends SceneBase { if (allyPokemon?.isActive(true)) { let targetingMovePhase: MovePhase; do { - targetingMovePhase = this.findPhase( + targetingMovePhase = this.phaseManager.findPhase( mp => mp.is("MovePhase") && mp.targets.length === 1 && @@ -1277,7 +1260,7 @@ export default class BattleScene extends SceneBase { duration: 250, ease: "Sine.easeInOut", onComplete: () => { - this.clearPhaseQueue(); + this.phaseManager.clearPhaseQueue(); this.ui.freeUIData(); this.uiContainer.remove(this.ui, true); @@ -1450,7 +1433,7 @@ export default class BattleScene extends SceneBase { } if (lastBattle?.double && !newDouble) { - this.tryRemovePhase((p: Phase) => p.is("SwitchPhase")); + this.phaseManager.tryRemovePhase((p: Phase) => p.is("SwitchPhase")); for (const p of this.getPlayerField()) { p.lapseTag(BattlerTagType.COMMANDED); } @@ -1492,7 +1475,7 @@ export default class BattleScene extends SceneBase { playerField.forEach((pokemon, p) => { if (pokemon.isOnField()) { - this.pushPhase(new ReturnPhase(p)); + this.phaseManager.pushPhase(new ReturnPhase(p)); } }); @@ -1509,7 +1492,7 @@ export default class BattleScene extends SceneBase { } if (!this.trainer.visible) { - this.pushPhase(new ShowTrainerPhase()); + this.phaseManager.pushPhase(new ShowTrainerPhase()); } } @@ -1518,13 +1501,13 @@ export default class BattleScene extends SceneBase { } if (!this.gameMode.hasRandomBiomes && !isNewBiome) { - this.pushPhase(new NextEncounterPhase()); + this.phaseManager.pushPhase(new NextEncounterPhase()); } else { - this.pushPhase(new NewBiomeEncounterPhase()); + this.phaseManager.pushPhase(new NewBiomeEncounterPhase()); const newMaxExpLevel = this.getMaxExpLevel(); if (newMaxExpLevel > maxExpLevel) { - this.pushPhase(new LevelCapPhase()); + this.phaseManager.pushPhase(new LevelCapPhase()); } } } @@ -1588,7 +1571,9 @@ export default class BattleScene extends SceneBase { return 0; } - const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes(this.getCurrentPhase()?.phaseName ?? ""); + const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes( + this.phaseManager.getCurrentPhase()?.phaseName ?? "", + ); if ( // Give trainers with specialty types an appropriately-typed form for Wormadam, Rotom, Arceus, Oricorio, Silvally, or Paldean Tauros. @@ -2615,286 +2600,6 @@ export default class BattleScene extends SceneBase { } } - /* Phase Functions */ - getCurrentPhase(): Phase | null { - return this.currentPhase; - } - - getStandbyPhase(): Phase | null { - return this.standbyPhase; - } - - /** - * Adds a phase to the conditional queue and ensures it is executed only when the specified condition is met. - * - * This method allows deferring the execution of a phase until certain conditions are met, which is useful for handling - * situations like abilities and entry hazards that depend on specific game states. - * - * @param {Phase} phase - The phase to be added to the conditional queue. - * @param {() => boolean} condition - A function that returns a boolean indicating whether the phase should be executed. - * - */ - pushConditionalPhase(phase: Phase, condition: () => boolean): void { - this.conditionalQueue.push([condition, phase]); - } - - /** - * Adds a phase to nextCommandPhaseQueue, as long as boolean passed in is false - * @param phase {@linkcode Phase} the phase to add - * @param defer boolean on which queue to add to, defaults to false, and adds to phaseQueue - */ - pushPhase(phase: Phase, defer = false): void { - (!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase); - } - - /** - * Adds Phase(s) to the end of phaseQueuePrepend, or at phaseQueuePrependSpliceIndex - * @param phases {@linkcode Phase} the phase(s) to add - */ - unshiftPhase(...phases: Phase[]): void { - if (this.phaseQueuePrependSpliceIndex === -1) { - this.phaseQueuePrepend.push(...phases); - } else { - this.phaseQueuePrepend.splice(this.phaseQueuePrependSpliceIndex, 0, ...phases); - } - } - - /** - * Clears the phaseQueue - */ - clearPhaseQueue(): void { - this.phaseQueue.splice(0, this.phaseQueue.length); - } - - /** - * Clears all phase-related stuff, including all phase queues, the current and standby phases, and a splice index - */ - clearAllPhases(): void { - for (const queue of [this.phaseQueue, this.phaseQueuePrepend, this.conditionalQueue, this.nextCommandPhaseQueue]) { - queue.splice(0, queue.length); - } - this.currentPhase = null; - this.standbyPhase = null; - this.clearPhaseQueueSplice(); - } - - /** - * Used by function unshiftPhase(), sets index to start inserting at current length instead of the end of the array, useful if phaseQueuePrepend gets longer with Phases - */ - setPhaseQueueSplice(): void { - this.phaseQueuePrependSpliceIndex = this.phaseQueuePrepend.length; - } - - /** - * Resets phaseQueuePrependSpliceIndex to -1, implies that calls to unshiftPhase will insert at end of phaseQueuePrepend - */ - clearPhaseQueueSplice(): void { - this.phaseQueuePrependSpliceIndex = -1; - } - - /** - * Is called by each Phase implementations "end()" by default - * We dump everything from phaseQueuePrepend to the start of of phaseQueue - * then removes first Phase and starts it - */ - shiftPhase(): void { - if (this.standbyPhase) { - this.currentPhase = this.standbyPhase; - this.standbyPhase = null; - return; - } - - if (this.phaseQueuePrependSpliceIndex > -1) { - this.clearPhaseQueueSplice(); - } - if (this.phaseQueuePrepend.length) { - while (this.phaseQueuePrepend.length) { - const poppedPhase = this.phaseQueuePrepend.pop(); - if (poppedPhase) { - this.phaseQueue.unshift(poppedPhase); - } - } - } - if (!this.phaseQueue.length) { - this.populatePhaseQueue(); - // Clear the conditionalQueue if there are no phases left in the phaseQueue - this.conditionalQueue = []; - } - - this.currentPhase = this.phaseQueue.shift() ?? null; - - // Check if there are any conditional phases queued - if (this.conditionalQueue?.length) { - // Retrieve the first conditional phase from the queue - const conditionalPhase = this.conditionalQueue.shift(); - // Evaluate the condition associated with the phase - if (conditionalPhase?.[0]()) { - // If the condition is met, add the phase to the phase queue - this.pushPhase(conditionalPhase[1]); - } else if (conditionalPhase) { - // If the condition is not met, re-add the phase back to the front of the conditional queue - this.conditionalQueue.unshift(conditionalPhase); - } else { - console.warn("condition phase is undefined/null!", conditionalPhase); - } - } - - if (this.currentPhase) { - console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;"); - this.currentPhase.start(); - } - } - - overridePhase(phase: Phase): boolean { - if (this.standbyPhase) { - return false; - } - - this.standbyPhase = this.currentPhase; - this.currentPhase = phase; - console.log(`%cStart Phase ${phase.constructor.name}`, "color:green;"); - phase.start(); - - return true; - } - - /** - * Find a specific {@linkcode Phase} in the phase queue. - * - * @param phaseFilter filter function to use to find the wanted phase - * @returns the found phase or undefined if none found - */ - findPhase

(phaseFilter: (phase: P) => boolean): P | undefined { - return this.phaseQueue.find(phaseFilter) as P; - } - - tryReplacePhase(phaseFilter: (phase: Phase) => boolean, phase: Phase): boolean { - const phaseIndex = this.phaseQueue.findIndex(phaseFilter); - if (phaseIndex > -1) { - this.phaseQueue[phaseIndex] = phase; - return true; - } - return false; - } - - tryRemovePhase(phaseFilter: (phase: Phase) => boolean): boolean { - const phaseIndex = this.phaseQueue.findIndex(phaseFilter); - if (phaseIndex > -1) { - this.phaseQueue.splice(phaseIndex, 1); - return true; - } - return false; - } - - /** - * Will search for a specific phase in {@linkcode phaseQueuePrepend} via filter, and remove the first result if a match is found. - * @param phaseFilter filter function - */ - tryRemoveUnshiftedPhase(phaseFilter: (phase: Phase) => boolean): boolean { - const phaseIndex = this.phaseQueuePrepend.findIndex(phaseFilter); - if (phaseIndex > -1) { - this.phaseQueuePrepend.splice(phaseIndex, 1); - return true; - } - return false; - } - - /** - * Tries to add the input phase to index before target phase in the phaseQueue, else simply calls unshiftPhase() - * @param phase {@linkcode Phase} the phase to be added - * @param targetPhase {@linkcode Phase} the type of phase to search for in phaseQueue - * @returns boolean if a targetPhase was found and added - */ - prependToPhase(phase: Phase | Phase[], targetPhase: Constructor): boolean { - if (!Array.isArray(phase)) { - phase = [phase]; - } - const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof targetPhase); - - if (targetIndex !== -1) { - this.phaseQueue.splice(targetIndex, 0, ...phase); - return true; - } - this.unshiftPhase(...phase); - return false; - } - - /** - * Tries to add the input phase(s) to index after target phase in the {@linkcode phaseQueue}, else simply calls {@linkcode unshiftPhase()} - * @param phase {@linkcode Phase} the phase(s) to be added - * @param targetPhase {@linkcode Phase} the type of phase to search for in {@linkcode phaseQueue} - * @returns `true` if a `targetPhase` was found to append to - */ - appendToPhase(phase: Phase | Phase[], targetPhase: Constructor): boolean { - if (!Array.isArray(phase)) { - phase = [phase]; - } - const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof targetPhase); - - if (targetIndex !== -1 && this.phaseQueue.length > targetIndex) { - this.phaseQueue.splice(targetIndex + 1, 0, ...phase); - return true; - } - this.unshiftPhase(...phase); - return false; - } - - /** - * Adds a MessagePhase, either to PhaseQueuePrepend or nextCommandPhaseQueue - * @param message string for MessagePhase - * @param callbackDelay optional param for MessagePhase constructor - * @param prompt optional param for MessagePhase constructor - * @param promptDelay optional param for MessagePhase constructor - * @param defer boolean for which queue to add it to, false -> add to PhaseQueuePrepend, true -> nextCommandPhaseQueue - */ - queueMessage( - message: string, - callbackDelay?: number | null, - prompt?: boolean | null, - promptDelay?: number | null, - defer?: boolean | null, - ) { - const phase = new MessagePhase(message, callbackDelay, prompt, promptDelay); - if (!defer) { - // adds to the end of PhaseQueuePrepend - this.unshiftPhase(phase); - } else { - //remember that pushPhase adds it to nextCommandPhaseQueue - this.pushPhase(phase); - } - } - - /** - * Queues an ability bar flyout phase - * @param pokemon The pokemon who has the ability - * @param passive Whether the ability is a passive - * @param show Whether to show or hide the bar - */ - public queueAbilityDisplay(pokemon: Pokemon, passive: boolean, show: boolean): void { - this.unshiftPhase(show ? new ShowAbilityPhase(pokemon.getBattlerIndex(), passive) : new HideAbilityPhase()); - this.clearPhaseQueueSplice(); - } - - /** - * Hides the ability bar if it is currently visible - */ - public hideAbilityBar(): void { - if (this.abilityBar.isVisible()) { - this.unshiftPhase(new HideAbilityPhase()); - } - } - - /** - * Moves everything from nextCommandPhaseQueue to phaseQueue (keeping order) - */ - populatePhaseQueue(): void { - if (this.nextCommandPhaseQueue.length) { - this.phaseQueue.push(...this.nextCommandPhaseQueue); - this.nextCommandPhaseQueue.splice(0, this.nextCommandPhaseQueue.length); - } - this.phaseQueue.push(new TurnInitPhase()); - } - addMoney(amount: number): void { this.money = Math.min(this.money + amount, Number.MAX_SAFE_INTEGER); this.updateMoneyText(); @@ -2942,7 +2647,7 @@ export default class BattleScene extends SceneBase { } } else if (!virtual) { const defaultModifierType = getDefaultModifierTypeForTier(modifier.type.tier); - this.queueMessage( + this.phaseManager.queueMessage( i18next.t("battle:itemStackFull", { fullItemName: modifier.type.name, itemName: defaultModifierType.name, @@ -3499,11 +3204,11 @@ export default class BattleScene extends SceneBase { phase = new QuietFormChangePhase(pokemon, matchingFormChange); } if (pokemon.isPlayer() && !matchingFormChange.quiet && modal) { - this.overridePhase(phase); + this.phaseManager.overridePhase(phase); } else if (delayed) { - this.pushPhase(phase); + this.phaseManager.pushPhase(phase); } else { - this.unshiftPhase(phase); + this.phaseManager.unshiftPhase(phase); } return true; } @@ -3520,9 +3225,9 @@ export default class BattleScene extends SceneBase { ): boolean { const phase: Phase = new PokemonAnimPhase(battleAnimType, pokemon, fieldAssets); if (delayed) { - this.pushPhase(phase); + this.phaseManager.pushPhase(phase); } else { - this.unshiftPhase(phase); + this.phaseManager.unshiftPhase(phase); } return true; } @@ -3630,19 +3335,19 @@ export default class BattleScene extends SceneBase { this.currentBattle.double = true; const availablePartyMembers = this.getPlayerParty().filter(p => p.isAllowedInBattle()); if (availablePartyMembers.length > 1) { - this.pushPhase(new ToggleDoublePositionPhase(true)); + this.phaseManager.pushPhase(new ToggleDoublePositionPhase(true)); if (!availablePartyMembers[1].isOnField()) { - this.pushPhase(new SummonPhase(1)); + this.phaseManager.pushPhase(new SummonPhase(1)); } } - this.shiftPhase(); + this.phaseManager.shiftPhase(); }, ); return; } - this.shiftPhase(); + this.phaseManager.shiftPhase(); } /** @@ -3754,7 +3459,7 @@ export default class BattleScene extends SceneBase { if (exp) { const partyMemberIndex = party.indexOf(expPartyMembers[pm]); - this.unshiftPhase( + this.phaseManager.unshiftPhase( expPartyMembers[pm].isOnField() ? new ExpPhase(partyMemberIndex, exp) : new ShowPartyExpBarPhase(partyMemberIndex, exp), diff --git a/src/battle.ts b/src/battle.ts index 8e63a680c06..dbfed57ae41 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -205,7 +205,7 @@ export default class Battle { const message = i18next.t("battle:moneyPickedUp", { moneyAmount: formattedMoneyAmount, }); - globalScene.queueMessage(message, undefined, true); + globalScene.phaseManager.queueMessage(message, undefined, true); globalScene.currentBattle.moneyScattered = 0; } diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 4d307b2ce6f..e680a12f9fe 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -159,7 +159,7 @@ export class PostTeraFormChangeStatChangeAbAttr extends AbAttr { statStageChangePhases.push(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages)); for (const statStageChangePhase of statStageChangePhases) { - globalScene.unshiftPhase(statStageChangePhase); + globalScene.phaseManager.unshiftPhase(statStageChangePhase); } } } @@ -410,7 +410,7 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); if (!pokemon.isFullHp() && !simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), + globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), toDmgValue(pokemon.getMaxHp() / 4), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); cancelled.value = true; // Suppresses "No Effect" message } @@ -436,7 +436,7 @@ class TypeImmunityStatStageChangeAbAttr extends TypeImmunityAbAttr { super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); cancelled.value = true; // Suppresses "No Effect" message if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); } } } @@ -648,7 +648,7 @@ export class MoveImmunityStatStageChangeAbAttr extends MoveImmunityAbAttr { override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); } } /** @@ -675,7 +675,7 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr { */ override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { if (!simulated) { - globalScene.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) })); + globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) })); } } } @@ -710,10 +710,10 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr { const ally = pokemon.getAlly(); const otherPokemon = !isNullOrUndefined(ally) ? pokemon.getOpponents().concat([ ally ]) : pokemon.getOpponents(); for (const other of otherPokemon) { - globalScene.unshiftPhase(new StatStageChangePhase((other).getBattlerIndex(), false, [ this.stat ], this.stages)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase((other).getBattlerIndex(), false, [ this.stat ], this.stages)); } } else { - globalScene.unshiftPhase(new StatStageChangePhase((this.selfTarget ? pokemon : attacker).getBattlerIndex(), this.selfTarget, [ this.stat ], this.stages)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase((this.selfTarget ? pokemon : attacker).getBattlerIndex(), this.selfTarget, [ this.stat ], this.stages)); } } } @@ -744,7 +744,7 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase((this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.stages)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase((this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.stages)); } } } @@ -790,7 +790,7 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { if (!pokemon.getTag(this.tagType) && !simulated) { pokemon.addTag(this.tagType, undefined, undefined, pokemon.id); - globalScene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name })); + globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name })); } } } @@ -915,7 +915,7 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); } } @@ -1099,7 +1099,7 @@ export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChang override applyPostStatStageChange(pokemon: Pokemon, simulated: boolean, statStagesChanged: BattleStat[], stagesChanged: number, selfTarget: boolean, args: any[]): void { if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase((pokemon).getBattlerIndex(), true, this.statsToChange, this.stages)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase((pokemon).getBattlerIndex(), true, this.statsToChange, this.stages)); } } } @@ -1765,7 +1765,7 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; } if (globalScene.tryTransferHeldItemModifier(this.stolenItem, pokemon, false)) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:postAttackStealHeldItem", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), defenderName: defender.name, @@ -1892,7 +1892,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; } if (globalScene.tryTransferHeldItemModifier(this.stolenItem, pokemon, false)) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:postDefendStealHeldItem", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), attackerName: attacker.name, @@ -1999,7 +1999,7 @@ class PostVictoryStatStageChangeAbAttr extends PostVictoryAbAttr { override applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ stat ], this.stages)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ stat ], this.stages)); } } } @@ -2047,7 +2047,7 @@ export class PostKnockOutStatStageChangeAbAttr extends PostKnockOutAbAttr { override applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): void { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ stat ], this.stages)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ stat ], this.stages)); } } } @@ -2064,7 +2064,7 @@ export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr { override applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): void { if (!simulated) { pokemon.setTempAbility(knockedOut.getAbility()); - globalScene.queueMessage(i18next.t("abilityTriggers:copyFaintedAllyAbility", { pokemonNameWithAffix: getPokemonNameWithAffix(knockedOut), abilityName: allAbilities[knockedOut.getAbility().id].name })); + globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:copyFaintedAllyAbility", { pokemonNameWithAffix: getPokemonNameWithAffix(knockedOut), abilityName: allAbilities[knockedOut.getAbility().id].name })); } } } @@ -2130,7 +2130,7 @@ export class PostIntimidateStatStageChangeAbAttr extends AbAttr { override apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: BooleanHolder, args: any[]): void { if (!simulated) { - globalScene.pushPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), false, this.stats, this.stages)); + globalScene.phaseManager.pushPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), false, this.stats, this.stages)); } cancelled.value = this.overwrites; } @@ -2240,7 +2240,7 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr { override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (!simulated) { - globalScene.queueMessage(this.messageFunc(pokemon)); + globalScene.phaseManager.queueMessage(this.messageFunc(pokemon)); } } } @@ -2257,7 +2257,7 @@ export class PostSummonUnnamedMessageAbAttr extends PostSummonAbAttr { override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (!simulated) { - globalScene.queueMessage(this.message); + globalScene.phaseManager.queueMessage(this.message); } } } @@ -2332,7 +2332,7 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { if (this.selfTarget) { // we unshift the StatStageChangePhase to put it right after the showAbility and not at the end of the // phase list (which could be after CommandPhase for example) - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages)); } else { for (const opponent of pokemon.getOpponents()) { const cancelled = new BooleanHolder(false); @@ -2345,7 +2345,7 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { } } if (!cancelled.value) { - globalScene.unshiftPhase(new StatStageChangePhase(opponent.getBattlerIndex(), false, this.stats, this.stages)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(opponent.getBattlerIndex(), false, this.stats, this.stages)); } } } @@ -2370,7 +2370,7 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const target = pokemon.getAlly(); if (!simulated && !isNullOrUndefined(target)) { - globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), + globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), toDmgValue(pokemon.getMaxHp() / this.healRatio), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim)); } } @@ -2400,7 +2400,7 @@ export class PostSummonClearAllyStatStagesAbAttr extends PostSummonAbAttr { target.setStatStage(s, 0); } - globalScene.queueMessage(i18next.t("abilityTriggers:postSummonClearAllyStats", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:postSummonClearAllyStats", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); } } } @@ -2448,7 +2448,7 @@ export class DownloadAbAttr extends PostSummonAbAttr { } if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), false, this.stats, 1)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), false, this.stats, 1)); } } } @@ -2635,7 +2635,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt if (!simulated) { for (const pokemon of allowedParty) { if (pokemon.status && this.statusEffect.includes(pokemon.status.effect)) { - globalScene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); + globalScene.phaseManager.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); pokemon.resetStatus(false); pokemon.updateInfo(); } @@ -2725,7 +2725,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { const target = this.getTarget(pokemon.getOpponents()); - globalScene.unshiftPhase(new PokemonTransformPhase(pokemon.getBattlerIndex(), target.getBattlerIndex(), true)); + globalScene.phaseManager.unshiftPhase(new PokemonTransformPhase(pokemon.getBattlerIndex(), target.getBattlerIndex(), true)); } } @@ -2820,7 +2820,7 @@ export class CommanderAbAttr extends AbAttr { // Apply boosts from this effect to the ally Dondozo pokemon.getAlly()?.addTag(BattlerTagType.COMMANDED, 0, MoveId.NONE, pokemon.id); // Cancel the source Pokemon's next move (if a move is queued) - globalScene.tryRemovePhase((phase) => phase.is("MovePhase") && phase.pokemon === pokemon); + globalScene.phaseManager.tryRemovePhase((phase) => phase.is("MovePhase") && phase.pokemon === pokemon); } } } @@ -3076,7 +3076,7 @@ export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { const stages = args[1]; this.reflectedStat = stat; if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase(attacker.getBattlerIndex(), false, [ stat ], stages, true, false, true, null, true)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(attacker.getBattlerIndex(), false, [ stat ], stages, true, false, true, null, true)); } cancelled.value = true; } @@ -3768,7 +3768,7 @@ export class ForewarnAbAttr extends PostSummonAbAttr { } } if (!simulated) { - globalScene.queueMessage(i18next.t("abilityTriggers:forewarn", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: maxMove })); + globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:forewarn", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: maxMove })); } } } @@ -3781,7 +3781,7 @@ export class FriskAbAttr extends PostSummonAbAttr { override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (!simulated) { for (const opponent of pokemon.getOpponents()) { - globalScene.queueMessage(i18next.t("abilityTriggers:frisk", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), opponentName: opponent.name, opponentAbilityName: opponent.getAbility().name })); + globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:frisk", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), opponentName: opponent.name, opponentAbilityName: opponent.getAbility().name })); setAbilityRevealed(opponent); } } @@ -3913,7 +3913,7 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr { override applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, args: any[]): void { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; if (!simulated) { - globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), + globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), toDmgValue(pokemon.getMaxHp() / (16 / this.healFactor)), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); } } @@ -3935,7 +3935,7 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr { override applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, args: any[]): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.queueMessage(i18next.t("abilityTriggers:postWeatherLapseDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName })); + globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:postWeatherLapseDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName })); pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / (16 / this.damageFactor)), { result: HitResult.INDIRECT }); } } @@ -4015,7 +4015,7 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), + globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), toDmgValue(pokemon.getMaxHp() / 8), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true)); } } @@ -4047,7 +4047,7 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (!simulated && this.target?.status) { - globalScene.queueMessage(getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target))); + globalScene.phaseManager.queueMessage(getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target))); this.target.resetStatus(false); this.target.updateInfo(); } @@ -4132,7 +4132,7 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr { } globalScene.updateModifiers(pokemon.isPlayer()); - globalScene.queueMessage(i18next.t("abilityTriggers:postTurnLootCreateEatenBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: chosenBerry.name })); + globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:postTurnLootCreateEatenBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: chosenBerry.name })); return true; } } @@ -4162,7 +4162,7 @@ export class RepeatBerryNextTurnAbAttr extends PostTurnAbAttr { * @param _args - N/A */ override apply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: BooleanHolder | null, _args: any[]): void { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM), ); @@ -4226,11 +4226,11 @@ export class MoodyAbAttr extends PostTurnAbAttr { if (canRaise.length > 0) { const raisedStat = canRaise[pokemon.randBattleSeedInt(canRaise.length)]; canLower = canRaise.filter(s => s !== raisedStat); - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ raisedStat ], 2)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ raisedStat ], 2)); } if (canLower.length > 0) { const loweredStat = canLower[pokemon.randBattleSeedInt(canLower.length)]; - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ loweredStat ], -1)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ loweredStat ], -1)); } } } @@ -4247,7 +4247,7 @@ export class SpeedBoostAbAttr extends PostTurnAbAttr { } override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.SPD ], 1)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.SPD ], 1)); } } @@ -4259,7 +4259,7 @@ export class PostTurnHealAbAttr extends PostTurnAbAttr { override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), + globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), toDmgValue(pokemon.getMaxHp() / 16), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); } } @@ -4305,7 +4305,7 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { if ((opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(AbilityId.COMATOSE)) && !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !opp.switchOutStatus) { if (!simulated) { opp.damageAndUpdate(toDmgValue(opp.getMaxHp() / 8), { result: HitResult.INDIRECT }); - globalScene.queueMessage(i18next.t("abilityTriggers:badDreams", { pokemonName: getPokemonNameWithAffix(opp) })); + globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:badDreams", { pokemonName: getPokemonNameWithAffix(opp) })); } } } @@ -4336,7 +4336,7 @@ export class FetchBallAbAttr extends PostTurnAbAttr { const lastUsed = globalScene.currentBattle.lastUsedPokeball; globalScene.pokeballCounts[lastUsed!]++; globalScene.currentBattle.lastUsedPokeball = null; - globalScene.queueMessage(i18next.t("abilityTriggers:fetchBall", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokeballName: getPokeballName(lastUsed!) })); + globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:fetchBall", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokeballName: getPokeballName(lastUsed!) })); } } @@ -4442,10 +4442,10 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) { const target = this.getTarget(dancer, source, targets); - globalScene.unshiftPhase(new MovePhase(dancer, target, move, true, true)); + globalScene.phaseManager.unshiftPhase(new MovePhase(dancer, target, move, true, true)); } else if (move.getMove() instanceof SelfStatusMove) { // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself - globalScene.unshiftPhase(new MovePhase(dancer, [ dancer.getBattlerIndex() ], move, true, true)); + globalScene.phaseManager.unshiftPhase(new MovePhase(dancer, [ dancer.getBattlerIndex() ], move, true, true)); } } } @@ -4525,7 +4525,7 @@ export class StatStageChangeCopyAbAttr extends AbAttr { args: any[], ): void { if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, (args[0] as BattleStat[]), (args[1] as number), true, false, false)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, (args[0] as BattleStat[]), (args[1] as number), true, false, false)); } } } @@ -4606,7 +4606,7 @@ export class HealFromBerryUseAbAttr extends AbAttr { } const { name: abilityName } = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new PokemonHealPhase( pokemon.getBattlerIndex(), toDmgValue(pokemon.getMaxHp() * this.healPercent), @@ -4739,7 +4739,7 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr { if (globalScene.tryTransferHeldItemModifier(this.randItem, pokemon, true, 1, true, undefined, false)) { postBattleLoot.splice(postBattleLoot.indexOf(this.randItem), 1); - globalScene.queueMessage(i18next.t("abilityTriggers:postBattleLoot", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), itemName: this.randItem.type.name })); + globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:postBattleLoot", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), itemName: this.randItem.type.name })); } this.randItem = undefined; } @@ -4927,7 +4927,7 @@ export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr { override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { if (!simulated) { - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages)); } } } @@ -5495,16 +5495,16 @@ function applySingleAbAttrs( continue; } - globalScene.setPhaseQueueSplice(); + globalScene.phaseManager.setPhaseQueueSplice(); if (attr.showAbility && !simulated) { - globalScene.queueAbilityDisplay(pokemon, passive, true); + globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, true); abShown = true; } const message = attr.getTriggerMessage(pokemon, ability.name, args); if (message) { if (!simulated) { - globalScene.queueMessage(message); + globalScene.phaseManager.queueMessage(message); } messages.push(message); } @@ -5512,14 +5512,14 @@ function applySingleAbAttrs( applyFunc(attr, passive); if (abShown) { - globalScene.queueAbilityDisplay(pokemon, passive, false); + globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, false); } if (!simulated) { pokemon.waveData.abilitiesApplied.add(ability.id); } - globalScene.clearPhaseQueueSplice(); + globalScene.phaseManager.clearPhaseQueueSplice(); } } @@ -5546,7 +5546,7 @@ class ForceSwitchOutHelper { if (switchOutTarget.hp > 0) { switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); - globalScene.prependToPhase(new SwitchPhase(this.switchType, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase); + globalScene.phaseManager.prependToPhase(new SwitchPhase(this.switchType, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase); return true; } /** @@ -5560,7 +5560,7 @@ class ForceSwitchOutHelper { if (switchOutTarget.hp > 0) { switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); const summonIndex = (globalScene.currentBattle.trainer ? globalScene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0); - globalScene.prependToPhase(new SwitchSummonPhase(this.switchType, switchOutTarget.getFieldIndex(), summonIndex, false, false), MoveEndPhase); + globalScene.phaseManager.prependToPhase(new SwitchSummonPhase(this.switchType, switchOutTarget.getFieldIndex(), summonIndex, false, false), MoveEndPhase); return true; } /** @@ -5576,7 +5576,7 @@ class ForceSwitchOutHelper { if (switchOutTarget.hp > 0) { switchOutTarget.leaveField(false); - globalScene.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500); if (globalScene.currentBattle.double && !isNullOrUndefined(allyPokemon)) { globalScene.redirectPokemonMoves(switchOutTarget, allyPokemon); } @@ -5586,13 +5586,13 @@ class ForceSwitchOutHelper { globalScene.clearEnemyHeldItemModifiers(); if (switchOutTarget.hp) { - globalScene.pushPhase(new BattleEndPhase(false)); + globalScene.phaseManager.pushPhase(new BattleEndPhase(false)); if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { - globalScene.pushPhase(new SelectBiomePhase()); + globalScene.phaseManager.pushPhase(new SelectBiomePhase()); } - globalScene.pushPhase(new NewBattlePhase()); + globalScene.phaseManager.pushPhase(new NewBattlePhase()); } } } @@ -5792,7 +5792,7 @@ function applyAbAttrsInternal( for (const passive of [ false, true ]) { if (pokemon) { applySingleAbAttrs(pokemon, passive, attrType, applyFunc, successFunc, args, gainedMidTurn, simulated, messages); - globalScene.clearPhaseQueueSplice(); + globalScene.phaseManager.clearPhaseQueueSplice(); } } } @@ -6870,7 +6870,7 @@ export function initAbilities() { .ignorable(), new Ability(AbilityId.ANALYTIC, 5) .attr(MovePowerBoostAbAttr, (user, target, move) => { - const movePhase = globalScene.findPhase((phase) => phase.is("MovePhase") && phase.pokemon.id !== user?.id); + const movePhase = globalScene.phaseManager.findPhase((phase) => phase.is("MovePhase") && phase.pokemon.id !== user?.id); return isNullOrUndefined(movePhase); }, 1.3), new Ability(AbilityId.ILLUSION, 5) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 0254aab37e1..70fceb17c49 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -54,7 +54,7 @@ export abstract class ArenaTag { onRemove(_arena: Arena, quiet = false): void { if (!quiet) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t( `arenaTag:arenaOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, { moveName: this.getMoveName() }, @@ -126,7 +126,7 @@ export class MistTag extends ArenaTag { const source = globalScene.getPokemonById(this.sourceId); if (!quiet && source) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:mistOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source), }), @@ -161,7 +161,7 @@ export class MistTag extends ArenaTag { cancelled.value = true; if (!simulated) { - globalScene.queueMessage(i18next.t("arenaTag:mistApply")); + globalScene.phaseManager.queueMessage(i18next.t("arenaTag:mistApply")); } return true; @@ -239,7 +239,7 @@ class ReflectTag extends WeakenMoveScreenTag { onAdd(_arena: Arena, quiet = false): void { if (!quiet) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t( `arenaTag:reflectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, ), @@ -259,7 +259,7 @@ class LightScreenTag extends WeakenMoveScreenTag { onAdd(_arena: Arena, quiet = false): void { if (!quiet) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t( `arenaTag:lightScreenOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, ), @@ -282,7 +282,7 @@ class AuroraVeilTag extends WeakenMoveScreenTag { onAdd(_arena: Arena, quiet = false): void { if (!quiet) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t( `arenaTag:auroraVeilOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, ), @@ -318,7 +318,7 @@ export class ConditionalProtectTag extends ArenaTag { } onAdd(_arena: Arena): void { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t( `arenaTag:conditionalProtectOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, { moveName: super.getMoveName() }, @@ -355,7 +355,7 @@ export class ConditionalProtectTag extends ArenaTag { isProtected.value = true; if (!simulated) { new CommonBattleAnim(CommonAnim.PROTECT, defender).play(); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:conditionalProtectApply", { moveName: super.getMoveName(), pokemonNameWithAffix: getPokemonNameWithAffix(defender), @@ -381,7 +381,7 @@ export class ConditionalProtectTag extends ArenaTag { */ const QuickGuardConditionFunc: ProtectConditionFunc = (_arena, moveId) => { const move = allMoves[moveId]; - const effectPhase = globalScene.getCurrentPhase(); + const effectPhase = globalScene.phaseManager.getCurrentPhase(); if (effectPhase?.is("MoveEffectPhase")) { const attacker = effectPhase.getUserPokemon(); @@ -458,7 +458,7 @@ class MatBlockTag extends ConditionalProtectTag { if (this.sourceId) { const source = globalScene.getPokemonById(this.sourceId); if (source) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:matBlockOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source), }), @@ -517,7 +517,7 @@ export class NoCritTag extends ArenaTag { /** Queues a message upon adding this effect to the field */ onAdd(_arena: Arena): void { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t(`arenaTag:noCritOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : "Enemy"}`, { moveName: this.getMoveName(), }), @@ -527,7 +527,7 @@ export class NoCritTag extends ArenaTag { /** Queues a message upon removing this effect from the field */ onRemove(_arena: Arena): void { const source = globalScene.getPokemonById(this.sourceId!); // TODO: is this bang correct? - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:noCritOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined), moveName: this.getMoveName(), @@ -567,8 +567,10 @@ class WishTag extends ArenaTag { onRemove(_arena: Arena): void { const target = globalScene.getField()[this.battlerIndex]; if (target?.isActive(true)) { - globalScene.queueMessage(this.triggerMessage); - globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), this.healHp, null, true, false)); + globalScene.phaseManager.queueMessage(this.triggerMessage); + globalScene.phaseManager.unshiftPhase( + new PokemonHealPhase(target.getBattlerIndex(), this.healHp, null, true, false), + ); } } } @@ -621,11 +623,11 @@ class MudSportTag extends WeakenMoveTypeTag { } onAdd(_arena: Arena): void { - globalScene.queueMessage(i18next.t("arenaTag:mudSportOnAdd")); + globalScene.phaseManager.queueMessage(i18next.t("arenaTag:mudSportOnAdd")); } onRemove(_arena: Arena): void { - globalScene.queueMessage(i18next.t("arenaTag:mudSportOnRemove")); + globalScene.phaseManager.queueMessage(i18next.t("arenaTag:mudSportOnRemove")); } } @@ -639,11 +641,11 @@ class WaterSportTag extends WeakenMoveTypeTag { } onAdd(_arena: Arena): void { - globalScene.queueMessage(i18next.t("arenaTag:waterSportOnAdd")); + globalScene.phaseManager.queueMessage(i18next.t("arenaTag:waterSportOnAdd")); } onRemove(_arena: Arena): void { - globalScene.queueMessage(i18next.t("arenaTag:waterSportOnRemove")); + globalScene.phaseManager.queueMessage(i18next.t("arenaTag:waterSportOnRemove")); } } @@ -659,7 +661,7 @@ export class IonDelugeTag extends ArenaTag { /** Queues an on-add message */ onAdd(_arena: Arena): void { - globalScene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd")); + globalScene.phaseManager.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd")); } onRemove(_arena: Arena): void {} // Removes default on-remove message @@ -758,7 +760,7 @@ class SpikesTag extends ArenaTrapTag { const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; if (!quiet && source) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:spikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor(), @@ -781,7 +783,7 @@ class SpikesTag extends ArenaTrapTag { const damageHpRatio = 1 / (10 - 2 * this.layers); const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:spikesActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -811,7 +813,7 @@ class ToxicSpikesTag extends ArenaTrapTag { const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; if (!quiet && source) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:toxicSpikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor(), @@ -834,7 +836,7 @@ class ToxicSpikesTag extends ArenaTrapTag { if (pokemon.isOfType(PokemonType.POISON)) { this.neutralized = true; if (globalScene.arena.removeTag(this.tagType)) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:toxicSpikesActivateTrapPoison", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName(), @@ -891,7 +893,7 @@ export class DelayedAttackTag extends ArenaTag { const ret = super.lapse(arena); if (!ret) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new MoveEffectPhase(this.sourceId!, [this.targetIndex], allMoves[this.sourceMove!], false, true), ); // TODO: are those bangs correct? } @@ -917,7 +919,7 @@ class StealthRockTag extends ArenaTrapTag { const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; if (!quiet && source) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:stealthRockOnAdd", { opponentDesc: source.getOpponentDescriptor(), }), @@ -971,7 +973,7 @@ class StealthRockTag extends ArenaTrapTag { } const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:stealthRockActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -1001,7 +1003,7 @@ class StickyWebTag extends ArenaTrapTag { super.onAdd(arena); const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; if (!quiet && source) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:stickyWebOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor(), @@ -1020,13 +1022,13 @@ class StickyWebTag extends ArenaTrapTag { } if (!cancelled.value) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:stickyWebActivateTrap", { pokemonName: pokemon.getNameToRender(), }), ); const stages = new NumberHolder(-1); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase( pokemon.getBattlerIndex(), false, @@ -1074,7 +1076,7 @@ export class TrickRoomTag extends ArenaTag { onAdd(_arena: Arena): void { const source = this.sourceId ? globalScene.getPokemonById(this.sourceId) : null; if (source) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:trickRoomOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source), }), @@ -1083,7 +1085,7 @@ export class TrickRoomTag extends ArenaTag { } onRemove(_arena: Arena): void { - globalScene.queueMessage(i18next.t("arenaTag:trickRoomOnRemove")); + globalScene.phaseManager.queueMessage(i18next.t("arenaTag:trickRoomOnRemove")); } } @@ -1098,7 +1100,7 @@ export class GravityTag extends ArenaTag { } onAdd(_arena: Arena): void { - globalScene.queueMessage(i18next.t("arenaTag:gravityOnAdd")); + globalScene.phaseManager.queueMessage(i18next.t("arenaTag:gravityOnAdd")); globalScene.getField(true).forEach(pokemon => { if (pokemon !== null) { pokemon.removeTag(BattlerTagType.FLOATING); @@ -1111,7 +1113,7 @@ export class GravityTag extends ArenaTag { } onRemove(_arena: Arena): void { - globalScene.queueMessage(i18next.t("arenaTag:gravityOnRemove")); + globalScene.phaseManager.queueMessage(i18next.t("arenaTag:gravityOnRemove")); } } @@ -1127,7 +1129,7 @@ class TailwindTag extends ArenaTag { onAdd(_arena: Arena, quiet = false): void { if (!quiet) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t( `arenaTag:tailwindOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, ), @@ -1141,7 +1143,7 @@ class TailwindTag extends ArenaTag { // Apply the CHARGED tag to party members with the WIND_POWER ability if (pokemon.hasAbility(AbilityId.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) { pokemon.addTag(BattlerTagType.CHARGED); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName(), @@ -1151,16 +1153,18 @@ class TailwindTag extends ArenaTag { // Raise attack by one stage if party member has WIND_RIDER ability // TODO: Ability displays should be handled by the ability if (pokemon.hasAbility(AbilityId.WIND_RIDER)) { - globalScene.queueAbilityDisplay(pokemon, false, true); - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true)); - globalScene.queueAbilityDisplay(pokemon, false, false); + globalScene.phaseManager.queueAbilityDisplay(pokemon, false, true); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true), + ); + globalScene.phaseManager.queueAbilityDisplay(pokemon, false, false); } } } onRemove(_arena: Arena, quiet = false): void { if (!quiet) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t( `arenaTag:tailwindOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, ), @@ -1179,11 +1183,11 @@ class HappyHourTag extends ArenaTag { } onAdd(_arena: Arena): void { - globalScene.queueMessage(i18next.t("arenaTag:happyHourOnAdd")); + globalScene.phaseManager.queueMessage(i18next.t("arenaTag:happyHourOnAdd")); } onRemove(_arena: Arena): void { - globalScene.queueMessage(i18next.t("arenaTag:happyHourOnRemove")); + globalScene.phaseManager.queueMessage(i18next.t("arenaTag:happyHourOnRemove")); } } @@ -1193,7 +1197,7 @@ class SafeguardTag extends ArenaTag { } onAdd(_arena: Arena): void { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t( `arenaTag:safeguardOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, ), @@ -1201,7 +1205,7 @@ class SafeguardTag extends ArenaTag { } onRemove(_arena: Arena): void { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t( `arenaTag:safeguardOnRemove${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, ), @@ -1237,7 +1241,7 @@ class ImprisonTag extends ArenaTrapTag { p.addTag(BattlerTagType.IMPRISON, 1, MoveId.IMPRISON, this.sourceId); } }); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:imprisonOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source), }), @@ -1294,7 +1298,7 @@ class FireGrassPledgeTag extends ArenaTag { override onAdd(_arena: Arena): void { // "A sea of fire enveloped your/the opposing team!" - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t( `arenaTag:fireGrassPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, ), @@ -1309,13 +1313,13 @@ class FireGrassPledgeTag extends ArenaTag { .filter(pokemon => !pokemon.isOfType(PokemonType.FIRE) && !pokemon.switchOutStatus) .forEach(pokemon => { // "{pokemonNameWithAffix} was hurt by the sea of fire!" - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:fireGrassPledgeLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); // TODO: Replace this with a proper animation - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM), ); pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT }); @@ -1339,7 +1343,7 @@ class WaterFirePledgeTag extends ArenaTag { override onAdd(_arena: Arena): void { // "A rainbow appeared in the sky on your/the opposing team's side!" - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t( `arenaTag:waterFirePledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, ), @@ -1373,7 +1377,7 @@ class GrassWaterPledgeTag extends ArenaTag { override onAdd(_arena: Arena): void { // "A swamp enveloped your/the opposing team!" - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t( `arenaTag:grassWaterPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`, ), @@ -1394,7 +1398,7 @@ export class FairyLockTag extends ArenaTag { } onAdd(_arena: Arena): void { - globalScene.queueMessage(i18next.t("arenaTag:fairyLockOnAdd")); + globalScene.phaseManager.queueMessage(i18next.t("arenaTag:fairyLockOnAdd")); } } @@ -1451,7 +1455,7 @@ export class SuppressAbilitiesTag extends ArenaTag { public override onRemove(_arena: Arena, quiet = false) { this.beingRemoved = true; if (!quiet) { - globalScene.queueMessage(i18next.t("arenaTag:neutralizingGasOnRemove")); + globalScene.phaseManager.queueMessage(i18next.t("arenaTag:neutralizingGasOnRemove")); } for (const pokemon of globalScene.getField(true)) { @@ -1472,7 +1476,7 @@ export class SuppressAbilitiesTag extends ArenaTag { private playActivationMessage(pokemon: Pokemon | null) { if (pokemon) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("arenaTag:neutralizingGasOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 456f519a34c..d0b4620d8eb 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -164,12 +164,12 @@ export abstract class MoveRestrictionBattlerTag extends BattlerTag { override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { if (lapseType === BattlerTagLapseType.PRE_MOVE) { // Cancel the affected pokemon's selected move - const phase = globalScene.getCurrentPhase() as MovePhase; + const phase = globalScene.phaseManager.getCurrentPhase() as MovePhase; const move = phase.move; if (this.isMoveRestricted(move.moveId, pokemon)) { if (this.interruptedText(pokemon, move.moveId)) { - globalScene.queueMessage(this.interruptedText(pokemon, move.moveId)); + globalScene.phaseManager.queueMessage(this.interruptedText(pokemon, move.moveId)); } phase.cancel(); } @@ -315,7 +315,7 @@ export class DisabledTag extends MoveRestrictionBattlerTag { this.moveId = move.move; - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:disabledOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[this.moveId].name, @@ -327,7 +327,7 @@ export class DisabledTag extends MoveRestrictionBattlerTag { override onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:disabledLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[this.moveId].name, @@ -456,12 +456,12 @@ export class RechargingTag extends BattlerTag { /** Cancels the source's move this turn and queues a "__ must recharge!" message */ lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { if (lapseType === BattlerTagLapseType.PRE_MOVE) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:rechargingLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - (globalScene.getCurrentPhase() as MovePhase).cancel(); + (globalScene.phaseManager.getCurrentPhase() as MovePhase).cancel(); pokemon.getMoveQueue().shift(); } return super.lapse(pokemon, lapseType); @@ -488,7 +488,7 @@ export class BeakBlastChargingTag extends BattlerTag { new MoveChargeAnim(ChargeAnim.BEAK_BLAST_CHARGING, this.sourceMove, pokemon).play(); // Queue Beak Blast's header message - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("moveTriggers:startedHeatingUpBeak", { pokemonName: getPokemonNameWithAffix(pokemon), }), @@ -533,7 +533,7 @@ export class ShellTrapTag extends BattlerTag { } onAdd(pokemon: Pokemon): void { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("moveTriggers:setUpShellTrap", { pokemonName: getPokemonNameWithAffix(pokemon), }), @@ -552,15 +552,15 @@ export class ShellTrapTag extends BattlerTag { // Trap should only be triggered by opponent's Physical moves if (phaseData?.move.category === MoveCategory.PHYSICAL && pokemon.isOpponent(phaseData.attacker)) { - const shellTrapPhaseIndex = globalScene.phaseQueue.findIndex( + const shellTrapPhaseIndex = globalScene.phaseManager.phaseQueue.findIndex( phase => phase.is("MovePhase") && phase.pokemon === pokemon, ); - const firstMovePhaseIndex = globalScene.phaseQueue.findIndex(phase => phase.is("MovePhase")); + const firstMovePhaseIndex = globalScene.phaseManager.phaseQueue.findIndex(phase => phase.is("MovePhase")); // Only shift MovePhase timing if it's not already next up if (shellTrapPhaseIndex !== -1 && shellTrapPhaseIndex !== firstMovePhaseIndex) { - const shellTrapMovePhase = globalScene.phaseQueue.splice(shellTrapPhaseIndex, 1)[0]; - globalScene.prependToPhase(shellTrapMovePhase, MovePhase); + const shellTrapMovePhase = globalScene.phaseManager.phaseQueue.splice(shellTrapPhaseIndex, 1)[0]; + globalScene.phaseManager.prependToPhase(shellTrapMovePhase, MovePhase); } this.activated = true; @@ -598,13 +598,13 @@ export class TrappedTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.queueMessage(this.getTrapMessage(pokemon)); + globalScene.phaseManager.queueMessage(this.getTrapMessage(pokemon)); } onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:trappedOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName(), @@ -660,8 +660,8 @@ export class FlinchedTag extends BattlerTag { */ lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { if (lapseType === BattlerTagLapseType.PRE_MOVE) { - (globalScene.getCurrentPhase() as MovePhase).cancel(); - globalScene.queueMessage( + (globalScene.phaseManager.getCurrentPhase() as MovePhase).cancel(); + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:flinchedLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -699,7 +699,7 @@ export class InterruptedTag extends BattlerTag { } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { - (globalScene.getCurrentPhase() as MovePhase).cancel(); + (globalScene.phaseManager.getCurrentPhase() as MovePhase).cancel(); return super.lapse(pokemon, lapseType); } } @@ -719,8 +719,10 @@ export class ConfusedTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.unshiftPhase(new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION)); - globalScene.queueMessage( + globalScene.phaseManager.unshiftPhase( + new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION), + ); + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:confusedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -730,7 +732,7 @@ export class ConfusedTag extends BattlerTag { onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:confusedOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -740,7 +742,7 @@ export class ConfusedTag extends BattlerTag { onOverlap(pokemon: Pokemon): void { super.onOverlap(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:confusedOnOverlap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -754,12 +756,14 @@ export class ConfusedTag extends BattlerTag { return false; } - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:confusedLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - globalScene.unshiftPhase(new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION)); + globalScene.phaseManager.unshiftPhase( + new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION), + ); // 1/3 chance of hitting self with a 40 base power move if (pokemon.randBattleSeedInt(3) === 0 || Overrides.CONFUSION_ACTIVATION_OVERRIDE === true) { @@ -769,9 +773,9 @@ export class ConfusedTag extends BattlerTag { ((((2 * pokemon.level) / 5 + 2) * 40 * atk) / def / 50 + 2) * (pokemon.randBattleSeedIntRange(85, 100) / 100), ); // Intentionally don't increment rage fist's hitCount - globalScene.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself")); + globalScene.phaseManager.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself")); pokemon.damageAndUpdate(damage, { result: HitResult.CONFUSION }); - (globalScene.getCurrentPhase() as MovePhase).cancel(); + (globalScene.phaseManager.getCurrentPhase() as MovePhase).cancel(); } return true; @@ -815,7 +819,7 @@ export class DestinyBondTag extends BattlerTag { } if (pokemon.isBossImmune()) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:destinyBondLapseIsBoss", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -823,7 +827,7 @@ export class DestinyBondTag extends BattlerTag { return false; } - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:destinyBondLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(source), pokemonNameWithAffix2: getPokemonNameWithAffix(pokemon), @@ -856,7 +860,7 @@ export class InfatuatedTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:infatuatedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), sourcePokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? @@ -867,7 +871,7 @@ export class InfatuatedTag extends BattlerTag { onOverlap(pokemon: Pokemon): void { super.onOverlap(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:infatuatedOnOverlap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -878,21 +882,23 @@ export class InfatuatedTag extends BattlerTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:infatuatedLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), sourcePokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? }), ); - globalScene.unshiftPhase(new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT)); + globalScene.phaseManager.unshiftPhase( + new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT), + ); if (pokemon.randBattleSeedInt(2)) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:infatuatedLapseImmobilize", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - (globalScene.getCurrentPhase() as MovePhase).cancel(); + (globalScene.phaseManager.getCurrentPhase() as MovePhase).cancel(); } } @@ -902,7 +908,7 @@ export class InfatuatedTag extends BattlerTag { onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:infatuatedOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -941,7 +947,7 @@ export class SeedTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:seededOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -959,13 +965,13 @@ export class SeedTag extends BattlerTag { applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); if (!cancelled.value) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new CommonAnimPhase(source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED), ); const damage = pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT }); const reverseDrain = pokemon.hasAbilityWithAttr(ReverseDrainAbAttr, false); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new PokemonHealPhase( source.getBattlerIndex(), !reverseDrain ? damage : damage * -1, @@ -1006,7 +1012,7 @@ export class PowderTag extends BattlerTag { super.onAdd(pokemon); // "{Pokemon} is covered in powder!" - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:powderOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -1022,7 +1028,7 @@ export class PowderTag extends BattlerTag { */ lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { if (lapseType === BattlerTagLapseType.PRE_MOVE) { - const movePhase = globalScene.getCurrentPhase(); + const movePhase = globalScene.phaseManager.getCurrentPhase(); if (movePhase?.is("MovePhase")) { const move = movePhase.move.getMove(); const weather = globalScene.arena.weather; @@ -1033,7 +1039,7 @@ export class PowderTag extends BattlerTag { movePhase.fail(); movePhase.showMoveText(); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.POWDER), ); @@ -1044,7 +1050,7 @@ export class PowderTag extends BattlerTag { } // "When the flame touched the powder\non the Pokémon, it exploded!" - globalScene.queueMessage(i18next.t("battlerTags:powderLapse", { moveName: move.name })); + globalScene.phaseManager.queueMessage(i18next.t("battlerTags:powderLapse", { moveName: move.name })); } } return true; @@ -1061,7 +1067,7 @@ export class NightmareTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:nightmareOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -1071,7 +1077,7 @@ export class NightmareTag extends BattlerTag { onOverlap(pokemon: Pokemon): void { super.onOverlap(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:nightmareOnOverlap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -1082,12 +1088,14 @@ export class NightmareTag extends BattlerTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:nightmareLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - globalScene.unshiftPhase(new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE)); // TODO: Update animation type + globalScene.phaseManager.unshiftPhase( + new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE), + ); // TODO: Update animation type const cancelled = new BooleanHolder(false); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); @@ -1173,18 +1181,18 @@ export class EncoreTag extends MoveRestrictionBattlerTag { onAdd(pokemon: Pokemon): void { super.onRemove(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:encoreOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - const movePhase = globalScene.findPhase(m => m.is("MovePhase") && m.pokemon === pokemon); + const movePhase = globalScene.phaseManager.findPhase(m => m.is("MovePhase") && m.pokemon === pokemon); if (movePhase) { const movesetMove = pokemon.getMoveset().find(m => m.moveId === this.moveId); if (movesetMove) { const lastMove = pokemon.getLastXMoves(1)[0]; - globalScene.tryReplacePhase( + globalScene.phaseManager.tryReplacePhase( m => m.is("MovePhase") && m.pokemon === pokemon, new MovePhase(pokemon, lastMove.targets ?? [], movesetMove), ); @@ -1221,7 +1229,7 @@ export class EncoreTag extends MoveRestrictionBattlerTag { onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:encoreOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -1235,7 +1243,7 @@ export class HelpingHandTag extends BattlerTag { } onAdd(pokemon: Pokemon): void { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:helpingHandOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? pokemonName: getPokemonNameWithAffix(pokemon), @@ -1268,7 +1276,7 @@ export class IngrainTag extends TrappedTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new PokemonHealPhase( pokemon.getBattlerIndex(), toDmgValue(pokemon.getMaxHp() / 16), @@ -1307,7 +1315,9 @@ export class OctolockTag extends TrappedTag { const shouldLapse = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (shouldLapse) { - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), false, [Stat.DEF, Stat.SPDEF], -1)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), false, [Stat.DEF, Stat.SPDEF], -1), + ); return true; } @@ -1323,7 +1333,7 @@ export class AquaRingTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:aquaRingOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -1334,7 +1344,7 @@ export class AquaRingTag extends BattlerTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new PokemonHealPhase( pokemon.getBattlerIndex(), toDmgValue(pokemon.getMaxHp() / 16), @@ -1382,7 +1392,7 @@ export class DrowsyTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:drowsyOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -1435,13 +1445,13 @@ export abstract class DamagingTrapTag extends TrappedTag { const ret = super.lapse(pokemon, lapseType); if (ret) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:damagingTrapLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName(), }), ); - globalScene.unshiftPhase(new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, this.commonAnim)); + globalScene.phaseManager.unshiftPhase(new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, this.commonAnim)); const cancelled = new BooleanHolder(false); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); @@ -1596,7 +1606,7 @@ export class ProtectedTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:protectedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -1606,14 +1616,14 @@ export class ProtectedTag extends BattlerTag { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { if (lapseType === BattlerTagLapseType.CUSTOM) { new CommonBattleAnim(CommonAnim.PROTECT, pokemon).play(); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:protectedLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); // Stop multi-hit moves early - const effectPhase = globalScene.getCurrentPhase(); + const effectPhase = globalScene.phaseManager.getCurrentPhase(); if (effectPhase?.is("MoveEffectPhase")) { effectPhase.stopMultiHit(pokemon); } @@ -1754,7 +1764,9 @@ export class ContactStatStageChangeProtectedTag extends DamageProtectedTag { * @param user - The pokemon that is being attacked and has the tag */ override onContact(attacker: Pokemon, _user: Pokemon): void { - globalScene.unshiftPhase(new StatStageChangePhase(attacker.getBattlerIndex(), false, [this.stat], this.levels)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(attacker.getBattlerIndex(), false, [this.stat], this.levels), + ); } } @@ -1771,7 +1783,7 @@ export class EnduringTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:enduringOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -1780,7 +1792,7 @@ export class EnduringTag extends BattlerTag { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { if (lapseType === BattlerTagLapseType.CUSTOM) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:enduringLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -1799,7 +1811,7 @@ export class SturdyTag extends BattlerTag { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { if (lapseType === BattlerTagLapseType.CUSTOM) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:sturdyLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -1824,7 +1836,7 @@ export class PerishSongTag extends BattlerTag { const ret = super.lapse(pokemon, lapseType); if (ret) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:perishSongLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), turnCount: this.turnCount, @@ -1861,7 +1873,7 @@ export class CenterOfAttentionTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:centerOfAttentionOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -1918,15 +1930,15 @@ export class TruantTag extends AbilityBattlerTag { const lastMove = pokemon.getLastXMoves().find(() => true); if (lastMove && lastMove.move !== MoveId.NONE) { - (globalScene.getCurrentPhase() as MovePhase).cancel(); + (globalScene.phaseManager.getCurrentPhase() as MovePhase).cancel(); // TODO: Ability displays should be handled by the ability - globalScene.queueAbilityDisplay(pokemon, passive, true); - globalScene.queueMessage( + globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, true); + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:truantLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - globalScene.queueAbilityDisplay(pokemon, passive, false); + globalScene.phaseManager.queueAbilityDisplay(pokemon, passive, false); } return true; @@ -1941,7 +1953,7 @@ export class SlowStartTag extends AbilityBattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:slowStartOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -1959,7 +1971,7 @@ export class SlowStartTag extends AbilityBattlerTag { onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:slowStartOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -2006,7 +2018,7 @@ export class HighestStatBoostTag extends AbilityBattlerTag { this.stat = highestStat; this.multiplier = this.stat === Stat.SPD ? 1.5 : 1.3; - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:highestStatBoostOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), statName: i18next.t(getStatKey(highestStat)), @@ -2021,7 +2033,7 @@ export class HighestStatBoostTag extends AbilityBattlerTag { onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:highestStatBoostOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: allAbilities[this.ability].name, @@ -2119,7 +2131,7 @@ export class FloatingTag extends TypeImmuneTag { super.onAdd(pokemon); if (this.sourceMove === MoveId.MAGNET_RISE) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:magnetRisenOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -2130,7 +2142,7 @@ export class FloatingTag extends TypeImmuneTag { onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); if (this.sourceMove === MoveId.MAGNET_RISE) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:magnetRisenOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -2174,7 +2186,7 @@ export class TypeBoostTag extends BattlerTag { } override onAdd(pokemon: Pokemon): void { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:typeImmunityPowerBoost", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: i18next.t(`pokemonInfo:Type.${PokemonType[this.boostedType]}`), @@ -2183,7 +2195,7 @@ export class TypeBoostTag extends BattlerTag { } override onOverlap(pokemon: Pokemon): void { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:moveImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), ); } @@ -2197,7 +2209,7 @@ export class CritBoostTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:critBoostOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -2211,7 +2223,7 @@ export class CritBoostTag extends BattlerTag { onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:critBoostOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -2257,7 +2269,7 @@ export class SaltCuredTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:saltCuredOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -2269,7 +2281,7 @@ export class SaltCuredTag extends BattlerTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.SALT_CURE), ); @@ -2282,7 +2294,7 @@ export class SaltCuredTag extends BattlerTag { result: HitResult.INDIRECT, }); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:saltCuredLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName(), @@ -2320,7 +2332,7 @@ export class CursedTag extends BattlerTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.SALT_CURE), ); @@ -2329,7 +2341,7 @@ export class CursedTag extends BattlerTag { if (!cancelled.value) { pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT }); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:cursedLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -2497,7 +2509,7 @@ export class CommandedTag extends BattlerTag { /** Caches the Tatsugiri's form key and sharply boosts the tagged Pokemon's stats */ override onAdd(pokemon: Pokemon): void { this._tatsugiriFormKey = this.getSourcePokemon()?.getFormKey() ?? "curly"; - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase( pokemon.getBattlerIndex(), true, @@ -2572,7 +2584,7 @@ export class StockpilingTag extends BattlerTag { if (this.stockpiledCount < 3) { this.stockpiledCount++; - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:stockpilingOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), stockpiledCount: this.stockpiledCount, @@ -2580,7 +2592,7 @@ export class StockpilingTag extends BattlerTag { ); // Attempt to increase DEF and SPDEF by one stage, keeping track of successful changes. - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase( pokemon.getBattlerIndex(), true, @@ -2608,13 +2620,13 @@ export class StockpilingTag extends BattlerTag { const spDefChange = this.statChangeCounts[Stat.SPDEF]; if (defChange) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.DEF], -defChange, true, false, true), ); } if (spDefChange) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPDEF], -spDefChange, true, false, true), ); } @@ -2635,7 +2647,7 @@ export class GulpMissileTag extends BattlerTag { return true; } - const moveEffectPhase = globalScene.getCurrentPhase(); + const moveEffectPhase = globalScene.phaseManager.getCurrentPhase(); if (moveEffectPhase?.is("MoveEffectPhase")) { const attacker = moveEffectPhase.getUserPokemon(); @@ -2655,7 +2667,9 @@ export class GulpMissileTag extends BattlerTag { } if (this.tagType === BattlerTagType.GULP_MISSILE_ARROKUDA) { - globalScene.unshiftPhase(new StatStageChangePhase(attacker.getBattlerIndex(), false, [Stat.DEF], -1)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(attacker.getBattlerIndex(), false, [Stat.DEF], -1), + ); } else { attacker.trySetStatus(StatusEffect.PARALYSIS, true, pokemon); } @@ -2803,7 +2817,7 @@ export class HealBlockTag extends MoveRestrictionBattlerTag { override onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battle:battlerTagsHealBlockOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -2833,7 +2847,7 @@ export class TarShotTag extends BattlerTag { } override onAdd(pokemon: Pokemon): void { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:tarShotOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -2852,7 +2866,7 @@ export class ElectrifiedTag extends BattlerTag { override onAdd(pokemon: Pokemon): void { // "{pokemonNameWithAffix}'s moves have been electrified!" - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:electrifiedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -2878,7 +2892,7 @@ export class AutotomizedTag extends BattlerTag { onAdd(pokemon: Pokemon): void { const minWeight = 0.1; if (pokemon.getWeight() > minWeight) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:autotomizeOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -2924,14 +2938,14 @@ export class SubstituteTag extends BattlerTag { // Queue battle animation and message globalScene.triggerPokemonBattleAnim(pokemon, PokemonAnimType.SUBSTITUTE_ADD); if (this.sourceMove === MoveId.SHED_TAIL) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:shedTailOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), 1500, ); } else { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:substituteOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -2951,7 +2965,7 @@ export class SubstituteTag extends BattlerTag { } else { this.sprite.destroy(); } - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:substituteOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -2987,7 +3001,7 @@ export class SubstituteTag extends BattlerTag { /** If the Substitute redirects damage, queue a message to indicate it. */ onHit(pokemon: Pokemon): void { - const moveEffectPhase = globalScene.getCurrentPhase(); + const moveEffectPhase = globalScene.phaseManager.getCurrentPhase(); if (moveEffectPhase?.is("MoveEffectPhase")) { const attacker = moveEffectPhase.getUserPokemon(); if (!attacker) { @@ -2997,7 +3011,7 @@ export class SubstituteTag extends BattlerTag { const firstHit = attacker.turnData.hitCount === attacker.turnData.hitsLeft; if (firstHit && move.hitsSubstitute(attacker, pokemon)) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:substituteOnHit", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -3074,7 +3088,7 @@ export class TormentTag extends MoveRestrictionBattlerTag { */ override onAdd(pokemon: Pokemon) { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:tormentOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -3132,7 +3146,7 @@ export class TauntTag extends MoveRestrictionBattlerTag { override onAdd(pokemon: Pokemon) { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:tauntOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -3143,7 +3157,7 @@ export class TauntTag extends MoveRestrictionBattlerTag { public override onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:tauntOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -3253,7 +3267,7 @@ export class SyrupBombTag extends BattlerTag { */ override onAdd(pokemon: Pokemon) { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:syrupBombOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -3271,12 +3285,12 @@ export class SyrupBombTag extends BattlerTag { return false; } // Custom message in lieu of an animation in mainline - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:syrupBombLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPD], -1, true, false, true), ); return --this.turnCount > 0; @@ -3302,7 +3316,7 @@ export class TelekinesisTag extends BattlerTag { } override onAdd(pokemon: Pokemon) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:telekinesisOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -3321,7 +3335,7 @@ export class PowerTrickTag extends BattlerTag { onAdd(pokemon: Pokemon): void { this.swapStat(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:powerTrickActive", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -3330,7 +3344,7 @@ export class PowerTrickTag extends BattlerTag { onRemove(pokemon: Pokemon): void { this.swapStat(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:powerTrickActive", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -3368,7 +3382,7 @@ export class GrudgeTag extends BattlerTag { onAdd(pokemon: Pokemon) { super.onAdd(pokemon); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:grudgeOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -3389,7 +3403,7 @@ export class GrudgeTag extends BattlerTag { const lastMoveData = sourcePokemon.getMoveset().find(m => m.moveId === lastMove.move); if (lastMoveData && lastMove.move !== MoveId.STRUGGLE) { lastMoveData.ppUsed = lastMoveData.getMovePp(); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:grudgeLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: lastMoveData.getName(), @@ -3417,7 +3431,9 @@ export class PsychoShiftTag extends BattlerTag { */ override lapse(pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean { if (pokemon.status && pokemon.isActive(true)) { - globalScene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); + globalScene.phaseManager.queueMessage( + getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)), + ); pokemon.resetStatus(); pokemon.updateInfo(); } @@ -3439,7 +3455,7 @@ export class MagicCoatTag extends BattlerTag { */ override onAdd(pokemon: Pokemon) { // "{pokemonNameWithAffix} shrouded itself with Magic Coat!" - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:magicCoatOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -3673,7 +3689,7 @@ export function loadBattlerTag(source: BattlerTag | any): BattlerTag { * corresponding {@linkcode Move} and user {@linkcode Pokemon} */ function getMoveEffectPhaseData(_pokemon: Pokemon): { phase: MoveEffectPhase; attacker: Pokemon; move: Move } | null { - const phase = globalScene.getCurrentPhase(); + const phase = globalScene.phaseManager.getCurrentPhase(); if (phase?.is("MoveEffectPhase")) { return { phase: phase, diff --git a/src/data/berry.ts b/src/data/berry.ts index ecc3e92ca64..defc9b85541 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -3,11 +3,7 @@ import type Pokemon from "../field/pokemon"; import { HitResult } from "../field/pokemon"; import { getStatusEffectHealText } from "./status-effect"; import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils/common"; -import { - DoubleBerryEffectAbAttr, - ReduceBerryUseThresholdAbAttr, - applyAbAttrs, -} from "./abilities/ability"; +import { DoubleBerryEffectAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs } from "./abilities/ability"; import i18next from "i18next"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; @@ -79,7 +75,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { { const hpHealed = new NumberHolder(toDmgValue(consumer.getMaxHp() / 4)); applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, hpHealed); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new PokemonHealPhase( consumer.getBattlerIndex(), hpHealed.value, @@ -95,7 +91,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { case BerryType.LUM: { if (consumer.status) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( getStatusEffectHealText(consumer.status.effect, getPokemonNameWithAffix(consumer)), ); } @@ -113,7 +109,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { const stat: BattleStat = berryType - BerryType.ENIGMA; const statStages = new NumberHolder(1); applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, statStages); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase(consumer.getBattlerIndex(), true, [stat], statStages.value), ); } @@ -130,7 +126,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { const randStat = randSeedInt(Stat.SPD, Stat.ATK); const stages = new NumberHolder(2); applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, stages); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase(consumer.getBattlerIndex(), true, [randStat], stages.value), ); } @@ -144,7 +140,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { consumer.getMoveset().find(m => m.ppUsed < m.getMovePp()); if (ppRestoreMove) { ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battle:ppHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(consumer), moveName: ppRestoreMove.getName(), diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index f610c745fd2..94d0ae50523 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -1048,7 +1048,7 @@ function ChargeMove(Base: TBase) { * @param target the {@linkcode Pokemon} targeted by this move (optional) */ showChargeText(user: Pokemon, target?: Pokemon): void { - globalScene.queueMessage(this._chargeText + globalScene.phaseManager.queueMessage(this._chargeText .replace("{USER}", getPokemonNameWithAffix(user)) .replace("{TARGET}", getPokemonNameWithAffix(target)) ); @@ -1310,7 +1310,7 @@ export class MessageHeaderAttr extends MoveHeaderAttr { : this.message(user, move); if (message) { - globalScene.queueMessage(message); + globalScene.phaseManager.queueMessage(message); return true; } return false; @@ -1363,7 +1363,7 @@ export class PreMoveMessageAttr extends MoveAttr { ? this.message as string : this.message(user, target, move); if (message) { - globalScene.queueMessage(message, 500); + globalScene.phaseManager.queueMessage(message, 500); return true; } return false; @@ -1620,14 +1620,14 @@ export class SurviveDamageAttr extends ModifiedDamageAttr { export class SplashAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - globalScene.queueMessage(i18next.t("moveTriggers:splash")); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:splash")); return true; } } export class CelebrateAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - globalScene.queueMessage(i18next.t("moveTriggers:celebrate", { playerName: loggedInUser?.username })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:celebrate", { playerName: loggedInUser?.username })); return true; } } @@ -1677,7 +1677,7 @@ export class RecoilAttr extends MoveEffectAttr { } user.damageAndUpdate(recoilDamage, { result: HitResult.INDIRECT, ignoreSegments: true }); - globalScene.queueMessage(i18next.t("moveTriggers:hitWithRecoil", { pokemonName: getPokemonNameWithAffix(user) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:hitWithRecoil", { pokemonName: getPokemonNameWithAffix(user) })); user.turnData.damageTaken += recoilDamage; return true; @@ -1789,7 +1789,7 @@ export class HalfSacrificialAttr extends MoveEffectAttr { applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled); if (!cancelled.value) { user.damageAndUpdate(toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT, ignoreSegments: true }); - globalScene.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", { pokemonName: getPokemonNameWithAffix(user) })); // Queue recoil message + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", { pokemonName: getPokemonNameWithAffix(user) })); // Queue recoil message } return true; } @@ -1890,7 +1890,7 @@ export class HealAttr extends MoveEffectAttr { * This heals the target and shows the appropriate message. */ addHealPhase(target: Pokemon, healRatio: number) { - globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), + globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), toDmgValue(target.getMaxHp() * healRatio), i18next.t("moveTriggers:healHp", { pokemonName: getPokemonNameWithAffix(target) }), true, !this.showAnim)); } @@ -1934,7 +1934,7 @@ export class PartyStatusCureAttr extends MoveEffectAttr { partyPokemon.forEach(p => this.cureStatus(p, user.id)); if (this.message) { - globalScene.queueMessage(this.message); + globalScene.phaseManager.queueMessage(this.message); } return true; @@ -1954,8 +1954,8 @@ export class PartyStatusCureAttr extends MoveEffectAttr { pokemon.updateInfo(); } else { // TODO: Ability displays should be handled by the ability - globalScene.queueAbilityDisplay(pokemon, pokemon.getPassiveAbility()?.id === this.abilityCondition, true); - globalScene.queueAbilityDisplay(pokemon, pokemon.getPassiveAbility()?.id === this.abilityCondition, false); + globalScene.phaseManager.queueAbilityDisplay(pokemon, pokemon.getPassiveAbility()?.id === this.abilityCondition, true); + globalScene.phaseManager.queueAbilityDisplay(pokemon, pokemon.getPassiveAbility()?.id === this.abilityCondition, false); } } } @@ -2021,7 +2021,7 @@ export class SacrificialFullRestoreAttr extends SacrificialAttr { const party = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); const maxPartyMemberHp = party.map(p => p.getMaxHp()).reduce((maxHp: number, hp: number) => Math.max(hp, maxHp), 0); - globalScene.pushPhase( + globalScene.phaseManager.pushPhase( new PokemonHealPhase( user.getBattlerIndex(), maxPartyMemberHp, @@ -2233,7 +2233,7 @@ export class HitHealAttr extends MoveEffectAttr { message = ""; } } - globalScene.unshiftPhase(new PokemonHealPhase(user.getBattlerIndex(), healAmount, message, false, true)); + globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(user.getBattlerIndex(), healAmount, message, false, true)); return true; } @@ -2565,7 +2565,7 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr { return false; } - globalScene.queueMessage(i18next.t("moveTriggers:stoleItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: stolenItem.type.name })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:stoleItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: stolenItem.type.name })); return true; } @@ -2643,9 +2643,9 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { globalScene.updateModifiers(target.isPlayer()); if (this.berriesOnly) { - globalScene.queueMessage(i18next.t("moveTriggers:incineratedItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: removedItem.type.name })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:incineratedItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: removedItem.type.name })); } else { - globalScene.queueMessage(i18next.t("moveTriggers:knockedOffItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: removedItem.type.name })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:knockedOffItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: removedItem.type.name })); } return true; @@ -2777,7 +2777,7 @@ export class StealEatBerryAttr extends EatBerryAttr { this.chosenBerry = heldBerries[user.randBattleSeedInt(heldBerries.length)]; applyPostItemLostAbAttrs(PostItemLostAbAttr, target, false); const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name }); - globalScene.queueMessage(message); + globalScene.phaseManager.queueMessage(message); this.reduceBerryModifier(target); this.eatBerry(user, target); @@ -2822,7 +2822,7 @@ export class HealStatusEffectAttr extends MoveEffectAttr { const pokemon = this.selfTarget ? user : target; if (pokemon.status && this.effects.includes(pokemon.status.effect)) { - globalScene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); + globalScene.phaseManager.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); pokemon.resetStatus(); pokemon.updateInfo(); @@ -3067,13 +3067,13 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { if (!virtual) { overridden.value = true; - globalScene.unshiftPhase(new MoveAnimPhase(new MoveChargeAnim(this.chargeAnim, move.id, user))); - globalScene.queueMessage(this.chargeText.replace("{TARGET}", getPokemonNameWithAffix(target)).replace("{USER}", getPokemonNameWithAffix(user))); + globalScene.phaseManager.unshiftPhase(new MoveAnimPhase(new MoveChargeAnim(this.chargeAnim, move.id, user))); + globalScene.phaseManager.queueMessage(this.chargeText.replace("{TARGET}", getPokemonNameWithAffix(target)).replace("{USER}", getPokemonNameWithAffix(user))); user.pushMoveHistory({ move: move.id, targets: [ target.getBattlerIndex() ], result: MoveResult.OTHER }); const side = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; globalScene.arena.addTag(this.tagType, 3, move.id, user.id, side, false, target.getBattlerIndex()); } else { - globalScene.queueMessage(i18next.t("moveTriggers:tookMoveAttack", { pokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(target.id) ?? undefined), moveName: move.name })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:tookMoveAttack", { pokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(target.id) ?? undefined), moveName: move.name })); } return true; @@ -3103,29 +3103,29 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr { override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (user.turnData.combiningPledge) { // "The two moves have become one!\nIt's a combined move!" - globalScene.queueMessage(i18next.t("moveTriggers:combiningPledge")); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:combiningPledge")); return false; } const overridden = args[0] as BooleanHolder; - const allyMovePhase = globalScene.findPhase((phase) => phase.is("MovePhase") && phase.pokemon.isPlayer() === user.isPlayer()); + const allyMovePhase = globalScene.phaseManager.findPhase((phase) => phase.is("MovePhase") && phase.pokemon.isPlayer() === user.isPlayer()); if (allyMovePhase) { const allyMove = allyMovePhase.move.getMove(); if (allyMove !== move && allyMove.hasAttr(AwaitCombinedPledgeAttr)) { [ user, allyMovePhase.pokemon ].forEach((p) => p.turnData.combiningPledge = move.id); // "{userPokemonName} is waiting for {allyPokemonName}'s move..." - globalScene.queueMessage(i18next.t("moveTriggers:awaitingPledge", { + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:awaitingPledge", { userPokemonName: getPokemonNameWithAffix(user), allyPokemonName: getPokemonNameWithAffix(allyMovePhase.pokemon) })); // Move the ally's MovePhase (if needed) so that the ally moves next - const allyMovePhaseIndex = globalScene.phaseQueue.indexOf(allyMovePhase); - const firstMovePhaseIndex = globalScene.phaseQueue.findIndex((phase) => phase.is("MovePhase")); + const allyMovePhaseIndex = globalScene.phaseManager.phaseQueue.indexOf(allyMovePhase); + const firstMovePhaseIndex = globalScene.phaseManager.phaseQueue.findIndex((phase) => phase.is("MovePhase")); if (allyMovePhaseIndex !== firstMovePhaseIndex) { - globalScene.prependToPhase(globalScene.phaseQueue.splice(allyMovePhaseIndex, 1)[0], MovePhase); + globalScene.phaseManager.prependToPhase(globalScene.phaseManager.phaseQueue.splice(allyMovePhaseIndex, 1)[0], MovePhase); } overridden.value = true; @@ -3207,7 +3207,7 @@ export class StatStageChangeAttr extends MoveEffectAttr { const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); if (moveChance < 0 || moveChance === 100 || user.randBattleSeedInt(100) < moveChance) { const stages = this.getLevels(user); - globalScene.unshiftPhase(new StatStageChangePhase((this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, stages, this.showMessage)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase((this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, stages, this.showMessage)); return true; } @@ -3432,7 +3432,7 @@ export class AcupressureStatStageChangeAttr extends MoveEffectAttr { const randStats = BATTLE_STATS.filter((s) => target.getStatStage(s) < 6); if (randStats.length > 0) { const boostStat = [ randStats[user.randBattleSeedInt(randStats.length)] ]; - globalScene.unshiftPhase(new StatStageChangePhase(target.getBattlerIndex(), this.selfTarget, boostStat, 2)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(target.getBattlerIndex(), this.selfTarget, boostStat, 2)); return true; } return false; @@ -3510,7 +3510,7 @@ export class OrderUpStatBoostAttr extends MoveEffectAttr { break; } - globalScene.unshiftPhase(new StatStageChangePhase(user.getBattlerIndex(), this.selfTarget, [ increasedStat ], 1)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(user.getBattlerIndex(), this.selfTarget, [ increasedStat ], 1)); return true; } } @@ -3533,7 +3533,7 @@ export class CopyStatsAttr extends MoveEffectAttr { } target.updateInfo(); user.updateInfo(); - globalScene.queueMessage(i18next.t("moveTriggers:copiedStatChanges", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:copiedStatChanges", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); return true; } @@ -3552,7 +3552,7 @@ export class InvertStatsAttr extends MoveEffectAttr { target.updateInfo(); user.updateInfo(); - globalScene.queueMessage(i18next.t("moveTriggers:invertStats", { pokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:invertStats", { pokemonName: getPokemonNameWithAffix(target) })); return true; } @@ -3570,10 +3570,10 @@ export class ResetStatsAttr extends MoveEffectAttr { // Target all pokemon on the field when Freezy Frost or Haze are used const activePokemon = globalScene.getField(true); activePokemon.forEach((p) => this.resetStats(p)); - globalScene.queueMessage(i18next.t("moveTriggers:statEliminated")); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:statEliminated")); } else { // Affects only the single target when Clear Smog is used this.resetStats(target); - globalScene.queueMessage(i18next.t("moveTriggers:resetStats", { pokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:resetStats", { pokemonName: getPokemonNameWithAffix(target) })); } return true; } @@ -3623,9 +3623,9 @@ export class SwapStatStagesAttr extends MoveEffectAttr { user.updateInfo(); if (this.stats.length === 7) { - globalScene.queueMessage(i18next.t("moveTriggers:switchedStatChanges", { pokemonName: getPokemonNameWithAffix(user) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:switchedStatChanges", { pokemonName: getPokemonNameWithAffix(user) })); } else if (this.stats.length === 2) { - globalScene.queueMessage(i18next.t("moveTriggers:switchedTwoStatChanges", { + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:switchedTwoStatChanges", { pokemonName: getPokemonNameWithAffix(user), firstStat: i18next.t(getStatKey(this.stats[0])), secondStat: i18next.t(getStatKey(this.stats[1])) @@ -4227,7 +4227,7 @@ export class PresentPowerAttr extends VariablePowerAttr { // If this move is multi-hit, disable all other hits user.turnData.hitCount = 1; user.turnData.hitsLeft = 1; - globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), + globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), toDmgValue(target.getMaxHp() / 4), i18next.t("moveTriggers:regainedHealth", { pokemonName: getPokemonNameWithAffix(target) }), true)); } @@ -4476,7 +4476,7 @@ export class CueNextRoundAttr extends MoveEffectAttr { } override apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean { - const nextRoundPhase = globalScene.findPhase(phase => + const nextRoundPhase = globalScene.phaseManager.findPhase(phase => phase.is("MovePhase") && phase.move.moveId === MoveId.ROUND ); @@ -4485,10 +4485,10 @@ export class CueNextRoundAttr extends MoveEffectAttr { } // Update the phase queue so that the next Pokemon using Round moves next - const nextRoundIndex = globalScene.phaseQueue.indexOf(nextRoundPhase); - const nextMoveIndex = globalScene.phaseQueue.findIndex(phase => phase.is("MovePhase")); + const nextRoundIndex = globalScene.phaseManager.phaseQueue.indexOf(nextRoundPhase); + const nextMoveIndex = globalScene.phaseManager.phaseQueue.findIndex(phase => phase.is("MovePhase")); if (nextRoundIndex !== nextMoveIndex) { - globalScene.prependToPhase(globalScene.phaseQueue.splice(nextRoundIndex, 1)[0], MovePhase); + globalScene.phaseManager.prependToPhase(globalScene.phaseManager.phaseQueue.splice(nextRoundIndex, 1)[0], MovePhase); } // Mark the corresponding Pokemon as having "joined the Round" (for doubling power later) @@ -4546,14 +4546,14 @@ export class SpectralThiefAttr extends StatChangeBeforeDmgCalcAttr { */ const availableToSteal = Math.min(statStageValueTarget, 6 - statStageValueUser); - globalScene.unshiftPhase(new StatStageChangePhase(user.getBattlerIndex(), this.selfTarget, [ s ], availableToSteal)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(user.getBattlerIndex(), this.selfTarget, [ s ], availableToSteal)); target.setStatStage(s, statStageValueTarget - availableToSteal); } } target.updateInfo(); user.updateInfo(); - globalScene.queueMessage(i18next.t("moveTriggers:stealPositiveStats", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:stealPositiveStats", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); return true; } @@ -5368,7 +5368,7 @@ const crashDamageFunc = (user: Pokemon, move: Move) => { } user.damageAndUpdate(toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT }); - globalScene.queueMessage(i18next.t("moveTriggers:keptGoingAndCrashed", { pokemonName: getPokemonNameWithAffix(user) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:keptGoingAndCrashed", { pokemonName: getPokemonNameWithAffix(user) })); user.turnData.damageTaken += toDmgValue(user.getMaxHp() / 2); return true; @@ -5581,7 +5581,7 @@ export class FallDownAttr extends AddBattlerTagAttr { */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!target.isGrounded()) { - globalScene.queueMessage(i18next.t("moveTriggers:fallDown", { targetPokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:fallDown", { targetPokemonName: getPokemonNameWithAffix(target) })); } return super.apply(user, target, move, args); } @@ -5665,12 +5665,12 @@ export class CurseAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move:Move, args: any[]): boolean { if (user.getTypes(true).includes(PokemonType.GHOST)) { if (target.getTag(BattlerTagType.CURSED)) { - globalScene.queueMessage(i18next.t("battle:attackFailed")); + globalScene.phaseManager.queueMessage(i18next.t("battle:attackFailed")); return false; } const curseRecoilDamage = Math.max(1, Math.floor(user.getMaxHp() / 2)); user.damageAndUpdate(curseRecoilDamage, { result: HitResult.INDIRECT, ignoreSegments: true }); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battlerTags:cursedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user), pokemonName: getPokemonNameWithAffix(target) @@ -5680,8 +5680,8 @@ export class CurseAttr extends MoveEffectAttr { target.addTag(BattlerTagType.CURSED, 0, move.id, user.id); return true; } else { - globalScene.unshiftPhase(new StatStageChangePhase(user.getBattlerIndex(), true, [ Stat.ATK, Stat.DEF ], 1)); - globalScene.unshiftPhase(new StatStageChangePhase(user.getBattlerIndex(), true, [ Stat.SPD ], -1)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(user.getBattlerIndex(), true, [ Stat.ATK, Stat.DEF ], 1)); + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(user.getBattlerIndex(), true, [ Stat.SPD ], -1)); return true; } } @@ -5745,7 +5745,7 @@ export class ConfuseAttr extends AddBattlerTagAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!this.selfTarget && target.isSafeguarded(user)) { if (move.category === MoveCategory.STATUS) { - globalScene.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(target) })); } return false; } @@ -5802,7 +5802,7 @@ export class IgnoreAccuracyAttr extends AddBattlerTagAttr { return false; } - globalScene.queueMessage(i18next.t("moveTriggers:tookAimAtTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:tookAimAtTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); return true; } @@ -5818,7 +5818,7 @@ export class FaintCountdownAttr extends AddBattlerTagAttr { return false; } - globalScene.queueMessage(i18next.t("moveTriggers:faintCountdown", { pokemonName: getPokemonNameWithAffix(target), turnCount: this.turnCountMin - 1 })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:faintCountdown", { pokemonName: getPokemonNameWithAffix(target), turnCount: this.turnCountMin - 1 })); return true; } @@ -6102,7 +6102,7 @@ export class SwapArenaTagsAttr extends MoveEffectAttr { } - globalScene.queueMessage(i18next.t("moveTriggers:swapArenaTags", { pokemonName: getPokemonNameWithAffix(user) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:swapArenaTags", { pokemonName: getPokemonNameWithAffix(user) })); return true; } } @@ -6154,7 +6154,7 @@ export class RevivalBlessingAttr extends MoveEffectAttr { override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { // If user is player, checks if the user has fainted pokemon if (user.isPlayer()) { - globalScene.unshiftPhase(new RevivalBlessingPhase(user)); + globalScene.phaseManager.unshiftPhase(new RevivalBlessingPhase(user)); return true; } else if (user.isEnemy() && user.hasTrainer() && globalScene.getEnemyParty().findIndex((p) => p.isFainted() && !p.isBoss()) > -1) { // If used by an enemy trainer with at least one fainted non-boss Pokemon, this @@ -6164,20 +6164,20 @@ export class RevivalBlessingAttr extends MoveEffectAttr { const slotIndex = globalScene.getEnemyParty().findIndex((p) => pokemon.id === p.id); pokemon.resetStatus(true, false, false, true); pokemon.heal(Math.min(toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); - globalScene.queueMessage(i18next.t("moveTriggers:revivalBlessing", { pokemonName: getPokemonNameWithAffix(pokemon) }), 0, true); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:revivalBlessing", { pokemonName: getPokemonNameWithAffix(pokemon) }), 0, true); const allyPokemon = user.getAlly(); if (globalScene.currentBattle.double && globalScene.getEnemyParty().length > 1 && !isNullOrUndefined(allyPokemon)) { // Handle cases where revived pokemon needs to get switched in on same turn if (allyPokemon.isFainted() || allyPokemon === pokemon) { // Enemy switch phase should be removed and replaced with the revived pkmn switching in - globalScene.tryRemovePhase((phase: SwitchSummonPhase) => phase.is("SwitchSummonPhase") && phase.getPokemon() === pokemon); + globalScene.phaseManager.tryRemovePhase((phase: SwitchSummonPhase) => phase.is("SwitchSummonPhase") && phase.getPokemon() === pokemon); // If the pokemon being revived was alive earlier in the turn, cancel its move // (revived pokemon can't move in the turn they're brought back) - globalScene.findPhase((phase: MovePhase) => phase.pokemon === pokemon)?.cancel(); + globalScene.phaseManager.findPhase((phase: MovePhase) => phase.pokemon === pokemon)?.cancel(); if (user.fieldPosition === FieldPosition.CENTER) { user.setFieldPosition(FieldPosition.LEFT); } - globalScene.unshiftPhase(new SwitchSummonPhase(SwitchType.SWITCH, allyPokemon.getFieldIndex(), slotIndex, false, false)); + globalScene.phaseManager.unshiftPhase(new SwitchSummonPhase(SwitchType.SWITCH, allyPokemon.getFieldIndex(), slotIndex, false, false)); } } return true; @@ -6255,7 +6255,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { if (this.switchType === SwitchType.FORCE_SWITCH) { switchOutTarget.leaveField(true); const slotIndex = eligibleNewIndices[user.randBattleSeedInt(eligibleNewIndices.length)]; - globalScene.prependToPhase( + globalScene.phaseManager.prependToPhase( new SwitchSummonPhase( this.switchType, switchOutTarget.getFieldIndex(), @@ -6267,7 +6267,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { ); } else { switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); - globalScene.prependToPhase( + globalScene.phaseManager.prependToPhase( new SwitchPhase( this.switchType, switchOutTarget.getFieldIndex(), @@ -6298,7 +6298,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { if (this.switchType === SwitchType.FORCE_SWITCH) { switchOutTarget.leaveField(true); const slotIndex = eligibleNewIndices[user.randBattleSeedInt(eligibleNewIndices.length)]; - globalScene.prependToPhase( + globalScene.phaseManager.prependToPhase( new SwitchSummonPhase( this.switchType, switchOutTarget.getFieldIndex(), @@ -6310,7 +6310,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { ); } else { switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); - globalScene.prependToPhase( + globalScene.phaseManager.prependToPhase( new SwitchSummonPhase( this.switchType, switchOutTarget.getFieldIndex(), @@ -6339,7 +6339,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { if (switchOutTarget.hp > 0) { switchOutTarget.leaveField(false); - globalScene.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500); // in double battles redirect potential moves off fled pokemon if (globalScene.currentBattle.double && !isNullOrUndefined(allyPokemon)) { @@ -6351,13 +6351,13 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { globalScene.clearEnemyHeldItemModifiers(switchOutTarget); if (!allyPokemon?.isActive(true) && switchOutTarget.hp) { - globalScene.pushPhase(new BattleEndPhase(false)); + globalScene.phaseManager.pushPhase(new BattleEndPhase(false)); if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { - globalScene.pushPhase(new SelectBiomePhase()); + globalScene.phaseManager.pushPhase(new SelectBiomePhase()); } - globalScene.pushPhase(new NewBattlePhase()); + globalScene.phaseManager.pushPhase(new NewBattlePhase()); } } @@ -6525,7 +6525,7 @@ export class CopyTypeAttr extends MoveEffectAttr { user.summonData.types = targetTypes; user.updateInfo(); - globalScene.queueMessage(i18next.t("moveTriggers:copyType", { pokemonName: getPokemonNameWithAffix(user), targetPokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:copyType", { pokemonName: getPokemonNameWithAffix(user), targetPokemonName: getPokemonNameWithAffix(target) })); return true; } @@ -6556,7 +6556,7 @@ export class CopyBiomeTypeAttr extends MoveEffectAttr { user.summonData.types = [ typeChange ]; user.updateInfo(); - globalScene.queueMessage(i18next.t("moveTriggers:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), typeName: i18next.t(`pokemonInfo:Type.${PokemonType[typeChange]}`) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), typeName: i18next.t(`pokemonInfo:Type.${PokemonType[typeChange]}`) })); return true; } @@ -6661,7 +6661,7 @@ export class ChangeTypeAttr extends MoveEffectAttr { target.summonData.types = [ this.type ]; target.updateInfo(); - globalScene.queueMessage(i18next.t("moveTriggers:transformedIntoType", { pokemonName: getPokemonNameWithAffix(target), typeName: i18next.t(`pokemonInfo:Type.${PokemonType[this.type]}`) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:transformedIntoType", { pokemonName: getPokemonNameWithAffix(target), typeName: i18next.t(`pokemonInfo:Type.${PokemonType[this.type]}`) })); return true; } @@ -6684,7 +6684,7 @@ export class AddTypeAttr extends MoveEffectAttr { target.summonData.addedType = this.type; target.updateInfo(); - globalScene.queueMessage(i18next.t("moveTriggers:addType", { typeName: i18next.t(`pokemonInfo:Type.${PokemonType[this.type]}`), pokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:addType", { typeName: i18next.t(`pokemonInfo:Type.${PokemonType[this.type]}`), pokemonName: getPokemonNameWithAffix(target) })); return true; } @@ -6706,7 +6706,7 @@ export class FirstMoveTypeAttr extends MoveEffectAttr { const firstMoveType = target.getMoveset()[0].getMove().type; user.summonData.types = [ firstMoveType ]; - globalScene.queueMessage(i18next.t("battle:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), type: i18next.t(`pokemonInfo:Type.${PokemonType[firstMoveType]}`) })); + globalScene.phaseManager.queueMessage(i18next.t("battle:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), type: i18next.t(`pokemonInfo:Type.${PokemonType[firstMoveType]}`) })); return true; } @@ -6725,7 +6725,7 @@ class CallMoveAttr extends OverrideMoveEffectAttr { const replaceMoveTarget = move.moveTarget === MoveTarget.NEAR_OTHER ? MoveTarget.NEAR_ENEMY : undefined; const moveTargets = getMoveTargets(user, move.id, replaceMoveTarget); if (moveTargets.targets.length === 0) { - globalScene.queueMessage(i18next.t("battle:attackFailed")); + globalScene.phaseManager.queueMessage(i18next.t("battle:attackFailed")); console.log("CallMoveAttr failed due to no targets."); return false; } @@ -6733,8 +6733,8 @@ class CallMoveAttr extends OverrideMoveEffectAttr { ? moveTargets.targets : [ this.hasTarget ? target.getBattlerIndex() : moveTargets.targets[user.randBattleSeedInt(moveTargets.targets.length)] ]; // account for Mirror Move having a target already user.getMoveQueue().push({ move: move.id, targets: targets, virtual: true, ignorePP: true }); - globalScene.unshiftPhase(new LoadMoveAnimPhase(move.id)); - globalScene.unshiftPhase(new MovePhase(user, targets, new PokemonMove(move.id, 0, 0, true), true, true)); + globalScene.phaseManager.unshiftPhase(new LoadMoveAnimPhase(move.id)); + globalScene.phaseManager.unshiftPhase(new MovePhase(user, targets, new PokemonMove(move.id, 0, 0, true), true, true)); return true; } } @@ -6962,8 +6962,8 @@ export class NaturePowerAttr extends OverrideMoveEffectAttr { } user.getMoveQueue().push({ move: moveId, targets: [ target.getBattlerIndex() ], ignorePP: true }); - globalScene.unshiftPhase(new LoadMoveAnimPhase(moveId)); - globalScene.unshiftPhase(new MovePhase(user, [ target.getBattlerIndex() ], new PokemonMove(moveId, 0, 0, true), true)); + globalScene.phaseManager.unshiftPhase(new LoadMoveAnimPhase(moveId)); + globalScene.phaseManager.unshiftPhase(new MovePhase(user, [ target.getBattlerIndex() ], new PokemonMove(moveId, 0, 0, true), true)); return true; } } @@ -7044,13 +7044,13 @@ export class RepeatMoveAttr extends MoveEffectAttr { } } - globalScene.queueMessage(i18next.t("moveTriggers:instructingMove", { + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:instructingMove", { userPokemonName: getPokemonNameWithAffix(user), targetPokemonName: getPokemonNameWithAffix(target) })); target.getMoveQueue().unshift({ move: lastMove.move, targets: moveTargets, ignorePP: false }); target.turnData.extraTurns++; - globalScene.appendToPhase(new MovePhase(target, moveTargets, movesetMove), MoveEndPhase); + globalScene.phaseManager.appendToPhase(new MovePhase(target, moveTargets, movesetMove), MoveEndPhase); return true; } @@ -7165,7 +7165,7 @@ export class ReducePpMoveAttr extends MoveEffectAttr { const message = i18next.t("battle:ppReduced", { targetName: getPokemonNameWithAffix(target), moveName: movesetMove.getName(), reduction: (movesetMove.ppUsed) - lastPpUsed }); globalScene.eventTarget.dispatchEvent(new MoveUsedEvent(target.id, movesetMove.getMove(), movesetMove.ppUsed)); - globalScene.queueMessage(message); + globalScene.phaseManager.queueMessage(message); return true; } @@ -7276,7 +7276,7 @@ export class MovesetCopyMoveAttr extends OverrideMoveEffectAttr { user.summonData.moveset = user.getMoveset().slice(0); user.summonData.moveset[thisMoveIndex] = new PokemonMove(copiedMove.id, 0, 0); - globalScene.queueMessage(i18next.t("moveTriggers:copiedMove", { pokemonName: getPokemonNameWithAffix(user), moveName: copiedMove.name })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:copiedMove", { pokemonName: getPokemonNameWithAffix(user), moveName: copiedMove.name })); return true; } @@ -7326,7 +7326,7 @@ export class SketchAttr extends MoveEffectAttr { user.setMove(sketchIndex, sketchedMove.id); - globalScene.queueMessage(i18next.t("moveTriggers:sketchedMove", { pokemonName: getPokemonNameWithAffix(user), moveName: sketchedMove.name })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:sketchedMove", { pokemonName: getPokemonNameWithAffix(user), moveName: sketchedMove.name })); return true; } @@ -7381,9 +7381,9 @@ export class AbilityChangeAttr extends MoveEffectAttr { globalScene.triggerPokemonFormChange(moveTarget, SpeciesFormChangeRevertWeatherFormTrigger); if (moveTarget.breakIllusion()) { - globalScene.queueMessage(i18next.t("abilityTriggers:illusionBreak", { pokemonName: getPokemonNameWithAffix(moveTarget) })); + globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:illusionBreak", { pokemonName: getPokemonNameWithAffix(moveTarget) })); } - globalScene.queueMessage(i18next.t("moveTriggers:acquiredAbility", { pokemonName: getPokemonNameWithAffix(moveTarget), abilityName: allAbilities[this.ability].name })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:acquiredAbility", { pokemonName: getPokemonNameWithAffix(moveTarget), abilityName: allAbilities[this.ability].name })); moveTarget.setTempAbility(allAbilities[this.ability]); globalScene.triggerPokemonFormChange(moveTarget, SpeciesFormChangeRevertWeatherFormTrigger); return true; @@ -7408,13 +7408,13 @@ export class AbilityCopyAttr extends MoveEffectAttr { return false; } - globalScene.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name })); user.setTempAbility(target.getAbility()); const ally = user.getAlly(); if (this.copyToPartner && globalScene.currentBattle?.double && !isNullOrUndefined(ally) && ally.hp) { // TODO is this the best way to check that the ally is active? - globalScene.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(ally), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(ally), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name })); ally.setTempAbility(target.getAbility()); } @@ -7447,7 +7447,7 @@ export class AbilityGiveAttr extends MoveEffectAttr { return false; } - globalScene.queueMessage(i18next.t("moveTriggers:acquiredAbility", { pokemonName: getPokemonNameWithAffix(target), abilityName: allAbilities[user.getAbility().id].name })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:acquiredAbility", { pokemonName: getPokemonNameWithAffix(target), abilityName: allAbilities[user.getAbility().id].name })); target.setTempAbility(user.getAbility()); @@ -7467,7 +7467,7 @@ export class SwitchAbilitiesAttr extends MoveEffectAttr { const tempAbility = user.getAbility(); - globalScene.queueMessage(i18next.t("moveTriggers:swappedAbilitiesWithTarget", { pokemonName: getPokemonNameWithAffix(user) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:swappedAbilitiesWithTarget", { pokemonName: getPokemonNameWithAffix(user) })); user.setTempAbility(target.getAbility()); target.setTempAbility(tempAbility); @@ -7497,7 +7497,7 @@ export class SuppressAbilitiesAttr extends MoveEffectAttr { return false; } - globalScene.queueMessage(i18next.t("moveTriggers:suppressAbilities", { pokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:suppressAbilities", { pokemonName: getPokemonNameWithAffix(target) })); target.suppressAbility(); @@ -7550,9 +7550,9 @@ export class TransformAttr extends MoveEffectAttr { return false; } - globalScene.unshiftPhase(new PokemonTransformPhase(user.getBattlerIndex(), target.getBattlerIndex())); + globalScene.phaseManager.unshiftPhase(new PokemonTransformPhase(user.getBattlerIndex(), target.getBattlerIndex())); - globalScene.queueMessage(i18next.t("moveTriggers:transformedIntoTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:transformedIntoTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); return true; } @@ -7589,7 +7589,7 @@ export class SwapStatAttr extends MoveEffectAttr { user.setStat(this.stat, target.getStat(this.stat, false), false); target.setStat(this.stat, temp, false); - globalScene.queueMessage(i18next.t("moveTriggers:switchedStat", { + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:switchedStat", { pokemonName: getPokemonNameWithAffix(user), stat: i18next.t(getStatKey(this.stat)), })); @@ -7635,7 +7635,7 @@ export class ShiftStatAttr extends MoveEffectAttr { user.setStat(this.statToSwitch, secondStat, false); user.setStat(this.statToSwitchWith, firstStat, false); - globalScene.queueMessage(i18next.t("moveTriggers:shiftedStats", { + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:shiftedStats", { pokemonName: getPokemonNameWithAffix(user), statToSwitch: i18next.t(getStatKey(this.statToSwitch)), statToSwitchWith: i18next.t(getStatKey(this.statToSwitchWith)) @@ -7694,7 +7694,7 @@ export class AverageStatsAttr extends MoveEffectAttr { target.setStat(s, avg, false); } - globalScene.queueMessage(i18next.t(this.msgKey, { pokemonName: getPokemonNameWithAffix(user) })); + globalScene.phaseManager.queueMessage(i18next.t(this.msgKey, { pokemonName: getPokemonNameWithAffix(user) })); return true; } @@ -7709,7 +7709,7 @@ export class MoneyAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move): boolean { globalScene.currentBattle.moneyScattered += globalScene.getWaveMoneyAmount(0.2); - globalScene.queueMessage(i18next.t("moveTriggers:coinsScatteredEverywhere")); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:coinsScatteredEverywhere")); return true; } } @@ -7733,7 +7733,7 @@ export class DestinyBondAttr extends MoveEffectAttr { * @returns true */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - globalScene.queueMessage(`${i18next.t("moveTriggers:tryingToTakeFoeDown", { pokemonName: getPokemonNameWithAffix(user) })}`); + globalScene.phaseManager.queueMessage(`${i18next.t("moveTriggers:tryingToTakeFoeDown", { pokemonName: getPokemonNameWithAffix(user) })}`); user.addTag(BattlerTagType.DESTINY_BOND, undefined, move.id, user.id); return true; } @@ -7847,12 +7847,12 @@ export class AfterYouAttr extends MoveEffectAttr { * @returns true */ override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { - globalScene.queueMessage(i18next.t("moveTriggers:afterYou", { targetName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:afterYou", { targetName: getPokemonNameWithAffix(target) })); //Will find next acting phase of the targeted pokémon, delete it and queue it next on successful delete. - const nextAttackPhase = globalScene.findPhase((phase) => phase.pokemon === target); - if (nextAttackPhase && globalScene.tryRemovePhase((phase: MovePhase) => phase.pokemon === target)) { - globalScene.prependToPhase(new MovePhase(target, [ ...nextAttackPhase.targets ], nextAttackPhase.move), MovePhase); + const nextAttackPhase = globalScene.phaseManager.findPhase((phase) => phase.pokemon === target); + if (nextAttackPhase && globalScene.phaseManager.tryRemovePhase((phase: MovePhase) => phase.pokemon === target)) { + globalScene.phaseManager.prependToPhase(new MovePhase(target, [ ...nextAttackPhase.targets ], nextAttackPhase.move), MovePhase); } return true; @@ -7875,19 +7875,19 @@ export class ForceLastAttr extends MoveEffectAttr { * @returns true */ override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { - globalScene.queueMessage(i18next.t("moveTriggers:forceLast", { targetPokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:forceLast", { targetPokemonName: getPokemonNameWithAffix(target) })); - const targetMovePhase = globalScene.findPhase((phase) => phase.pokemon === target); - if (targetMovePhase && !targetMovePhase.isForcedLast() && globalScene.tryRemovePhase((phase: MovePhase) => phase.pokemon === target)) { + const targetMovePhase = globalScene.phaseManager.findPhase((phase) => phase.pokemon === target); + if (targetMovePhase && !targetMovePhase.isForcedLast() && globalScene.phaseManager.tryRemovePhase((phase: MovePhase) => phase.pokemon === target)) { // Finding the phase to insert the move in front of - // Either the end of the turn or in front of another, slower move which has also been forced last - const prependPhase = globalScene.findPhase((phase) => + const prependPhase = globalScene.phaseManager.findPhase((phase) => [ MovePhase, MoveEndPhase ].every(cls => !(phase instanceof cls)) || (phase.is("MovePhase")) && phaseForcedSlower(phase, target, !!globalScene.arena.getTag(ArenaTagType.TRICK_ROOM)) ); if (prependPhase) { - globalScene.phaseQueue.splice( - globalScene.phaseQueue.indexOf(prependPhase), + globalScene.phaseManager.phaseQueue.splice( + globalScene.phaseManager.phaseQueue.indexOf(prependPhase), 0, new MovePhase(target, [ ...targetMovePhase.targets ], targetMovePhase.move, false, false, false, true) ); @@ -7920,7 +7920,7 @@ const failIfDampCondition: MoveConditionFunc = (user, target, move) => { globalScene.getField(true).map(p=>applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled)); // Queue a message if an ability prevented usage of the move if (cancelled.value) { - globalScene.queueMessage(i18next.t("moveTriggers:cannotUseMove", { pokemonName: getPokemonNameWithAffix(user), moveName: move.name })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cannotUseMove", { pokemonName: getPokemonNameWithAffix(user), moveName: move.name })); } return !cancelled.value; }; @@ -7929,7 +7929,7 @@ const userSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target: const targetSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => target.status?.effect === StatusEffect.SLEEP || target.hasAbility(AbilityId.COMATOSE); -const failIfLastCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => globalScene.phaseQueue.find(phase => phase.is("MovePhase")) !== undefined; +const failIfLastCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => globalScene.phaseManager.phaseQueue.find(phase => phase.is("MovePhase")) !== undefined; const failIfLastInPartyCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => { const party: Pokemon[] = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); @@ -8107,7 +8107,7 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr { } const type = validTypes[user.randBattleSeedInt(validTypes.length)]; user.summonData.types = [ type ]; - globalScene.queueMessage(i18next.t("battle:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), type: toReadableString(PokemonType[type]) })); + globalScene.phaseManager.queueMessage(i18next.t("battle:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), type: toReadableString(PokemonType[type]) })); user.updateInfo(); return true; @@ -8166,7 +8166,7 @@ export class ExposedMoveAttr extends AddBattlerTagAttr { return false; } - globalScene.queueMessage(i18next.t("moveTriggers:exposedMove", { pokemonName: getPokemonNameWithAffix(user), targetPokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:exposedMove", { pokemonName: getPokemonNameWithAffix(user), targetPokemonName: getPokemonNameWithAffix(target) })); return true; } @@ -8802,7 +8802,7 @@ export function initMoves() { .reflectable(), new SelfStatusMove(MoveId.BELLY_DRUM, PokemonType.NORMAL, -1, 10, -1, 0, 2) .attr(CutHpStatStageBoostAttr, [ Stat.ATK ], 12, 2, (user) => { - globalScene.queueMessage(i18next.t("moveTriggers:cutOwnHpAndMaximizedStat", { pokemonName: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.ATK)) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cutOwnHpAndMaximizedStat", { pokemonName: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.ATK)) })); }), new AttackMove(MoveId.SLUDGE_BOMB, PokemonType.POISON, MoveCategory.SPECIAL, 90, 100, 10, 30, 0, 2) .attr(StatusEffectAttr, StatusEffect.POISON) @@ -10370,7 +10370,7 @@ export function initMoves() { .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) .attr(AddBattlerTagAttr, BattlerTagType.BURNED_UP, true, false) .attr(RemoveTypeAttr, PokemonType.FIRE, (user) => { - globalScene.queueMessage(i18next.t("moveTriggers:burnedItselfOut", { pokemonName: getPokemonNameWithAffix(user) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:burnedItselfOut", { pokemonName: getPokemonNameWithAffix(user) })); }), new StatusMove(MoveId.SPEED_SWAP, PokemonType.PSYCHIC, -1, 10, -1, 0, 7) .attr(SwapStatAttr, Stat.SPD) @@ -11154,7 +11154,7 @@ export function initMoves() { }) .attr(AddBattlerTagAttr, BattlerTagType.DOUBLE_SHOCKED, true, false) .attr(RemoveTypeAttr, PokemonType.ELECTRIC, (user) => { - globalScene.queueMessage(i18next.t("moveTriggers:usedUpAllElectricity", { pokemonName: getPokemonNameWithAffix(user) })); + globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:usedUpAllElectricity", { pokemonName: getPokemonNameWithAffix(user) })); }), new AttackMove(MoveId.GIGATON_HAMMER, PokemonType.STEEL, MoveCategory.PHYSICAL, 160, 100, 5, -1, 0, 9) .makesContact(false) diff --git a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts index ae5cd2b2a99..5f69090064f 100644 --- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts +++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts @@ -182,7 +182,7 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder. async () => { const encounter = globalScene.currentBattle.mysteryEncounter!; // Full heal party - globalScene.unshiftPhase(new PartyHealPhase(true)); + globalScene.phaseManager.unshiftPhase(new PartyHealPhase(true)); const eggOptions: IEggOptions = { pulled: false, diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index b14caa4e2c3..e23b5024599 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -237,7 +237,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { queueEncounterMessage(`${namespace}:option.1.boss_enraged`); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1), ); }, diff --git a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts index 2f4dfaa5f99..87cd8f207c4 100644 --- a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts +++ b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts @@ -137,7 +137,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB }) .withOptionPhase(async () => { // Give the player a Shiny Charm - globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.SHINY_CHARM)); + globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.SHINY_CHARM)); leaveEncounterWithoutBattle(true); }) .build(), diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index 7f54e51565e..3de16c7086e 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -237,7 +237,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder. config.pokemonConfigs![0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON]; config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { queueEncounterMessage(`${namespace}:option.2.boss_enraged`); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1), ); }; diff --git a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts index c080122f922..cd8289163eb 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -766,7 +766,7 @@ function doBugTypeMoveTutor(): Promise { // Option select complete, handle if they are learning a move if (result && result.selectedOptionIndex < moveOptions.length) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new LearnMovePhase(result.selectedPokemonIndex, moveOptions[result.selectedOptionIndex].moveId), ); } diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index c7f9a99569d..66cd1ae0509 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -176,7 +176,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { queueEncounterMessage(`${namespace}:option.1.boss_enraged`); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase( pokemon.getBattlerIndex(), true, @@ -245,7 +245,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder const onPokemonSelected = (pokemon: PlayerPokemon) => { encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender()); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new LearnMovePhase(globalScene.getPlayerParty().indexOf(pokemon), MoveId.REVELATION_DANCE), ); diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index c54cb232087..1385e9c5a75 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -165,7 +165,7 @@ export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder.withE .withOptionPhase(async () => { // Give the player 5 Rogue Balls const encounter = globalScene.currentBattle.mysteryEncounter!; - globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.ROGUE_BALL)); + globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.ROGUE_BALL)); // Start encounter with random legendary (7-10 starter strength) that has level additive // If this is a mono-type challenge, always ensure the required type is filtered for diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts index 8d3d30bcd66..c321b5f9674 100644 --- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts +++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts @@ -65,10 +65,10 @@ const doEventReward = () => { return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount()); }); if (candidates.length > 0) { - globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes[randSeedItem(candidates)])); + globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes[randSeedItem(candidates)])); } else { // At max stacks, give a Voucher instead - globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.VOUCHER)); + globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.VOUCHER)); } } }; @@ -181,7 +181,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with ); doEventReward(); } else { - globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.AMULET_COIN)); + globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.AMULET_COIN)); doEventReward(); } @@ -266,7 +266,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with ); doEventReward(); } else { - globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.CANDY_JAR)); + globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.CANDY_JAR)); doEventReward(); } } else { @@ -288,7 +288,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with ); doEventReward(); } else { - globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.BERRY_POUCH)); + globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.BERRY_POUCH)); doEventReward(); } } @@ -372,7 +372,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with ); doEventReward(); } else { - globalScene.unshiftPhase(new ModifierRewardPhase(modifierTypes.HEALING_CHARM)); + globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.HEALING_CHARM)); doEventReward(); } diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 872e6300a29..ced04ce224d 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -92,7 +92,7 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w gender: Gender.MALE, tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPDEF, Stat.SPD], 1), ); }, @@ -103,7 +103,7 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w gender: Gender.FEMALE, tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPDEF, Stat.SPD], 1), ); }, diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index ecc2e17a06f..6df8ca8b167 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -76,7 +76,9 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder. queueEncounterMessage(`${namespace}:option.1.stat_boost`); // Randomly boost 1 stat 2 stages // Cannot boost Spd, Acc, or Evasion - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [randSeedInt(4, 1)], 2)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), true, [randSeedInt(4, 1)], 2), + ); }, }, ], diff --git a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts index 7694f62eac4..42bfe76d98e 100644 --- a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts +++ b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts @@ -411,13 +411,13 @@ function summonPlayerPokemonAnimation(pokemon: PlayerPokemon): Promise { pokemon.resetSummonData(); globalScene.time.delayedCall(1000, () => { if (pokemon.isShiny()) { - globalScene.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex())); + globalScene.phaseManager.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex())); } pokemon.resetTurnData(); globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true); - globalScene.pushPhase(new PostSummonPhase(pokemon.getBattlerIndex())); + globalScene.phaseManager.pushPhase(new PostSummonPhase(pokemon.getBattlerIndex())); resolve(); }); }, diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 9f699f7d045..42167d240f9 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -189,8 +189,8 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde const allowedPokemon = globalScene.getPokemonAllowedInBattle(); if (allowedPokemon.length === 0) { // If there are no longer any legal pokemon in the party, game over. - globalScene.clearPhaseQueue(); - globalScene.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.unshiftPhase(new GameOverPhase()); } else { // Show which Pokemon was KOed, then start battle against Gimmighoul await transitionMysteryEncounterIntroVisuals(true, true, 500); diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 7a12c86edff..54e15dcb102 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -276,7 +276,7 @@ async function summonSafariPokemon() { const encounter = globalScene.currentBattle.mysteryEncounter!; // Message pokemon remaining encounter.setDialogueToken("remainingCount", encounter.misc.safariPokemonRemaining); - globalScene.queueMessage(getEncounterText(`${namespace}:safari.remaining_count`) ?? "", null, true); + globalScene.phaseManager.queueMessage(getEncounterText(`${namespace}:safari.remaining_count`) ?? "", null, true); // Generate pokemon using safariPokemonRemaining so they are always the same pokemon no matter how many turns are taken // Safari pokemon roll twice on shiny and HA chances, but are otherwise normal @@ -325,7 +325,7 @@ async function summonSafariPokemon() { encounter.misc.pokemon = pokemon; encounter.misc.safariPokemonRemaining -= 1; - globalScene.unshiftPhase(new SummonPhase(0, false)); + globalScene.phaseManager.unshiftPhase(new SummonPhase(0, false)); encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon)); @@ -336,7 +336,7 @@ async function summonSafariPokemon() { const ivScannerModifier = globalScene.findModifier(m => m instanceof IvScannerModifier); if (ivScannerModifier) { - globalScene.pushPhase(new ScanIvsPhase(pokemon.getBattlerIndex())); + globalScene.phaseManager.pushPhase(new ScanIvsPhase(pokemon.getBattlerIndex())); } } @@ -559,7 +559,7 @@ async function doEndTurn(cursorIndex: number) { leaveEncounterWithoutBattle(true); } } else { - globalScene.queueMessage(getEncounterText(`${namespace}:safari.watching`) ?? "", 0, null, 1000); + globalScene.phaseManager.queueMessage(getEncounterText(`${namespace}:safari.watching`) ?? "", 0, null, 1000); initSubsequentOptionSelect({ overrideOptions: safariZoneGameOptions, startingCursorIndex: cursorIndex, diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index 196d27c3f30..a0898f7cfec 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -155,7 +155,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil async () => { // Fall asleep waiting for Snorlax // Full heal party - globalScene.unshiftPhase(new PartyHealPhase(true)); + globalScene.phaseManager.unshiftPhase(new PartyHealPhase(true)); queueEncounterMessage(`${namespace}:option.2.rest_result`); leaveEncounterWithoutBattle(); }, diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index f6bf5575120..b101837adb9 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -227,7 +227,9 @@ async function doBiomeTransitionDialogueAndBattleInit() { tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { queueEncounterMessage(`${namespace}:boss_enraged`); - globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1), + ); }, }, ], diff --git a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts index 5c3a4dd1a81..fdfcc8f2f13 100644 --- a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts @@ -658,8 +658,8 @@ function onGameOver() { globalScene.playBgm(globalScene.arena.bgm); // Clear any leftover battle phases - globalScene.clearPhaseQueue(); - globalScene.clearPhaseQueueSplice(); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.clearPhaseQueueSplice(); // Return enemy Pokemon const pokemon = globalScene.getEnemyPokemon(); diff --git a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts index 33f43617913..e2b6d62745b 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -116,7 +116,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { queueEncounterMessage(`${namespace}:option.2.stat_boost`); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.DEF, Stat.SPDEF], 1), ); }, diff --git a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts index 0776d89ed63..05e036e9189 100644 --- a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts @@ -143,7 +143,7 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter = MysteryEncounter }, async () => { // Refuse the challenge, they full heal the party and give the player a Rarer Candy - globalScene.unshiftPhase(new PartyHealPhase(true)); + globalScene.phaseManager.unshiftPhase(new PartyHealPhase(true)); setEncounterRewards({ guaranteedModifierTypeFuncs: [modifierTypes.RARER_CANDY], fillRemaining: false, @@ -209,7 +209,7 @@ function endTrainerBattleAndShowDialogue(): Promise { for (const pokemon of playerField) { pokemon.lapseTag(BattlerTagType.COMMANDED); } - playerField.forEach((_, p) => globalScene.unshiftPhase(new ReturnPhase(p))); + playerField.forEach((_, p) => globalScene.phaseManager.unshiftPhase(new ReturnPhase(p))); for (const pokemon of globalScene.getPlayerParty()) { // Only trigger form change when Eiscue is in Noice form @@ -227,7 +227,7 @@ function endTrainerBattleAndShowDialogue(): Promise { applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon); } - globalScene.unshiftPhase(new ShowTrainerPhase()); + globalScene.phaseManager.unshiftPhase(new ShowTrainerPhase()); // Hide the trainer and init next battle const trainer = globalScene.currentBattle.trainer; // Unassign previous trainer from battle so it isn't destroyed before animation completes diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index e51a8554120..e917574a065 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -103,7 +103,7 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder. tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { queueEncounterMessage(`${namespace}:option.1.stat_boost`); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1), ); }, diff --git a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts index 296d94093d9..e1055f57496 100644 --- a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts @@ -51,7 +51,7 @@ function getTextWithDialogueTokens(keyOrString: string): string | null { */ export function queueEncounterMessage(contentKey: string): void { const text: string | null = getEncounterText(contentKey); - globalScene.queueMessage(text ?? "", null, true); + globalScene.phaseManager.queueMessage(text ?? "", null, true); } /** diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 5736835d98a..c566ed68476 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -428,7 +428,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig): console.log("Moveset:", moveset); }); - globalScene.pushPhase(new MysteryEncounterBattlePhase(partyConfig.disableSwitch)); + globalScene.phaseManager.pushPhase(new MysteryEncounterBattlePhase(partyConfig.disableSwitch)); await Promise.all(loadEnemyAssets); battle.enemyParty.forEach((enemyPokemon_2, e_1) => { @@ -480,7 +480,7 @@ export function updatePlayerMoney(changeValue: number, playSound = true, showMes } if (showMessage) { if (changeValue < 0) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("mysteryEncounterMessages:paid_money", { amount: -changeValue, }), @@ -488,7 +488,7 @@ export function updatePlayerMoney(changeValue: number, playSound = true, showMes true, ); } else { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("mysteryEncounterMessages:receive_money", { amount: changeValue, }), @@ -767,9 +767,9 @@ export function setEncounterRewards( } if (customShopRewards) { - globalScene.unshiftPhase(new SelectModifierPhase(0, undefined, customShopRewards)); + globalScene.phaseManager.unshiftPhase(new SelectModifierPhase(0, undefined, customShopRewards)); } else { - globalScene.tryRemovePhase(p => p.is("MysteryEncounterRewardsPhase")); + globalScene.phaseManager.tryRemovePhase(p => p.is("MysteryEncounterRewardsPhase")); } if (eggRewards) { @@ -807,7 +807,7 @@ export function setEncounterExp(participantId: number | number[], baseExpValue: const participantIds = Array.isArray(participantId) ? participantId : [participantId]; globalScene.currentBattle.mysteryEncounter!.doEncounterExp = () => { - globalScene.unshiftPhase(new PartyExpPhase(baseExpValue, useWaveIndex, new Set(participantIds))); + globalScene.phaseManager.unshiftPhase(new PartyExpPhase(baseExpValue, useWaveIndex, new Set(participantIds))); return true; }; @@ -829,7 +829,7 @@ export class OptionSelectSettings { * @param optionSelectSettings */ export function initSubsequentOptionSelect(optionSelectSettings: OptionSelectSettings) { - globalScene.pushPhase(new MysteryEncounterPhase(optionSelectSettings)); + globalScene.phaseManager.pushPhase(new MysteryEncounterPhase(optionSelectSettings)); } /** @@ -843,8 +843,8 @@ export function leaveEncounterWithoutBattle( encounterMode: MysteryEncounterMode = MysteryEncounterMode.NO_BATTLE, ) { globalScene.currentBattle.mysteryEncounter!.encounterMode = encounterMode; - globalScene.clearPhaseQueue(); - globalScene.clearPhaseQueueSplice(); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.clearPhaseQueueSplice(); handleMysteryEncounterVictory(addHealPhase); } @@ -857,8 +857,8 @@ export function handleMysteryEncounterVictory(addHealPhase = false, doNotContinu const allowedPkm = globalScene.getPlayerParty().filter(pkm => pkm.isAllowedInBattle()); if (allowedPkm.length === 0) { - globalScene.clearPhaseQueue(); - globalScene.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.unshiftPhase(new GameOverPhase()); return; } @@ -869,8 +869,8 @@ export function handleMysteryEncounterVictory(addHealPhase = false, doNotContinu return; } if (encounter.encounterMode === MysteryEncounterMode.NO_BATTLE) { - globalScene.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase)); - globalScene.pushPhase(new EggLapsePhase()); + globalScene.phaseManager.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase)); + globalScene.phaseManager.pushPhase(new EggLapsePhase()); } else if ( !globalScene .getEnemyParty() @@ -878,15 +878,15 @@ export function handleMysteryEncounterVictory(addHealPhase = false, doNotContinu encounter.encounterMode !== MysteryEncounterMode.TRAINER_BATTLE ? p.isOnField() : !p?.isFainted(true), ) ) { - globalScene.pushPhase(new BattleEndPhase(true)); + globalScene.phaseManager.pushPhase(new BattleEndPhase(true)); if (encounter.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) { - globalScene.pushPhase(new TrainerVictoryPhase()); + globalScene.phaseManager.pushPhase(new TrainerVictoryPhase()); } if (globalScene.gameMode.isEndless || !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) { - globalScene.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase)); + globalScene.phaseManager.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase)); if (!encounter.doContinueEncounter) { // Only lapse eggs once for multi-battle encounters - globalScene.pushPhase(new EggLapsePhase()); + globalScene.phaseManager.pushPhase(new EggLapsePhase()); } } } @@ -900,8 +900,8 @@ export function handleMysteryEncounterBattleFailed(addHealPhase = false, doNotCo const allowedPkm = globalScene.getPlayerParty().filter(pkm => pkm.isAllowedInBattle()); if (allowedPkm.length === 0) { - globalScene.clearPhaseQueue(); - globalScene.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.unshiftPhase(new GameOverPhase()); return; } @@ -912,14 +912,14 @@ export function handleMysteryEncounterBattleFailed(addHealPhase = false, doNotCo return; } if (encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE) { - globalScene.pushPhase(new BattleEndPhase(false)); + globalScene.phaseManager.pushPhase(new BattleEndPhase(false)); } - globalScene.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase)); + globalScene.phaseManager.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase)); if (!encounter.doContinueEncounter) { // Only lapse eggs once for multi-battle encounters - globalScene.pushPhase(new EggLapsePhase()); + globalScene.phaseManager.pushPhase(new EggLapsePhase()); } } @@ -1004,12 +1004,14 @@ export function handleMysteryEncounterBattleStartEffects() { } else { source = globalScene.getEnemyField()[0]; } - // @ts-ignore: source cannot be undefined - globalScene.pushPhase(new MovePhase(source, effect.targets, effect.move, effect.followUp, effect.ignorePp)); + globalScene.phaseManager.pushPhase( + // @ts-ignore: source cannot be undefined + new MovePhase(source, effect.targets, effect.move, effect.followUp, effect.ignorePp), + ); }); // Pseudo turn end phase to reset flinch states, Endure, etc. - globalScene.pushPhase(new MysteryEncounterBattleStartCleanupPhase()); + globalScene.phaseManager.pushPhase(new MysteryEncounterBattleStartCleanupPhase()); encounter.startOfBattleEffectsComplete = true; } diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index d4102c045c0..757d8173820 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -675,7 +675,7 @@ export async function catchPokemon( if (!globalScene.getEnemyParty().some(p => p.id === pokemon.id)) { globalScene.getEnemyParty().push(pokemon); } - globalScene.unshiftPhase(new VictoryPhase(pokemon.id, true)); + globalScene.phaseManager.unshiftPhase(new VictoryPhase(pokemon.id, true)); globalScene.pokemonInfoContainer.hide(); if (pokeball) { removePb(pokeball); diff --git a/src/field/arena.ts b/src/field/arena.ts index 2ec98c53afa..1c54382c89b 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -297,8 +297,8 @@ export class Arena { */ trySetWeatherOverride(weather: WeatherType): boolean { this.weather = new Weather(weather, 0); - globalScene.unshiftPhase(new CommonAnimPhase(undefined, undefined, CommonAnim.SUNNY + (weather - 1))); - globalScene.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct? + globalScene.phaseManager.unshiftPhase(new CommonAnimPhase(undefined, undefined, CommonAnim.SUNNY + (weather - 1))); + globalScene.phaseManager.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct? return true; } @@ -328,10 +328,10 @@ export class Arena { this.weather?.isImmutable() && ![WeatherType.HARSH_SUN, WeatherType.HEAVY_RAIN, WeatherType.STRONG_WINDS, WeatherType.NONE].includes(weather) ) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new CommonAnimPhase(undefined, undefined, CommonAnim.SUNNY + (oldWeatherType - 1), true), ); - globalScene.queueMessage(getLegendaryWeatherContinuesMessage(oldWeatherType)!); + globalScene.phaseManager.queueMessage(getLegendaryWeatherContinuesMessage(oldWeatherType)!); return false; } @@ -348,10 +348,12 @@ export class Arena { ); // TODO: is this bang correct? if (this.weather) { - globalScene.unshiftPhase(new CommonAnimPhase(undefined, undefined, CommonAnim.SUNNY + (weather - 1), true)); - globalScene.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct? + globalScene.phaseManager.unshiftPhase( + new CommonAnimPhase(undefined, undefined, CommonAnim.SUNNY + (weather - 1), true), + ); + globalScene.phaseManager.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct? } else { - globalScene.queueMessage(getWeatherClearMessage(oldWeatherType)!); // TODO: is this bang correct? + globalScene.phaseManager.queueMessage(getWeatherClearMessage(oldWeatherType)!); // TODO: is this bang correct? } globalScene @@ -431,11 +433,13 @@ export class Arena { if (this.terrain) { if (!ignoreAnim) { - globalScene.unshiftPhase(new CommonAnimPhase(undefined, undefined, CommonAnim.MISTY_TERRAIN + (terrain - 1))); + globalScene.phaseManager.unshiftPhase( + new CommonAnimPhase(undefined, undefined, CommonAnim.MISTY_TERRAIN + (terrain - 1)), + ); } - globalScene.queueMessage(getTerrainStartMessage(terrain)!); // TODO: is this bang correct? + globalScene.phaseManager.queueMessage(getTerrainStartMessage(terrain)!); // TODO: is this bang correct? } else { - globalScene.queueMessage(getTerrainClearMessage(oldTerrainType)!); // TODO: is this bang correct? + globalScene.phaseManager.queueMessage(getTerrainClearMessage(oldTerrainType)!); // TODO: is this bang correct? } globalScene diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index d26dfd193f9..a6d41074700 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1298,7 +1298,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } // During the Pokemon's MoveEffect phase, the offset is removed to put the Pokemon "in focus" - const currentPhase = globalScene.getCurrentPhase(); + const currentPhase = globalScene.phaseManager.getCurrentPhase(); return !(currentPhase?.is("MoveEffectPhase") && currentPhase.getPokemon() === this); } @@ -2537,7 +2537,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ) { multiplier /= 2; if (!simulated) { - globalScene.queueMessage(i18next.t("weather:strongWindsEffectMessage")); + globalScene.phaseManager.queueMessage(i18next.t("weather:strongWindsEffectMessage")); } } return multiplier as TypeDamageMultiplier; @@ -4011,8 +4011,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * * Once the MoveEffectPhase is over (and calls it's .end() function, shiftPhase() will reset the PhaseQueueSplice via clearPhaseQueueSplice() ) */ - globalScene.setPhaseQueueSplice(); - globalScene.unshiftPhase(new FaintPhase(this.getBattlerIndex(), preventEndure)); + globalScene.phaseManager.setPhaseQueueSplice(); + globalScene.phaseManager.unshiftPhase(new FaintPhase(this.getBattlerIndex(), preventEndure)); this.destroySubstitute(); this.lapseTag(BattlerTagType.COMMANDED); } @@ -4049,7 +4049,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ): number { const isIndirectDamage = [HitResult.INDIRECT, HitResult.INDIRECT_KO].includes(result); const damagePhase = new DamageAnimPhase(this.getBattlerIndex(), damage, result as DamageResult, isCritical); - globalScene.unshiftPhase(damagePhase); + globalScene.phaseManager.unshiftPhase(damagePhase); if (this.switchOutStatus && source) { damage = 0; } @@ -4615,7 +4615,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { : i18next.t("abilityTriggers:moveImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(this), }); - globalScene.queueMessage(message); + globalScene.phaseManager.queueMessage(message); } /** @@ -4735,7 +4735,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (sourcePokemon && sourcePokemon !== this && this.isSafeguarded(sourcePokemon)) { if (!quiet) { - globalScene.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(this) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(this) }), + ); } return false; } @@ -4764,7 +4766,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * cancel the attack's subsequent hits. */ if (effect === StatusEffect.SLEEP || effect === StatusEffect.FREEZE) { - const currentPhase = globalScene.getCurrentPhase(); + const currentPhase = globalScene.phaseManager.getCurrentPhase(); if (currentPhase?.is("MoveEffectPhase") && currentPhase.getUserPokemon() === this) { this.turnData.hitCount = 1; this.turnData.hitsLeft = 1; @@ -4775,7 +4777,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (overrideStatus) { this.resetStatus(false); } - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new ObtainStatusEffectPhase(this.getBattlerIndex(), effect, turnsRemaining, sourceText, sourcePokemon), ); return true; @@ -4825,7 +4827,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (asPhase) { - globalScene.unshiftPhase(new ResetStatusPhase(this, confusion, reloadAssets)); + globalScene.phaseManager.unshiftPhase(new ResetStatusPhase(this, confusion, reloadAssets)); } else { this.clearStatus(confusion, reloadAssets); } @@ -5632,7 +5634,7 @@ export class PlayerPokemon extends Pokemon { this.getFieldIndex(), (slotIndex: number, _option: PartyOption) => { if (slotIndex >= globalScene.currentBattle.getBattlerCount() && slotIndex < 6) { - globalScene.prependToPhase( + globalScene.phaseManager.prependToPhase( new SwitchSummonPhase(switchType, this.getFieldIndex(), slotIndex, false), MoveEndPhase, ); @@ -5997,7 +5999,9 @@ export class PlayerPokemon extends Pokemon { const newPartyMemberIndex = globalScene.getPlayerParty().indexOf(this); pokemon .getMoveset(true) - .map((m: PokemonMove) => globalScene.unshiftPhase(new LearnMovePhase(newPartyMemberIndex, m.getMove().id))); + .map((m: PokemonMove) => + globalScene.phaseManager.unshiftPhase(new LearnMovePhase(newPartyMemberIndex, m.getMove().id)), + ); pokemon.destroy(); this.updateFusionPalette(); } @@ -6639,7 +6643,7 @@ export class EnemyPokemon extends Pokemon { stages++; } - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase(this.getBattlerIndex(), true, [boostedStat!], stages, true, true), ); this.bossSegmentIndex--; diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index f8c71b2c891..e1517b3bcde 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1548,7 +1548,7 @@ export class SurviveDamageModifier extends PokemonHeldItemModifier { if (!surviveDamage.value && pokemon.randBattleSeedInt(10) < this.getStackCount()) { surviveDamage.value = true; - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("modifier:surviveDamageApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name, @@ -1598,7 +1598,7 @@ export class BypassSpeedChanceModifier extends PokemonHeldItemModifier { const hasQuickClaw = this.type instanceof PokemonHeldItemModifierType && this.type.id === "QUICK_CLAW"; if (isCommandFight && hasQuickClaw) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("modifier:bypassSpeedChanceApply", { pokemonName: getPokemonNameWithAffix(pokemon), itemName: i18next.t("modifierType:ModifierType.QUICK_CLAW.name"), @@ -1684,7 +1684,7 @@ export class TurnHealModifier extends PokemonHeldItemModifier { */ override apply(pokemon: Pokemon): boolean { if (!pokemon.isFullHp()) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new PokemonHealPhase( pokemon.getBattlerIndex(), toDmgValue(pokemon.getMaxHp() / 16) * this.stackCount, @@ -1782,7 +1782,7 @@ export class HitHealModifier extends PokemonHeldItemModifier { override apply(pokemon: Pokemon): boolean { if (pokemon.turnData.totalDamageDealt && !pokemon.isFullHp()) { // TODO: this shouldn't be undefined AFAIK - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new PokemonHealPhase( pokemon.getBattlerIndex(), toDmgValue(pokemon.turnData.totalDamageDealt / 8) * this.stackCount, @@ -1950,7 +1950,7 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier { */ override apply(pokemon: Pokemon): boolean { // Restore the Pokemon to half HP - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new PokemonHealPhase( pokemon.getBattlerIndex(), toDmgValue(pokemon.getMaxHp() / 2), @@ -2012,7 +2012,7 @@ export class ResetNegativeStatStageModifier extends PokemonHeldItemModifier { } if (statRestored) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("modifier:resetNegativeStatStageApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name, @@ -2323,7 +2323,7 @@ export class PokemonLevelIncrementModifier extends ConsumablePokemonModifier { playerPokemon.addFriendship(FRIENDSHIP_GAIN_FROM_RARE_CANDY); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new LevelUpPhase( globalScene.getPlayerParty().indexOf(playerPokemon), playerPokemon.level - levelCount.value, @@ -2344,7 +2344,7 @@ export class TmModifier extends ConsumablePokemonModifier { * @returns always `true` */ override apply(playerPokemon: PlayerPokemon): boolean { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new LearnMovePhase(globalScene.getPlayerParty().indexOf(playerPokemon), this.type.moveId, LearnMoveType.TM), ); @@ -2367,7 +2367,7 @@ export class RememberMoveModifier extends ConsumablePokemonModifier { * @returns always `true` */ override apply(playerPokemon: PlayerPokemon, cost?: number): boolean { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new LearnMovePhase( globalScene.getPlayerParty().indexOf(playerPokemon), playerPokemon.getLearnableLevelMoves()[this.levelMoveIndex], @@ -2410,7 +2410,9 @@ export class EvolutionItemModifier extends ConsumablePokemonModifier { } if (matchingEvolution) { - globalScene.unshiftPhase(new EvolutionPhase(playerPokemon, matchingEvolution, playerPokemon.level - 1)); + globalScene.phaseManager.unshiftPhase( + new EvolutionPhase(playerPokemon, matchingEvolution, playerPokemon.level - 1), + ); return true; } @@ -3008,7 +3010,7 @@ export class MoneyInterestModifier extends PersistentModifier { moneyAmount: formattedMoneyAmount, typeName: this.type.name, }); - globalScene.queueMessage(message, undefined, true); + globalScene.phaseManager.queueMessage(message, undefined, true); return true; } @@ -3262,7 +3264,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier { } for (const mt of transferredModifierTypes) { - globalScene.queueMessage(this.getTransferMessage(pokemon, targetPokemon, mt)); + globalScene.phaseManager.queueMessage(this.getTransferMessage(pokemon, targetPokemon, mt)); } return !!transferredModifierTypes.length; @@ -3572,7 +3574,7 @@ export class EnemyTurnHealModifier extends EnemyPersistentModifier { */ override apply(enemyPokemon: Pokemon): boolean { if (!enemyPokemon.isFullHp()) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new PokemonHealPhase( enemyPokemon.getBattlerIndex(), Math.max(Math.floor(enemyPokemon.getMaxHp() / (100 / this.healPercent)) * this.stackCount, 1), @@ -3668,7 +3670,7 @@ export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifier */ override apply(enemyPokemon: Pokemon): boolean { if (enemyPokemon.status && Phaser.Math.RND.realInRange(0, 1) < this.chance * this.getStackCount()) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( getStatusEffectHealText(enemyPokemon.status.effect, getPokemonNameWithAffix(enemyPokemon)), ); enemyPokemon.resetStatus(); diff --git a/src/phase-manager.ts b/src/phase-manager.ts new file mode 100644 index 00000000000..8869f4b27b7 --- /dev/null +++ b/src/phase-manager.ts @@ -0,0 +1,311 @@ +import { HideAbilityPhase } from "./phases/hide-ability-phase"; +import { ShowAbilityPhase } from "./phases/show-ability-phase"; +import { TurnInitPhase } from "./phases/turn-init-phase"; +import type { Phase } from "#app/phase"; +import type { default as Pokemon } from "#app/field/pokemon"; +import type { Constructor } from "#app/utils/common"; +import { MessagePhase } from "./phases/message-phase"; +import { globalScene } from "#app/global-scene"; + +/** + * Manager for phases used by battle scene. + * + * *This file must not be imported or used directly. The manager is exclusively used by the battle scene and is not intended for external use.* + */ + +export class PhaseManager { + /** PhaseQueue: dequeue/remove the first element to get the next phase */ + public phaseQueue: Phase[] = []; + public conditionalQueue: Array<[() => boolean, Phase]> = []; + /** PhaseQueuePrepend: is a temp storage of what will be added to PhaseQueue */ + private phaseQueuePrepend: Phase[] = []; + + /** overrides default of inserting phases to end of phaseQueuePrepend array. Useful for inserting Phases "out of order" */ + private phaseQueuePrependSpliceIndex = -1; + private nextCommandPhaseQueue: Phase[] = []; + + private currentPhase: Phase | null = null; + private standbyPhase: Phase | null = null; + + /* Phase Functions */ + getCurrentPhase(): Phase | null { + return this.currentPhase; + } + + getStandbyPhase(): Phase | null { + return this.standbyPhase; + } + + /** + * Adds a phase to the conditional queue and ensures it is executed only when the specified condition is met. + * + * This method allows deferring the execution of a phase until certain conditions are met, which is useful for handling + * situations like abilities and entry hazards that depend on specific game states. + * + * @param phase - The phase to be added to the conditional queue. + * @param condition - A function that returns a boolean indicating whether the phase should be executed. + * + */ + pushConditionalPhase(phase: Phase, condition: () => boolean): void { + this.conditionalQueue.push([condition, phase]); + } + + /** + * Adds a phase to nextCommandPhaseQueue, as long as boolean passed in is false + * @param phase {@linkcode Phase} the phase to add + * @param defer boolean on which queue to add to, defaults to false, and adds to phaseQueue + */ + pushPhase(phase: Phase, defer = false): void { + (!defer ? this.phaseQueue : this.nextCommandPhaseQueue).push(phase); + } + + /** + * Adds Phase(s) to the end of phaseQueuePrepend, or at phaseQueuePrependSpliceIndex + * @param phases {@linkcode Phase} the phase(s) to add + */ + unshiftPhase(...phases: Phase[]): void { + if (this.phaseQueuePrependSpliceIndex === -1) { + this.phaseQueuePrepend.push(...phases); + } else { + this.phaseQueuePrepend.splice(this.phaseQueuePrependSpliceIndex, 0, ...phases); + } + } + + /** + * Clears the phaseQueue + */ + clearPhaseQueue(): void { + this.phaseQueue.splice(0, this.phaseQueue.length); + } + + /** + * Clears all phase-related stuff, including all phase queues, the current and standby phases, and a splice index + */ + clearAllPhases(): void { + for (const queue of [this.phaseQueue, this.phaseQueuePrepend, this.conditionalQueue, this.nextCommandPhaseQueue]) { + queue.splice(0, queue.length); + } + this.currentPhase = null; + this.standbyPhase = null; + this.clearPhaseQueueSplice(); + } + + /** + * Used by function unshiftPhase(), sets index to start inserting at current length instead of the end of the array, useful if phaseQueuePrepend gets longer with Phases + */ + setPhaseQueueSplice(): void { + this.phaseQueuePrependSpliceIndex = this.phaseQueuePrepend.length; + } + + /** + * Resets phaseQueuePrependSpliceIndex to -1, implies that calls to unshiftPhase will insert at end of phaseQueuePrepend + */ + clearPhaseQueueSplice(): void { + this.phaseQueuePrependSpliceIndex = -1; + } + + /** + * Is called by each Phase implementations "end()" by default + * We dump everything from phaseQueuePrepend to the start of of phaseQueue + * then removes first Phase and starts it + */ + shiftPhase(): void { + if (this.standbyPhase) { + this.currentPhase = this.standbyPhase; + this.standbyPhase = null; + return; + } + + if (this.phaseQueuePrependSpliceIndex > -1) { + this.clearPhaseQueueSplice(); + } + if (this.phaseQueuePrepend.length) { + while (this.phaseQueuePrepend.length) { + const poppedPhase = this.phaseQueuePrepend.pop(); + if (poppedPhase) { + this.phaseQueue.unshift(poppedPhase); + } + } + } + if (!this.phaseQueue.length) { + this.populatePhaseQueue(); + // Clear the conditionalQueue if there are no phases left in the phaseQueue + this.conditionalQueue = []; + } + + this.currentPhase = this.phaseQueue.shift() ?? null; + + // Check if there are any conditional phases queued + if (this.conditionalQueue?.length) { + // Retrieve the first conditional phase from the queue + const conditionalPhase = this.conditionalQueue.shift(); + // Evaluate the condition associated with the phase + if (conditionalPhase?.[0]()) { + // If the condition is met, add the phase to the phase queue + this.pushPhase(conditionalPhase[1]); + } else if (conditionalPhase) { + // If the condition is not met, re-add the phase back to the front of the conditional queue + this.conditionalQueue.unshift(conditionalPhase); + } else { + console.warn("condition phase is undefined/null!", conditionalPhase); + } + } + + if (this.currentPhase) { + console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;"); + this.currentPhase.start(); + } + } + + overridePhase(phase: Phase): boolean { + if (this.standbyPhase) { + return false; + } + + this.standbyPhase = this.currentPhase; + this.currentPhase = phase; + console.log(`%cStart Phase ${phase.constructor.name}`, "color:green;"); + phase.start(); + + return true; + } + + /** + * Find a specific {@linkcode Phase} in the phase queue. + * + * @param phaseFilter filter function to use to find the wanted phase + * @returns the found phase or undefined if none found + */ + findPhase

(phaseFilter: (phase: P) => boolean): P | undefined { + return this.phaseQueue.find(phaseFilter) as P; + } + + tryReplacePhase(phaseFilter: (phase: Phase) => boolean, phase: Phase): boolean { + const phaseIndex = this.phaseQueue.findIndex(phaseFilter); + if (phaseIndex > -1) { + this.phaseQueue[phaseIndex] = phase; + return true; + } + return false; + } + + tryRemovePhase(phaseFilter: (phase: Phase) => boolean): boolean { + const phaseIndex = this.phaseQueue.findIndex(phaseFilter); + if (phaseIndex > -1) { + this.phaseQueue.splice(phaseIndex, 1); + return true; + } + return false; + } + + /** + * Will search for a specific phase in {@linkcode phaseQueuePrepend} via filter, and remove the first result if a match is found. + * @param phaseFilter filter function + */ + tryRemoveUnshiftedPhase(phaseFilter: (phase: Phase) => boolean): boolean { + const phaseIndex = this.phaseQueuePrepend.findIndex(phaseFilter); + if (phaseIndex > -1) { + this.phaseQueuePrepend.splice(phaseIndex, 1); + return true; + } + return false; + } + + /** + * Tries to add the input phase to index before target phase in the phaseQueue, else simply calls unshiftPhase() + * @param phase {@linkcode Phase} the phase to be added + * @param targetPhase {@linkcode Phase} the type of phase to search for in phaseQueue + * @returns boolean if a targetPhase was found and added + */ + prependToPhase(phase: Phase | Phase[], targetPhase: Constructor): boolean { + if (!Array.isArray(phase)) { + phase = [phase]; + } + const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof targetPhase); + + if (targetIndex !== -1) { + this.phaseQueue.splice(targetIndex, 0, ...phase); + return true; + } + this.unshiftPhase(...phase); + return false; + } + + /** + * Attempt to add the input phase(s) to index after target phase in the {@linkcode phaseQueue}, else simply calls {@linkcode unshiftPhase()} + * @param phase - The phase(s) to be added + * @param targetPhase - The type of phase to search for in {@linkcode phaseQueue} + * @returns `true` if a `targetPhase` was found to append to + */ + appendToPhase(phase: Phase | Phase[], targetPhase: Constructor): boolean { + if (!Array.isArray(phase)) { + phase = [phase]; + } + const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof targetPhase); + + if (targetIndex !== -1 && this.phaseQueue.length > targetIndex) { + this.phaseQueue.splice(targetIndex + 1, 0, ...phase); + return true; + } + this.unshiftPhase(...phase); + return false; + } + + /** + * Adds a MessagePhase, either to PhaseQueuePrepend or nextCommandPhaseQueue + * @param message - string for MessagePhase + * @param callbackDelay - optional param for MessagePhase constructor + * @param prompt - optional param for MessagePhase constructor + * @param promptDelay - optional param for MessagePhase constructor + * @param defer - Whether to allow the phase to be deferred + * + * @see {@linkcode MessagePhase} for more details on the parameters + */ + queueMessage( + message: string, + callbackDelay?: number | null, + prompt?: boolean | null, + promptDelay?: number | null, + defer?: boolean | null, + ) { + const phase = new MessagePhase(message, callbackDelay, prompt, promptDelay); + if (!defer) { + // adds to the end of PhaseQueuePrepend + this.unshiftPhase(phase); + } else { + //remember that pushPhase adds it to nextCommandPhaseQueue + this.pushPhase(phase); + } + } + + /** + * Queues an ability bar flyout phase + * @param pokemon The pokemon who has the ability + * @param passive Whether the ability is a passive + * @param show Whether to show or hide the bar + */ + public queueAbilityDisplay(pokemon: Pokemon, passive: boolean, show: boolean): void { + this.unshiftPhase(show ? new ShowAbilityPhase(pokemon.getBattlerIndex(), passive) : new HideAbilityPhase()); + this.clearPhaseQueueSplice(); + } + + /** + * Hides the ability bar if it is currently visible + */ + public hideAbilityBar(): void { + if (globalScene.abilityBar.isVisible()) { + this.unshiftPhase(new HideAbilityPhase()); + } + } + + /** + * Moves everything from nextCommandPhaseQueue to phaseQueue (keeping order) + */ + private populatePhaseQueue(): void { + if (this.nextCommandPhaseQueue.length) { + this.phaseQueue.push(...this.nextCommandPhaseQueue); + this.nextCommandPhaseQueue.splice(0, this.nextCommandPhaseQueue.length); + } + this.phaseQueue.push(new TurnInitPhase()); + } +} diff --git a/src/phase.ts b/src/phase.ts index 9e2468ebdff..5e81679d29e 100644 --- a/src/phase.ts +++ b/src/phase.ts @@ -5,7 +5,7 @@ export abstract class Phase { start() {} end() { - globalScene.shiftPhase(); + globalScene.phaseManager.shiftPhase(); } /** diff --git a/src/phases/attempt-capture-phase.ts b/src/phases/attempt-capture-phase.ts index 6c2f5f4dd76..09eeb6e0a19 100644 --- a/src/phases/attempt-capture-phase.ts +++ b/src/phases/attempt-capture-phase.ts @@ -257,7 +257,7 @@ export class AttemptCapturePhase extends PokemonPhase { null, () => { const end = () => { - globalScene.unshiftPhase(new VictoryPhase(this.battlerIndex)); + globalScene.phaseManager.unshiftPhase(new VictoryPhase(this.battlerIndex)); globalScene.pokemonInfoContainer.hide(); this.removePb(); this.end(); diff --git a/src/phases/attempt-run-phase.ts b/src/phases/attempt-run-phase.ts index ced81567c43..2bf6ba2fb5b 100644 --- a/src/phases/attempt-run-phase.ts +++ b/src/phases/attempt-run-phase.ts @@ -39,7 +39,7 @@ export class AttemptRunPhase extends PokemonPhase { enemyField.forEach(enemyPokemon => applyPreLeaveFieldAbAttrs(PreLeaveFieldAbAttr, enemyPokemon)); globalScene.playSound("se/flee"); - globalScene.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500); + globalScene.phaseManager.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500); globalScene.tweens.add({ targets: [globalScene.arenaEnemy, enemyField].flat(), @@ -60,16 +60,16 @@ export class AttemptRunPhase extends PokemonPhase { enemyPokemon.trySetStatus(StatusEffect.FAINT); }); - globalScene.pushPhase(new BattleEndPhase(false)); + globalScene.phaseManager.pushPhase(new BattleEndPhase(false)); if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { - globalScene.pushPhase(new SelectBiomePhase()); + globalScene.phaseManager.pushPhase(new SelectBiomePhase()); } - globalScene.pushPhase(new NewBattlePhase()); + globalScene.phaseManager.pushPhase(new NewBattlePhase()); } else { playerPokemon.turnData.failedRunAway = true; - globalScene.queueMessage(i18next.t("battle:runAwayCannotEscape"), null, true, 500); + globalScene.phaseManager.queueMessage(i18next.t("battle:runAwayCannotEscape"), null, true, 500); } this.end(); diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index 96f6d02b1fc..4e3543be03a 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -19,7 +19,7 @@ export class BattleEndPhase extends BattlePhase { super.start(); // cull any extra `BattleEnd` phases from the queue. - globalScene.phaseQueue = globalScene.phaseQueue.filter(phase => { + globalScene.phaseManager.phaseQueue = globalScene.phaseManager.phaseQueue.filter(phase => { if (phase.is("BattleEndPhase")) { this.isVictory ||= phase.isVictory; return false; @@ -28,7 +28,7 @@ export class BattleEndPhase extends BattlePhase { }); // `phaseQueuePrepend` is private, so we have to use this inefficient loop. while ( - globalScene.tryRemoveUnshiftedPhase(phase => { + globalScene.phaseManager.tryRemoveUnshiftedPhase(phase => { if (phase.is("BattleEndPhase")) { this.isVictory ||= phase.isVictory; return true; @@ -55,8 +55,8 @@ export class BattleEndPhase extends BattlePhase { // Endless graceful end if (globalScene.gameMode.isEndless && globalScene.currentBattle.waveIndex >= 5850) { - globalScene.clearPhaseQueue(); - globalScene.unshiftPhase(new GameOverPhase(true)); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.unshiftPhase(new GameOverPhase(true)); } for (const pokemon of globalScene.getField()) { diff --git a/src/phases/berry-phase.ts b/src/phases/berry-phase.ts index 8d66d498039..e9e177a8fe4 100644 --- a/src/phases/berry-phase.ts +++ b/src/phases/berry-phase.ts @@ -50,7 +50,7 @@ export class BerryPhase extends FieldPhase { const cancelled = new BooleanHolder(false); pokemon.getOpponents().forEach(opp => applyAbAttrs(PreventBerryUseAbAttr, opp, cancelled)); if (cancelled.value) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:preventBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -58,7 +58,7 @@ export class BerryPhase extends FieldPhase { return; } - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM), ); diff --git a/src/phases/check-status-effect-phase.ts b/src/phases/check-status-effect-phase.ts index 0c5884bbac7..ec15676ebcc 100644 --- a/src/phases/check-status-effect-phase.ts +++ b/src/phases/check-status-effect-phase.ts @@ -15,7 +15,7 @@ export class CheckStatusEffectPhase extends Phase { const field = globalScene.getField(); for (const o of this.order) { if (field[o].status?.isPostTurn()) { - globalScene.unshiftPhase(new PostTurnStatusEffectPhase(o)); + globalScene.phaseManager.unshiftPhase(new PostTurnStatusEffectPhase(o)); } } this.end(); diff --git a/src/phases/check-switch-phase.ts b/src/phases/check-switch-phase.ts index 0506cd36c46..2299bf0c0a5 100644 --- a/src/phases/check-switch-phase.ts +++ b/src/phases/check-switch-phase.ts @@ -35,7 +35,7 @@ export class CheckSwitchPhase extends BattlePhase { // ...if the checked Pokemon is somehow not on the field if (globalScene.field.getAll().indexOf(pokemon) === -1) { - globalScene.unshiftPhase(new SummonMissingPhase(this.fieldIndex)); + globalScene.phaseManager.unshiftPhase(new SummonMissingPhase(this.fieldIndex)); return super.end(); } @@ -68,7 +68,9 @@ export class CheckSwitchPhase extends BattlePhase { UiMode.CONFIRM, () => { globalScene.ui.setMode(UiMode.MESSAGE); - globalScene.unshiftPhase(new SwitchPhase(SwitchType.INITIAL_SWITCH, this.fieldIndex, false, true)); + globalScene.phaseManager.unshiftPhase( + new SwitchPhase(SwitchType.INITIAL_SWITCH, this.fieldIndex, false, true), + ); this.end(); }, () => { diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 209c1eefc85..7a2e427ecce 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -192,7 +192,7 @@ export class CommandPhase extends FieldPhase { } console.log(moveTargets, getPokemonNameWithAffix(playerPokemon)); if (moveTargets.targets.length > 1 && moveTargets.multiple) { - globalScene.unshiftPhase(new SelectTargetPhase(this.fieldIndex)); + globalScene.phaseManager.unshiftPhase(new SelectTargetPhase(this.fieldIndex)); } if (turnCommand.move && (moveTargets.targets.length <= 1 || moveTargets.multiple)) { turnCommand.move.targets = moveTargets.targets; @@ -203,7 +203,7 @@ export class CommandPhase extends FieldPhase { ) { turnCommand.move.targets = playerPokemon.getMoveQueue()[0].targets; } else { - globalScene.unshiftPhase(new SelectTargetPhase(this.fieldIndex)); + globalScene.phaseManager.unshiftPhase(new SelectTargetPhase(this.fieldIndex)); } globalScene.currentBattle.preTurnCommands[this.fieldIndex] = preTurnCommand; globalScene.currentBattle.turnCommands[this.fieldIndex] = turnCommand; @@ -457,8 +457,8 @@ export class CommandPhase extends FieldPhase { cancel() { if (this.fieldIndex) { - globalScene.unshiftPhase(new CommandPhase(0)); - globalScene.unshiftPhase(new CommandPhase(1)); + globalScene.phaseManager.unshiftPhase(new CommandPhase(0)); + globalScene.phaseManager.unshiftPhase(new CommandPhase(1)); this.end(); } } diff --git a/src/phases/egg-hatch-phase.ts b/src/phases/egg-hatch-phase.ts index dfcdc05f9a2..d6c40a1510e 100644 --- a/src/phases/egg-hatch-phase.ts +++ b/src/phases/egg-hatch-phase.ts @@ -225,7 +225,7 @@ export class EggHatchPhase extends Phase { } end() { - if (globalScene.findPhase(p => p.is("EggHatchPhase"))) { + if (globalScene.phaseManager.findPhase(p => p.is("EggHatchPhase"))) { this.eggHatchHandler.clear(); } else { globalScene.time.delayedCall(250, () => globalScene.setModifiersVisible(true)); diff --git a/src/phases/egg-lapse-phase.ts b/src/phases/egg-lapse-phase.ts index 182d6f304d6..a19bf8f50e5 100644 --- a/src/phases/egg-lapse-phase.ts +++ b/src/phases/egg-lapse-phase.ts @@ -62,12 +62,12 @@ export class EggLapsePhase extends Phase { true, ); } else if (eggsToHatchCount >= this.minEggsToSkip && globalScene.eggSkipPreference === 2) { - globalScene.queueMessage(i18next.t("battle:eggHatching")); + globalScene.phaseManager.queueMessage(i18next.t("battle:eggHatching")); this.hatchEggsSkipped(eggsToHatch); this.showSummary(); } else { // regular hatches, no summary - globalScene.queueMessage(i18next.t("battle:eggHatching")); + globalScene.phaseManager.queueMessage(i18next.t("battle:eggHatching")); this.hatchEggsRegular(eggsToHatch); this.end(); } @@ -83,7 +83,7 @@ export class EggLapsePhase extends Phase { hatchEggsRegular(eggsToHatch: Egg[]) { let eggsToHatchCount: number = eggsToHatch.length; for (const egg of eggsToHatch) { - globalScene.unshiftPhase(new EggHatchPhase(this, egg, eggsToHatchCount)); + globalScene.phaseManager.unshiftPhase(new EggHatchPhase(this, egg, eggsToHatchCount)); eggsToHatchCount--; } } @@ -99,7 +99,7 @@ export class EggLapsePhase extends Phase { } showSummary() { - globalScene.unshiftPhase(new EggSummaryPhase(this.eggHatchData)); + globalScene.phaseManager.unshiftPhase(new EggSummaryPhase(this.eggHatchData)); this.end(); } diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index df84f8f8ff4..cdd17c6d6d6 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -68,7 +68,7 @@ export class EncounterPhase extends BattlePhase { // Failsafe if players somehow skip floor 200 in classic mode if (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex > 200) { - globalScene.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.unshiftPhase(new GameOverPhase()); } const loadEnemyAssets: Promise[] = []; @@ -438,9 +438,9 @@ export class EncounterPhase extends BattlePhase { const doTrainerSummon = () => { this.hideEnemyTrainer(); const availablePartyMembers = globalScene.getEnemyParty().filter(p => !p.isFainted()).length; - globalScene.unshiftPhase(new SummonPhase(0, false)); + globalScene.phaseManager.unshiftPhase(new SummonPhase(0, false)); if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.unshiftPhase(new SummonPhase(1, false)); + globalScene.phaseManager.unshiftPhase(new SummonPhase(1, false)); } this.end(); }; @@ -496,7 +496,7 @@ export class EncounterPhase extends BattlePhase { globalScene.ui.clearText(); globalScene.ui.getMessageHandler().hideNameText(); - globalScene.unshiftPhase(new MysteryEncounterPhase()); + globalScene.phaseManager.unshiftPhase(new MysteryEncounterPhase()); this.end(); }; @@ -554,7 +554,7 @@ export class EncounterPhase extends BattlePhase { enemyField.forEach((enemyPokemon, e) => { if (enemyPokemon.isShiny(true)) { - globalScene.unshiftPhase(new ShinySparklePhase(BattlerIndex.ENEMY + e)); + globalScene.phaseManager.unshiftPhase(new ShinySparklePhase(BattlerIndex.ENEMY + e)); } /** This sets Eternatus' held item to be untransferrable, preventing it from being stolen */ if ( @@ -576,7 +576,7 @@ export class EncounterPhase extends BattlePhase { if (![BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType)) { enemyField.map(p => - globalScene.pushConditionalPhase(new PostSummonPhase(p.getBattlerIndex()), () => { + globalScene.phaseManager.pushConditionalPhase(new PostSummonPhase(p.getBattlerIndex()), () => { // if there is not a player party, we can't continue if (!globalScene.getPlayerParty().length) { return false; @@ -594,7 +594,7 @@ export class EncounterPhase extends BattlePhase { ); const ivScannerModifier = globalScene.findModifier(m => m instanceof IvScannerModifier); if (ivScannerModifier) { - enemyField.map(p => globalScene.pushPhase(new ScanIvsPhase(p.getBattlerIndex()))); + enemyField.map(p => globalScene.phaseManager.pushPhase(new ScanIvsPhase(p.getBattlerIndex()))); } } @@ -602,21 +602,21 @@ export class EncounterPhase extends BattlePhase { const availablePartyMembers = globalScene.getPokemonAllowedInBattle(); if (!availablePartyMembers[0].isOnField()) { - globalScene.pushPhase(new SummonPhase(0)); + globalScene.phaseManager.pushPhase(new SummonPhase(0)); } if (globalScene.currentBattle.double) { if (availablePartyMembers.length > 1) { - globalScene.pushPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.pushPhase(new ToggleDoublePositionPhase(true)); if (!availablePartyMembers[1].isOnField()) { - globalScene.pushPhase(new SummonPhase(1)); + globalScene.phaseManager.pushPhase(new SummonPhase(1)); } } } else { if (availablePartyMembers.length > 1 && availablePartyMembers[1].isOnField()) { - globalScene.pushPhase(new ReturnPhase(1)); + globalScene.phaseManager.pushPhase(new ReturnPhase(1)); } - globalScene.pushPhase(new ToggleDoublePositionPhase(false)); + globalScene.phaseManager.pushPhase(new ToggleDoublePositionPhase(false)); } if ( @@ -625,9 +625,9 @@ export class EncounterPhase extends BattlePhase { ) { const minPartySize = globalScene.currentBattle.double ? 2 : 1; if (availablePartyMembers.length > minPartySize) { - globalScene.pushPhase(new CheckSwitchPhase(0, globalScene.currentBattle.double)); + globalScene.phaseManager.pushPhase(new CheckSwitchPhase(0, globalScene.currentBattle.double)); if (globalScene.currentBattle.double) { - globalScene.pushPhase(new CheckSwitchPhase(1, globalScene.currentBattle.double)); + globalScene.phaseManager.pushPhase(new CheckSwitchPhase(1, globalScene.currentBattle.double)); } } } diff --git a/src/phases/evolution-phase.ts b/src/phases/evolution-phase.ts index 5e635f4cd82..937a7d31542 100644 --- a/src/phases/evolution-phase.ts +++ b/src/phases/evolution-phase.ts @@ -262,7 +262,7 @@ export class EvolutionPhase extends Phase { SoundFade.fadeOut(globalScene, this.evolutionBgm, 100); - globalScene.unshiftPhase(new EndEvolutionPhase()); + globalScene.phaseManager.unshiftPhase(new EndEvolutionPhase()); globalScene.ui.showText( i18next.t("menu:stoppedEvolving", { @@ -355,9 +355,11 @@ export class EvolutionPhase extends Phase { .getLevelMoves(this.lastLevel + 1, true, false, false, learnSituation) .filter(lm => lm[0] === EVOLVE_MOVE); for (const lm of levelMoves) { - globalScene.unshiftPhase(new LearnMovePhase(globalScene.getPlayerParty().indexOf(this.pokemon), lm[1])); + globalScene.phaseManager.unshiftPhase( + new LearnMovePhase(globalScene.getPlayerParty().indexOf(this.pokemon), lm[1]), + ); } - globalScene.unshiftPhase(new EndEvolutionPhase()); + globalScene.phaseManager.unshiftPhase(new EndEvolutionPhase()); globalScene.playSound("se/shine"); this.doSpray(); diff --git a/src/phases/exp-phase.ts b/src/phases/exp-phase.ts index 14d7d7578f8..8084ae78221 100644 --- a/src/phases/exp-phase.ts +++ b/src/phases/exp-phase.ts @@ -34,7 +34,7 @@ export class ExpPhase extends PlayerPartyMemberPokemonPhase { pokemon.addExp(exp.value); const newLevel = pokemon.level; if (newLevel > lastLevel) { - globalScene.unshiftPhase(new LevelUpPhase(this.partyMemberIndex, lastLevel, newLevel)); + globalScene.phaseManager.unshiftPhase(new LevelUpPhase(this.partyMemberIndex, lastLevel, newLevel)); } pokemon.updateInfo().then(() => this.end()); }, diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index fac0947c4e7..41e21162dbf 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -115,7 +115,7 @@ export class FaintPhase extends PokemonPhase { }); } - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), @@ -166,7 +166,7 @@ export class FaintPhase extends PokemonPhase { const legalPlayerPartyPokemon = legalPlayerPokemon.filter(p => !p.isActive(true)); if (!legalPlayerPokemon.length) { /** If the player doesn't have any legal Pokemon, end the game */ - globalScene.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.unshiftPhase(new GameOverPhase()); } else if ( globalScene.currentBattle.double && legalPlayerPokemon.length === 1 && @@ -176,23 +176,25 @@ export class FaintPhase extends PokemonPhase { * If the player has exactly one Pokemon in total at this point in a double battle, and that Pokemon * is already on the field, unshift a phase that moves that Pokemon to center position. */ - globalScene.unshiftPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.unshiftPhase(new ToggleDoublePositionPhase(true)); } else if (legalPlayerPartyPokemon.length > 0) { /** * 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. */ - globalScene.pushPhase(new SwitchPhase(SwitchType.SWITCH, this.fieldIndex, true, false)); + globalScene.phaseManager.pushPhase(new SwitchPhase(SwitchType.SWITCH, this.fieldIndex, true, false)); } } else { - globalScene.unshiftPhase(new VictoryPhase(this.battlerIndex)); + globalScene.phaseManager.unshiftPhase(new VictoryPhase(this.battlerIndex)); if ([BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType)) { const hasReservePartyMember = !!globalScene .getEnemyParty() .filter(p => p.isActive() && !p.isOnField() && p.trainerSlot === (pokemon as EnemyPokemon).trainerSlot) .length; if (hasReservePartyMember) { - globalScene.pushPhase(new SwitchSummonPhase(SwitchType.SWITCH, this.fieldIndex, -1, false, false)); + globalScene.phaseManager.pushPhase( + new SwitchSummonPhase(SwitchType.SWITCH, this.fieldIndex, -1, false, false), + ); } } } @@ -247,7 +249,7 @@ export class FaintPhase extends PokemonPhase { } else { // Final boss' HP threshold has been bypassed; cancel faint and force check for 2nd phase enemy.hp++; - globalScene.unshiftPhase(new DamageAnimPhase(enemy.getBattlerIndex(), 0, HitResult.INDIRECT)); + globalScene.phaseManager.unshiftPhase(new DamageAnimPhase(enemy.getBattlerIndex(), 0, HitResult.INDIRECT)); this.end(); } return true; diff --git a/src/phases/form-change-phase.ts b/src/phases/form-change-phase.ts index f5e428c6d3d..c0d2a9a11eb 100644 --- a/src/phases/form-change-phase.ts +++ b/src/phases/form-change-phase.ts @@ -100,7 +100,7 @@ export class FormChangePhase extends EvolutionPhase { globalScene.time.delayedCall(900, () => { this.pokemon.changeForm(this.formChange).then(() => { if (!this.modal) { - globalScene.unshiftPhase(new EndEvolutionPhase()); + globalScene.phaseManager.unshiftPhase(new EndEvolutionPhase()); } globalScene.playSound("se/shine"); diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index 1d03739d610..166bb955c24 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -47,7 +47,7 @@ export class GameOverPhase extends BattlePhase { start() { super.start(); - globalScene.hideAbilityBar(); + globalScene.phaseManager.hideAbilityBar(); // Failsafe if players somehow skip floor 200 in classic mode if (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex > 200) { @@ -84,23 +84,23 @@ export class GameOverPhase extends BattlePhase { () => { globalScene.ui.fadeOut(1250).then(() => { globalScene.reset(); - globalScene.clearPhaseQueue(); + globalScene.phaseManager.clearPhaseQueue(); globalScene.gameData.loadSession(globalScene.sessionSlotId).then(() => { - globalScene.pushPhase(new EncounterPhase(true)); + globalScene.phaseManager.pushPhase(new EncounterPhase(true)); const availablePartyMembers = globalScene.getPokemonAllowedInBattle().length; - globalScene.pushPhase(new SummonPhase(0)); + globalScene.phaseManager.pushPhase(new SummonPhase(0)); if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.pushPhase(new SummonPhase(1)); + globalScene.phaseManager.pushPhase(new SummonPhase(1)); } if ( globalScene.currentBattle.waveIndex > 1 && globalScene.currentBattle.battleType !== BattleType.TRAINER ) { - globalScene.pushPhase(new CheckSwitchPhase(0, globalScene.currentBattle.double)); + globalScene.phaseManager.pushPhase(new CheckSwitchPhase(0, globalScene.currentBattle.double)); if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.pushPhase(new CheckSwitchPhase(1, globalScene.currentBattle.double)); + globalScene.phaseManager.pushPhase(new CheckSwitchPhase(1, globalScene.currentBattle.double)); } } @@ -148,7 +148,7 @@ export class GameOverPhase extends BattlePhase { globalScene.ui.fadeOut(fadeDuration).then(() => { activeBattlers.map(a => a.setVisible(false)); globalScene.setFieldScale(1, true); - globalScene.clearPhaseQueue(); + globalScene.phaseManager.clearPhaseQueue(); globalScene.ui.clearText(); if (this.isVictory && globalScene.gameMode.isChallenge) { @@ -160,15 +160,17 @@ export class GameOverPhase extends BattlePhase { this.handleUnlocks(); for (const species of this.firstRibbons) { - globalScene.unshiftPhase(new RibbonModifierRewardPhase(modifierTypes.VOUCHER_PLUS, species)); + globalScene.phaseManager.unshiftPhase( + new RibbonModifierRewardPhase(modifierTypes.VOUCHER_PLUS, species), + ); } if (!firstClear) { - globalScene.unshiftPhase(new GameOverModifierRewardPhase(modifierTypes.VOUCHER_PREMIUM)); + globalScene.phaseManager.unshiftPhase(new GameOverModifierRewardPhase(modifierTypes.VOUCHER_PREMIUM)); } } this.getRunHistoryEntry().then(runHistoryEntry => { globalScene.gameData.saveRunHistory(runHistoryEntry, this.isVictory); - globalScene.pushPhase(new PostGameOverPhase(endCardPhase)); + globalScene.phaseManager.pushPhase(new PostGameOverPhase(endCardPhase)); this.end(); }); }; @@ -198,7 +200,7 @@ export class GameOverPhase extends BattlePhase { globalScene.ui.fadeOut(500).then(() => { globalScene.charSprite.hide().then(() => { const endCardPhase = new EndCardPhase(); - globalScene.unshiftPhase(endCardPhase); + globalScene.phaseManager.unshiftPhase(endCardPhase); clear(endCardPhase); }); }); @@ -208,7 +210,7 @@ export class GameOverPhase extends BattlePhase { }); } else { const endCardPhase = new EndCardPhase(); - globalScene.unshiftPhase(endCardPhase); + globalScene.phaseManager.unshiftPhase(endCardPhase); clear(endCardPhase); } } else { @@ -230,9 +232,9 @@ export class GameOverPhase extends BattlePhase { }) .then(success => doGameOver(!globalScene.gameMode.isDaily || !!success)) .catch(_err => { - globalScene.clearPhaseQueue(); - globalScene.clearPhaseQueueSplice(); - globalScene.unshiftPhase(new MessagePhase(i18next.t("menu:serverCommunicationFailed"), 2500)); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.clearPhaseQueueSplice(); + globalScene.phaseManager.unshiftPhase(new MessagePhase(i18next.t("menu:serverCommunicationFailed"), 2500)); // force the game to reload after 2 seconds. setTimeout(() => { window.location.reload(); @@ -251,22 +253,22 @@ export class GameOverPhase extends BattlePhase { handleUnlocks(): void { if (this.isVictory && globalScene.gameMode.isClassic) { if (!globalScene.gameData.unlocks[Unlockables.ENDLESS_MODE]) { - globalScene.unshiftPhase(new UnlockPhase(Unlockables.ENDLESS_MODE)); + globalScene.phaseManager.unshiftPhase(new UnlockPhase(Unlockables.ENDLESS_MODE)); } if ( globalScene.getPlayerParty().filter(p => p.fusionSpecies).length && !globalScene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE] ) { - globalScene.unshiftPhase(new UnlockPhase(Unlockables.SPLICED_ENDLESS_MODE)); + globalScene.phaseManager.unshiftPhase(new UnlockPhase(Unlockables.SPLICED_ENDLESS_MODE)); } if (!globalScene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE]) { - globalScene.unshiftPhase(new UnlockPhase(Unlockables.MINI_BLACK_HOLE)); + globalScene.phaseManager.unshiftPhase(new UnlockPhase(Unlockables.MINI_BLACK_HOLE)); } if ( !globalScene.gameData.unlocks[Unlockables.EVIOLITE] && globalScene.getPlayerParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions) ) { - globalScene.unshiftPhase(new UnlockPhase(Unlockables.EVIOLITE)); + globalScene.phaseManager.unshiftPhase(new UnlockPhase(Unlockables.EVIOLITE)); } } } diff --git a/src/phases/learn-move-phase.ts b/src/phases/learn-move-phase.ts index d455ed35591..7464cebe7da 100644 --- a/src/phases/learn-move-phase.ts +++ b/src/phases/learn-move-phase.ts @@ -195,7 +195,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { pokemon.usedTMs = []; } pokemon.usedTMs.push(this.moveId); - globalScene.tryRemovePhase(phase => phase.is("SelectModifierPhase")); + globalScene.phaseManager.tryRemovePhase(phase => phase.is("SelectModifierPhase")); } else if (this.learnMoveType === LearnMoveType.MEMORY) { if (this.cost !== -1) { if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { @@ -205,7 +205,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { } globalScene.playSound("se/buy"); } else { - globalScene.tryRemovePhase(phase => phase.is("SelectModifierPhase")); + globalScene.phaseManager.tryRemovePhase(phase => phase.is("SelectModifierPhase")); } } pokemon.setMove(index, this.moveId); diff --git a/src/phases/level-up-phase.ts b/src/phases/level-up-phase.ts index 7cf86a313df..b3b82f13f42 100644 --- a/src/phases/level-up-phase.ts +++ b/src/phases/level-up-phase.ts @@ -66,14 +66,14 @@ export class LevelUpPhase extends PlayerPartyMemberPokemonPhase { // this feels like an unnecessary optimization const levelMoves = this.getPokemon().getLevelMoves(this.lastLevel + 1); for (const lm of levelMoves) { - globalScene.unshiftPhase(new LearnMovePhase(this.partyMemberIndex, lm[1])); + globalScene.phaseManager.unshiftPhase(new LearnMovePhase(this.partyMemberIndex, lm[1])); } } if (!this.pokemon.pauseEvolutions) { const evolution = this.pokemon.getEvolution(); if (evolution) { this.pokemon.breakIllusion(); - globalScene.unshiftPhase(new EvolutionPhase(this.pokemon, evolution, this.lastLevel)); + globalScene.phaseManager.unshiftPhase(new EvolutionPhase(this.pokemon, evolution, this.lastLevel)); } } return super.end(); diff --git a/src/phases/login-phase.ts b/src/phases/login-phase.ts index ec12b5ddaa4..5e1728d4415 100644 --- a/src/phases/login-phase.ts +++ b/src/phases/login-phase.ts @@ -70,7 +70,7 @@ export class LoginPhase extends Phase { }); }, () => { - globalScene.unshiftPhase(new LoginPhase(false)); + globalScene.phaseManager.unshiftPhase(new LoginPhase(false)); this.end(); }, ], @@ -94,7 +94,7 @@ export class LoginPhase extends Phase { removeCookie(sessionIdKey); globalScene.reset(true, true); } else { - globalScene.unshiftPhase(new UnavailablePhase()); + globalScene.phaseManager.unshiftPhase(new UnavailablePhase()); super.end(); } return null; @@ -114,7 +114,7 @@ export class LoginPhase extends Phase { globalScene.ui.setMode(UiMode.MESSAGE); if (!globalScene.gameData.gender) { - globalScene.unshiftPhase(new SelectGenderPhase()); + globalScene.phaseManager.unshiftPhase(new SelectGenderPhase()); } handleTutorial(Tutorial.Intro).then(() => super.end()); diff --git a/src/phases/message-phase.ts b/src/phases/message-phase.ts index 335258abe5c..2a485d837b0 100644 --- a/src/phases/message-phase.ts +++ b/src/phases/message-phase.ts @@ -44,7 +44,7 @@ export class MessagePhase extends Phase { page0 = page0.split(repname[p]).join(pokename[p]); page1 = page1.split(repname[p]).join(pokename[p]); } - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new MessagePhase(page1, this.callbackDelay, this.prompt, this.promptDelay, this.speaker), ); this.text = page0.trim(); diff --git a/src/phases/move-charge-phase.ts b/src/phases/move-charge-phase.ts index 789651623fa..263306acbf2 100644 --- a/src/phases/move-charge-phase.ts +++ b/src/phases/move-charge-phase.ts @@ -62,9 +62,9 @@ export class MoveChargePhase extends PokemonPhase { if (instantCharge.value) { // this MoveEndPhase will be duplicated by the queued MovePhase if not removed - globalScene.tryRemovePhase(phase => phase.is("MoveEndPhase") && phase.getPokemon() === user); + globalScene.phaseManager.tryRemovePhase(phase => phase.is("MoveEndPhase") && phase.getPokemon() === user); // queue a new MovePhase for this move's attack phase - globalScene.unshiftPhase(new MovePhase(user, [this.targetIndex], this.move, false)); + globalScene.phaseManager.unshiftPhase(new MovePhase(user, [this.targetIndex], this.move, false)); } else { user.getMoveQueue().push({ move: move.id, targets: [this.targetIndex] }); } diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index e62f65b029b..e135f5bd161 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -221,7 +221,7 @@ export class MoveEffectPhase extends PokemonPhase { break; // biome-ignore lint/suspicious/noFallthroughSwitchClause: The fallthrough is intentional case HitCheckResult.NO_EFFECT: - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t(this.move.id === MoveId.SHEER_COLD ? "battle:hitResultImmune" : "battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(target), }), @@ -232,7 +232,7 @@ export class MoveEffectPhase extends PokemonPhase { applyMoveAttrs(NoEffectAttr, user, target, this.move); break; case HitCheckResult.MISS: - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) }), ); applyMoveAttrs(MissEffectAttr, user, target, this.move); @@ -384,7 +384,7 @@ export class MoveEffectPhase extends PokemonPhase { } if (this.queuedPhases.length) { - globalScene.appendToPhase(this.queuedPhases, MoveEndPhase); + globalScene.phaseManager.appendToPhase(this.queuedPhases, MoveEndPhase); } const moveType = user.getMoveType(this.move, true); if (this.move.category !== MoveCategory.STATUS && !user.stellarTypesBoosted.includes(moveType)) { @@ -410,14 +410,14 @@ export class MoveEffectPhase extends PokemonPhase { */ if (user) { if (user.turnData.hitsLeft && --user.turnData.hitsLeft >= 1 && this.getFirstTarget()?.isActive()) { - globalScene.unshiftPhase(this.getNewHitPhase()); + globalScene.phaseManager.unshiftPhase(this.getNewHitPhase()); } else { // Queue message for number of hits made by multi-move // If multi-hit attack only hits once, still want to render a message const hitsTotal = user.turnData.hitCount - Math.max(user.turnData.hitsLeft, 0); if (hitsTotal > 1 || (user.turnData.hitsLeft && user.turnData.hitsLeft > 0)) { // If there are multiple hits, or if there are hits of the multi-hit move left - globalScene.queueMessage(i18next.t("battle:attackHitsCount", { count: hitsTotal })); + globalScene.phaseManager.queueMessage(i18next.t("battle:attackHitsCount", { count: hitsTotal })); } globalScene.applyModifiers(HitHealModifier, this.player, user); this.getTargets().forEach(target => (target.turnData.moveEffectiveness = null)); @@ -858,7 +858,7 @@ export class MoveEffectPhase extends PokemonPhase { }); if (isCritical) { - globalScene.queueMessage(i18next.t("battle:hitResultCriticalHit")); + globalScene.phaseManager.queueMessage(i18next.t("battle:hitResultCriticalHit")); } if (damage <= 0) { @@ -901,9 +901,9 @@ export class MoveEffectPhase extends PokemonPhase { */ protected onFaintTarget(user: Pokemon, target: Pokemon): void { // set splice index here, so future scene queues happen before FaintedPhase - globalScene.setPhaseQueueSplice(); + globalScene.phaseManager.setPhaseQueueSplice(); - globalScene.unshiftPhase(new FaintPhase(target.getBattlerIndex(), false, user)); + globalScene.phaseManager.unshiftPhase(new FaintPhase(target.getBattlerIndex(), false, user)); target.destroySubstitute(); target.lapseTag(BattlerTagType.COMMANDED); @@ -936,7 +936,7 @@ export class MoveEffectPhase extends PokemonPhase { break; } if (msg) { - globalScene.queueMessage(msg); + globalScene.phaseManager.queueMessage(msg); } } diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 300c27e01fc..03f94ad3d1d 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -268,10 +268,10 @@ export class MovePhase extends BattlePhase { if (activated) { this.cancel(); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( getStatusEffectActivationText(this.pokemon.status.effect, getPokemonNameWithAffix(this.pokemon)), ); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new CommonAnimPhase( this.pokemon.getBattlerIndex(), undefined, @@ -279,7 +279,7 @@ export class MovePhase extends BattlePhase { ), ); } else if (healed) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( getStatusEffectHealText(this.pokemon.status.effect, getPokemonNameWithAffix(this.pokemon)), ); this.pokemon.resetStatus(); @@ -407,7 +407,7 @@ export class MovePhase extends BattlePhase { if (success) { const move = this.move.getMove(); applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, move); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new MoveEffectPhase(this.pokemon.getBattlerIndex(), this.targets, move, this.reflected, this.move.virtual), ); } else { @@ -457,7 +457,9 @@ export class MovePhase extends BattlePhase { applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove()); this.showMoveText(); - globalScene.unshiftPhase(new MoveChargePhase(this.pokemon.getBattlerIndex(), this.targets[0], this.move)); + globalScene.phaseManager.unshiftPhase( + new MoveChargePhase(this.pokemon.getBattlerIndex(), this.targets[0], this.move), + ); } else { this.pokemon.pushMoveHistory({ move: this.move.moveId, @@ -479,7 +481,7 @@ export class MovePhase extends BattlePhase { * Queues a {@linkcode MoveEndPhase} and then ends the phase */ public end(): void { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new MoveEndPhase(this.pokemon.getBattlerIndex(), this.getActiveTargetPokemon(), this.followUp), ); @@ -545,12 +547,12 @@ export class MovePhase extends BattlePhase { if (this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr)) { redirectTarget.value = currentTarget; // TODO: Ability displays should be handled by the ability - globalScene.queueAbilityDisplay( + globalScene.phaseManager.queueAbilityDisplay( this.pokemon, this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr), true, ); - globalScene.queueAbilityDisplay( + globalScene.phaseManager.queueAbilityDisplay( this.pokemon, this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr), false, @@ -649,7 +651,7 @@ export class MovePhase extends BattlePhase { return; } - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t(this.reflected ? "battle:magicCoatActivated" : "battle:useMove", { pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), moveName: this.move.getName(), @@ -660,6 +662,6 @@ export class MovePhase extends BattlePhase { } public showFailedText(failedText: string = i18next.t("battle:attackFailed")): void { - globalScene.queueMessage(failedText); + globalScene.phaseManager.queueMessage(failedText); } } diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index 5365ab3da32..b1ca11d45a5 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -58,8 +58,8 @@ export class MysteryEncounterPhase extends Phase { super.start(); // Clears out queued phases that are part of standard battle - globalScene.clearPhaseQueue(); - globalScene.clearPhaseQueueSplice(); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.clearPhaseQueueSplice(); const encounter = globalScene.currentBattle.mysteryEncounter!; encounter.updateSeedOffset(); @@ -124,7 +124,7 @@ export class MysteryEncounterPhase extends Phase { */ continueEncounter() { const endDialogueAndContinueEncounter = () => { - globalScene.pushPhase(new MysteryEncounterOptionSelectedPhase()); + globalScene.phaseManager.pushPhase(new MysteryEncounterOptionSelectedPhase()); this.end(); }; @@ -247,8 +247,8 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase { }); // Remove any status tick phases - while (globalScene.findPhase(p => p.is("PostTurnStatusEffectPhase"))) { - globalScene.tryRemovePhase(p => p.is("PostTurnStatusEffectPhase")); + while (globalScene.phaseManager.findPhase(p => p.is("PostTurnStatusEffectPhase"))) { + globalScene.phaseManager.tryRemovePhase(p => p.is("PostTurnStatusEffectPhase")); } // The total number of Pokemon in the player's party that can legally fight @@ -256,7 +256,7 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase { // The total number of legal player Pokemon that aren't currently on the field const legalPlayerPartyPokemon = legalPlayerPokemon.filter(p => !p.isActive(true)); if (!legalPlayerPokemon.length) { - globalScene.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.unshiftPhase(new GameOverPhase()); return this.end(); } @@ -265,13 +265,13 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase { const playerField = globalScene.getPlayerField(); playerField.forEach((pokemon, i) => { if (!pokemon.isAllowedInBattle() && legalPlayerPartyPokemon.length > i) { - globalScene.unshiftPhase(new SwitchPhase(SwitchType.SWITCH, i, true, false)); + globalScene.phaseManager.unshiftPhase(new SwitchPhase(SwitchType.SWITCH, i, true, false)); } }); // THEN, if is a double battle, and player only has 1 summoned pokemon, center pokemon on field if (globalScene.currentBattle.double && legalPlayerPokemon.length === 1 && legalPlayerPartyPokemon.length === 0) { - globalScene.unshiftPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.unshiftPhase(new ToggleDoublePositionPhase(true)); } this.end(); @@ -348,9 +348,9 @@ export class MysteryEncounterBattlePhase extends Phase { globalScene.playBgm(); } const availablePartyMembers = globalScene.getEnemyParty().filter(p => !p.isFainted()).length; - globalScene.unshiftPhase(new SummonPhase(0, false)); + globalScene.phaseManager.unshiftPhase(new SummonPhase(0, false)); if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.unshiftPhase(new SummonPhase(1, false)); + globalScene.phaseManager.unshiftPhase(new SummonPhase(1, false)); } if (!globalScene.currentBattle.mysteryEncounter?.hideBattleIntroMessage) { @@ -368,9 +368,9 @@ export class MysteryEncounterBattlePhase extends Phase { const doTrainerSummon = () => { this.hideEnemyTrainer(); const availablePartyMembers = globalScene.getEnemyParty().filter(p => !p.isFainted()).length; - globalScene.unshiftPhase(new SummonPhase(0, false)); + globalScene.phaseManager.unshiftPhase(new SummonPhase(0, false)); if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.unshiftPhase(new SummonPhase(1, false)); + globalScene.phaseManager.unshiftPhase(new SummonPhase(1, false)); } this.endBattleSetup(); }; @@ -426,37 +426,37 @@ export class MysteryEncounterBattlePhase extends Phase { if (encounterMode !== MysteryEncounterMode.TRAINER_BATTLE) { const ivScannerModifier = globalScene.findModifier(m => m instanceof IvScannerModifier); if (ivScannerModifier) { - enemyField.map(p => globalScene.pushPhase(new ScanIvsPhase(p.getBattlerIndex()))); + enemyField.map(p => globalScene.phaseManager.pushPhase(new ScanIvsPhase(p.getBattlerIndex()))); } } const availablePartyMembers = globalScene.getPlayerParty().filter(p => p.isAllowedInBattle()); if (!availablePartyMembers[0].isOnField()) { - globalScene.pushPhase(new SummonPhase(0)); + globalScene.phaseManager.pushPhase(new SummonPhase(0)); } if (globalScene.currentBattle.double) { if (availablePartyMembers.length > 1) { - globalScene.pushPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.pushPhase(new ToggleDoublePositionPhase(true)); if (!availablePartyMembers[1].isOnField()) { - globalScene.pushPhase(new SummonPhase(1)); + globalScene.phaseManager.pushPhase(new SummonPhase(1)); } } } else { if (availablePartyMembers.length > 1 && availablePartyMembers[1].isOnField()) { globalScene.getPlayerField().forEach(pokemon => pokemon.lapseTag(BattlerTagType.COMMANDED)); - globalScene.pushPhase(new ReturnPhase(1)); + globalScene.phaseManager.pushPhase(new ReturnPhase(1)); } - globalScene.pushPhase(new ToggleDoublePositionPhase(false)); + globalScene.phaseManager.pushPhase(new ToggleDoublePositionPhase(false)); } if (encounterMode !== MysteryEncounterMode.TRAINER_BATTLE && !this.disableSwitch) { const minPartySize = globalScene.currentBattle.double ? 2 : 1; if (availablePartyMembers.length > minPartySize) { - globalScene.pushPhase(new CheckSwitchPhase(0, globalScene.currentBattle.double)); + globalScene.phaseManager.pushPhase(new CheckSwitchPhase(0, globalScene.currentBattle.double)); if (globalScene.currentBattle.double) { - globalScene.pushPhase(new CheckSwitchPhase(1, globalScene.currentBattle.double)); + globalScene.phaseManager.pushPhase(new CheckSwitchPhase(1, globalScene.currentBattle.double)); } } } @@ -562,8 +562,8 @@ export class MysteryEncounterRewardsPhase extends Phase { if (encounter.doEncounterRewards) { encounter.doEncounterRewards(); } else if (this.addHealPhase) { - globalScene.tryRemovePhase(p => p.is("SelectModifierPhase")); - globalScene.unshiftPhase( + globalScene.phaseManager.tryRemovePhase(p => p.is("SelectModifierPhase")); + globalScene.phaseManager.unshiftPhase( new SelectModifierPhase(0, undefined, { fillRemaining: false, rerollMultiplier: -1, @@ -571,7 +571,7 @@ export class MysteryEncounterRewardsPhase extends Phase { ); } - globalScene.pushPhase(new PostMysteryEncounterPhase()); + globalScene.phaseManager.pushPhase(new PostMysteryEncounterPhase()); this.end(); } } @@ -618,10 +618,10 @@ export class PostMysteryEncounterPhase extends Phase { continueEncounter() { const endPhase = () => { if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { - globalScene.pushPhase(new SelectBiomePhase()); + globalScene.phaseManager.pushPhase(new SelectBiomePhase()); } - globalScene.pushPhase(new NewBattlePhase()); + globalScene.phaseManager.pushPhase(new NewBattlePhase()); this.end(); }; diff --git a/src/phases/new-battle-phase.ts b/src/phases/new-battle-phase.ts index c4cfc72fb53..65ecc81df2d 100644 --- a/src/phases/new-battle-phase.ts +++ b/src/phases/new-battle-phase.ts @@ -7,9 +7,11 @@ export class NewBattlePhase extends BattlePhase { super.start(); // cull any extra `NewBattle` phases from the queue. - globalScene.phaseQueue = globalScene.phaseQueue.filter(phase => !phase.is("NewBattlePhase")); + globalScene.phaseManager.phaseQueue = globalScene.phaseManager.phaseQueue.filter( + phase => !phase.is("NewBattlePhase"), + ); // `phaseQueuePrepend` is private, so we have to use this inefficient loop. - while (globalScene.tryRemoveUnshiftedPhase(phase => phase.is("NewBattlePhase"))) {} + while (globalScene.phaseManager.tryRemoveUnshiftedPhase(phase => phase.is("NewBattlePhase"))) {} globalScene.newBattle(); diff --git a/src/phases/obtain-status-effect-phase.ts b/src/phases/obtain-status-effect-phase.ts index 820db910681..2982bc982d9 100644 --- a/src/phases/obtain-status-effect-phase.ts +++ b/src/phases/obtain-status-effect-phase.ts @@ -41,7 +41,7 @@ export class ObtainStatusEffectPhase extends PokemonPhase { } pokemon.updateInfo(true); new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect! - 1), pokemon).play(false, () => { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( getStatusEffectObtainText( this.statusEffect, getPokemonNameWithAffix(pokemon), @@ -59,7 +59,7 @@ export class ObtainStatusEffectPhase extends PokemonPhase { return; } } else if (pokemon.status?.effect === this.statusEffect) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( getStatusEffectOverlapText(this.statusEffect ?? StatusEffect.NONE, getPokemonNameWithAffix(pokemon)), ); } diff --git a/src/phases/pokemon-heal-phase.ts b/src/phases/pokemon-heal-phase.ts index 60bbb17c30a..6e9b6b5c622 100644 --- a/src/phases/pokemon-heal-phase.ts +++ b/src/phases/pokemon-heal-phase.ts @@ -68,7 +68,7 @@ export class PokemonHealPhase extends CommonAnimPhase { let lastStatusEffect = StatusEffect.NONE; if (healBlock && this.hpHealed > 0) { - globalScene.queueMessage(healBlock.onActivation(pokemon)); + globalScene.phaseManager.queueMessage(healBlock.onActivation(pokemon)); this.message = null; return super.end(); } @@ -119,11 +119,13 @@ export class PokemonHealPhase extends CommonAnimPhase { } if (this.message) { - globalScene.queueMessage(this.message); + globalScene.phaseManager.queueMessage(this.message); } if (this.healStatus && lastStatusEffect && !hasMessage) { - globalScene.queueMessage(getStatusEffectHealText(lastStatusEffect, getPokemonNameWithAffix(pokemon))); + globalScene.phaseManager.queueMessage( + getStatusEffectHealText(lastStatusEffect, getPokemonNameWithAffix(pokemon)), + ); } if (!healOrDamage && !lastStatusEffect) { diff --git a/src/phases/pokemon-transform-phase.ts b/src/phases/pokemon-transform-phase.ts index c0f3b048003..4f18a19b2fb 100644 --- a/src/phases/pokemon-transform-phase.ts +++ b/src/phases/pokemon-transform-phase.ts @@ -65,7 +65,7 @@ export class PokemonTransformPhase extends PokemonPhase { globalScene.playSound("battle_anims/PRSFX- Transform"); } - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("abilityTriggers:postSummonTransform", { pokemonNameWithAffix: getPokemonNameWithAffix(user), targetName: target.name, diff --git a/src/phases/post-game-over-phase.ts b/src/phases/post-game-over-phase.ts index f985419da7a..37a3297cc52 100644 --- a/src/phases/post-game-over-phase.ts +++ b/src/phases/post-game-over-phase.ts @@ -28,7 +28,7 @@ export class PostGameOverPhase extends Phase { return globalScene.reset(true); } globalScene.reset(); - globalScene.unshiftPhase(new TitlePhase()); + globalScene.phaseManager.unshiftPhase(new TitlePhase()); this.end(); }); }); diff --git a/src/phases/post-turn-status-effect-phase.ts b/src/phases/post-turn-status-effect-phase.ts index 47a84059745..33fb012492d 100644 --- a/src/phases/post-turn-status-effect-phase.ts +++ b/src/phases/post-turn-status-effect-phase.ts @@ -32,7 +32,7 @@ export class PostTurnStatusEffectPhase extends PokemonPhase { applyAbAttrs(BlockStatusDamageAbAttr, pokemon, cancelled); if (!cancelled.value) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( getStatusEffectActivationText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)), ); const damage = new NumberHolder(0); diff --git a/src/phases/quiet-form-change-phase.ts b/src/phases/quiet-form-change-phase.ts index f677dcc48df..363b026831f 100644 --- a/src/phases/quiet-form-change-phase.ts +++ b/src/phases/quiet-form-change-phase.ts @@ -159,7 +159,7 @@ export class QuietFormChangePhase extends BattlePhase { this.pokemon.findAndRemoveTags(t => t.tagType === BattlerTagType.AUTOTOMIZED); if (globalScene?.currentBattle.battleSpec === BattleSpec.FINAL_BOSS && this.pokemon.isEnemy()) { globalScene.playBgm(); - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new PokemonHealPhase(this.pokemon.getBattlerIndex(), this.pokemon.getMaxHp(), null, false, false, false, true), ); this.pokemon.findAndRemoveTags(() => true); @@ -168,7 +168,9 @@ export class QuietFormChangePhase extends BattlePhase { this.pokemon.initBattleInfo(); this.pokemon.cry(); - const movePhase = globalScene.findPhase(p => p.is("MovePhase") && p.pokemon === this.pokemon) as MovePhase; + const movePhase = globalScene.phaseManager.findPhase( + p => p.is("MovePhase") && p.pokemon === this.pokemon, + ) as MovePhase; if (movePhase) { movePhase.cancel(); } diff --git a/src/phases/revival-blessing-phase.ts b/src/phases/revival-blessing-phase.ts index 3f70c93dd7a..2db73103a88 100644 --- a/src/phases/revival-blessing-phase.ts +++ b/src/phases/revival-blessing-phase.ts @@ -35,7 +35,7 @@ export class RevivalBlessingPhase extends BattlePhase { pokemon.resetTurnData(); pokemon.resetStatus(true, false, false, false); pokemon.heal(Math.min(toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("moveTriggers:revivalBlessing", { pokemonName: pokemon.name, }), @@ -51,16 +51,16 @@ export class RevivalBlessingPhase extends BattlePhase { ) { if (slotIndex <= 1) { // Revived ally pokemon - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new SwitchSummonPhase(SwitchType.SWITCH, pokemon.getFieldIndex(), slotIndex, false, true), ); - globalScene.unshiftPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.unshiftPhase(new ToggleDoublePositionPhase(true)); } else if (allyPokemon.isFainted()) { // Revived party pokemon, and ally pokemon is fainted - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new SwitchSummonPhase(SwitchType.SWITCH, allyPokemon.getFieldIndex(), slotIndex, false, true), ); - globalScene.unshiftPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.unshiftPhase(new ToggleDoublePositionPhase(true)); } } } diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index ef6b39e8b8f..633bf20d34c 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -22,9 +22,9 @@ export class SelectBiomePhase extends BattlePhase { const setNextBiome = (nextBiome: BiomeId) => { if (nextWaveIndex % 10 === 1) { globalScene.applyModifiers(MoneyInterestModifier, true); - globalScene.unshiftPhase(new PartyHealPhase(false)); + globalScene.phaseManager.unshiftPhase(new PartyHealPhase(false)); } - globalScene.unshiftPhase(new SwitchBiomePhase(nextBiome)); + globalScene.phaseManager.unshiftPhase(new SwitchBiomePhase(nextBiome)); this.end(); }; diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts index 6e429d9ad7f..5ac3b9e6d76 100644 --- a/src/phases/select-modifier-phase.ts +++ b/src/phases/select-modifier-phase.ts @@ -123,7 +123,7 @@ export class SelectModifierPhase extends BattlePhase { return false; } globalScene.reroll = true; - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new SelectModifierPhase( this.rerollCount + 1, this.typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as ModifierTier[], @@ -247,7 +247,7 @@ export class SelectModifierPhase extends BattlePhase { // If the player selects either of these, then escapes out of consuming them, // they are returned to a shop in the same state. if (modifier.type instanceof RememberMoveModifierType || modifier.type instanceof TmModifierType) { - globalScene.unshiftPhase(this.copy()); + globalScene.phaseManager.unshiftPhase(this.copy()); } if (cost && !(modifier.type instanceof RememberMoveModifierType)) { diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts index d25c2dd7211..2b60fcaf054 100644 --- a/src/phases/select-starter-phase.ts +++ b/src/phases/select-starter-phase.ts @@ -25,8 +25,8 @@ export class SelectStarterPhase extends Phase { globalScene.ui.clearText(); globalScene.ui.setMode(UiMode.SAVE_SLOT, SaveSlotUiMode.SAVE, (slotId: number) => { if (slotId === -1) { - globalScene.clearPhaseQueue(); - globalScene.pushPhase(new TitlePhase()); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.pushPhase(new TitlePhase()); return this.end(); } globalScene.sessionSlotId = slotId; diff --git a/src/phases/select-target-phase.ts b/src/phases/select-target-phase.ts index 46ba378a56c..515f7ed98ca 100644 --- a/src/phases/select-target-phase.ts +++ b/src/phases/select-target-phase.ts @@ -28,12 +28,12 @@ export class SelectTargetPhase extends PokemonPhase { const errorMessage = user .getRestrictingTag(move!, user, fieldSide[targets[0]])! .selectionDeniedText(user, moveObject.id); - globalScene.queueMessage(i18next.t(errorMessage, { moveName: moveObject.name }), 0, true); + globalScene.phaseManager.queueMessage(i18next.t(errorMessage, { moveName: moveObject.name }), 0, true); targets = []; } if (targets.length < 1) { globalScene.currentBattle.turnCommands[this.fieldIndex] = null; - globalScene.unshiftPhase(new CommandPhase(this.fieldIndex)); + globalScene.phaseManager.unshiftPhase(new CommandPhase(this.fieldIndex)); } else { turnCommand!.targets = targets; //TODO: is the bang correct here? } diff --git a/src/phases/show-ability-phase.ts b/src/phases/show-ability-phase.ts index 81aa69537e5..e4be6124784 100644 --- a/src/phases/show-ability-phase.ts +++ b/src/phases/show-ability-phase.ts @@ -36,8 +36,8 @@ export class ShowAbilityPhase extends PokemonPhase { // If the bar is already out, hide it before showing the new one if (globalScene.abilityBar.isVisible()) { - globalScene.unshiftPhase(new HideAbilityPhase()); - globalScene.unshiftPhase(new ShowAbilityPhase(this.battlerIndex, this.passive)); + globalScene.phaseManager.unshiftPhase(new HideAbilityPhase()); + globalScene.phaseManager.unshiftPhase(new ShowAbilityPhase(this.battlerIndex, this.passive)); return this.end(); } diff --git a/src/phases/show-party-exp-bar-phase.ts b/src/phases/show-party-exp-bar-phase.ts index 6b4236f0868..765bd498f66 100644 --- a/src/phases/show-party-exp-bar-phase.ts +++ b/src/phases/show-party-exp-bar-phase.ts @@ -29,9 +29,9 @@ export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase { pokemon.addExp(exp.value); const newLevel = pokemon.level; if (newLevel > lastLevel) { - globalScene.unshiftPhase(new LevelUpPhase(this.partyMemberIndex, lastLevel, newLevel)); + globalScene.phaseManager.unshiftPhase(new LevelUpPhase(this.partyMemberIndex, lastLevel, newLevel)); } - globalScene.unshiftPhase(new HidePartyExpBarPhase()); + globalScene.phaseManager.unshiftPhase(new HidePartyExpBarPhase()); pokemon.updateInfo(); if (globalScene.expParty === ExpNotification.SKIP) { diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index baa93c63099..49f3952ef01 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -72,7 +72,7 @@ export class StatStageChangePhase extends PokemonPhase { if (this.stats.length > 1) { for (let i = 0; i < this.stats.length; i++) { const stat = [this.stats[i]]; - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new StatStageChangePhase( this.battlerIndex, this.selfTarget, @@ -212,7 +212,7 @@ export class StatStageChangePhase extends PokemonPhase { if (this.showMessage) { const messages = this.getStatStageChangeMessages(filteredStats, stages.value, relLevels); for (const message of messages) { - globalScene.queueMessage(message); + globalScene.phaseManager.queueMessage(message); } } @@ -235,7 +235,7 @@ export class StatStageChangePhase extends PokemonPhase { applyPostStatStageChangeAbAttrs(PostStatStageChangeAbAttr, pokemon, filteredStats, this.stages, this.selfTarget); // Look for any other stat change phases; if this is the last one, do White Herb check - const existingPhase = globalScene.findPhase( + const existingPhase = globalScene.phaseManager.findPhase( p => p.is("StatStageChangePhase") && p.battlerIndex === this.battlerIndex, ); if (!existingPhase?.is("StatStageChangePhase")) { @@ -315,7 +315,7 @@ export class StatStageChangePhase extends PokemonPhase { let existingPhase: StatStageChangePhase; if (this.stats.length === 1) { while ( - (existingPhase = globalScene.findPhase( + (existingPhase = globalScene.phaseManager.findPhase( p => p.is("StatStageChangePhase") && p.battlerIndex === this.battlerIndex && @@ -328,13 +328,13 @@ export class StatStageChangePhase extends PokemonPhase { ) { this.stages += existingPhase.stages; - if (!globalScene.tryRemovePhase(p => p === existingPhase)) { + if (!globalScene.phaseManager.tryRemovePhase(p => p === existingPhase)) { break; } } } while ( - (existingPhase = globalScene.findPhase( + (existingPhase = globalScene.phaseManager.findPhase( p => p.is("StatStageChangePhase") && p.battlerIndex === this.battlerIndex && @@ -346,7 +346,7 @@ export class StatStageChangePhase extends PokemonPhase { ) as StatStageChangePhase) ) { this.stats.push(...existingPhase.stats); - if (!globalScene.tryRemovePhase(p => p === existingPhase)) { + if (!globalScene.phaseManager.tryRemovePhase(p => p === existingPhase)) { break; } } diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index 2cd7b122bb3..15f42e76a5a 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -57,8 +57,8 @@ export class SummonPhase extends PartyMemberPokemonPhase { if (legalIndex === -1) { console.error("Party Details:\n", party); console.error("All available Pokemon were fainted or illegal!"); - globalScene.clearPhaseQueue(); - globalScene.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.unshiftPhase(new GameOverPhase()); this.end(); return; } @@ -275,7 +275,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { const pokemon = this.getPokemon(); if (pokemon.isShiny(true)) { - globalScene.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex())); + globalScene.phaseManager.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex())); } pokemon.resetTurnData(); @@ -291,7 +291,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { } queuePostSummon(): void { - globalScene.pushPhase(new PostSummonPhase(this.getPokemon().getBattlerIndex())); + globalScene.phaseManager.pushPhase(new PostSummonPhase(this.getPokemon().getBattlerIndex())); } end() { diff --git a/src/phases/switch-phase.ts b/src/phases/switch-phase.ts index 6017aa0fa70..5f2ae55900e 100644 --- a/src/phases/switch-phase.ts +++ b/src/phases/switch-phase.ts @@ -76,9 +76,13 @@ export class SwitchPhase extends BattlePhase { if (slotIndex >= globalScene.currentBattle.getBattlerCount() && slotIndex < 6) { // Remove any pre-existing PostSummonPhase under the same field index. // Pre-existing PostSummonPhases may occur when this phase is invoked during a prompt to switch at the start of a wave. - globalScene.tryRemovePhase(p => p.is("PostSummonPhase") && p.player && p.fieldIndex === this.fieldIndex); + globalScene.phaseManager.tryRemovePhase( + p => p.is("PostSummonPhase") && p.player && p.fieldIndex === this.fieldIndex, + ); const switchType = option === PartyOption.PASS_BATON ? SwitchType.BATON_PASS : this.switchType; - globalScene.unshiftPhase(new SwitchSummonPhase(switchType, fieldIndex, slotIndex, this.doReturn)); + globalScene.phaseManager.unshiftPhase( + new SwitchSummonPhase(switchType, fieldIndex, slotIndex, this.doReturn), + ); } globalScene.ui.setMode(UiMode.MESSAGE).then(() => super.end()); }, diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index 1872dae1f1f..de8b9f3a5d9 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -265,6 +265,6 @@ export class SwitchSummonPhase extends SummonPhase { } queuePostSummon(): void { - globalScene.unshiftPhase(new PostSummonPhase(this.getPokemon().getBattlerIndex())); + globalScene.phaseManager.unshiftPhase(new PostSummonPhase(this.getPokemon().getBattlerIndex())); } } diff --git a/src/phases/tera-phase.ts b/src/phases/tera-phase.ts index 5e4ea2fe54e..5f403f5419e 100644 --- a/src/phases/tera-phase.ts +++ b/src/phases/tera-phase.ts @@ -21,7 +21,7 @@ export class TeraPhase extends BattlePhase { start() { super.start(); - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( i18next.t("battle:pokemonTerastallized", { pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), type: i18next.t(`pokemonInfo:Type.${PokemonType[this.pokemon.getTeraType()]}`), diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index aa9ae49ca8b..fb980d87359 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -124,8 +124,8 @@ export class TitlePhase extends Phase { options.push({ label: i18next.t("menu:cancel"), handler: () => { - globalScene.clearPhaseQueue(); - globalScene.pushPhase(new TitlePhase()); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.pushPhase(new TitlePhase()); super.end(); return true; }, @@ -198,9 +198,9 @@ export class TitlePhase extends Phase { initDailyRun(): void { globalScene.ui.clearText(); globalScene.ui.setMode(UiMode.SAVE_SLOT, SaveSlotUiMode.SAVE, (slotId: number) => { - globalScene.clearPhaseQueue(); + globalScene.phaseManager.clearPhaseQueue(); if (slotId === -1) { - globalScene.pushPhase(new TitlePhase()); + globalScene.phaseManager.pushPhase(new TitlePhase()); return super.end(); } globalScene.sessionSlotId = slotId; @@ -304,23 +304,23 @@ export class TitlePhase extends Phase { globalScene.arena.preloadBgm(); globalScene.gameMode = getGameMode(this.gameMode); if (this.gameMode === GameModes.CHALLENGE) { - globalScene.pushPhase(new SelectChallengePhase()); + globalScene.phaseManager.pushPhase(new SelectChallengePhase()); } else { - globalScene.pushPhase(new SelectStarterPhase()); + globalScene.phaseManager.pushPhase(new SelectStarterPhase()); } globalScene.newArena(globalScene.gameMode.getStartingBiome()); } else { globalScene.playBgm(); } - globalScene.pushPhase(new EncounterPhase(this.loaded)); + globalScene.phaseManager.pushPhase(new EncounterPhase(this.loaded)); if (this.loaded) { const availablePartyMembers = globalScene.getPokemonAllowedInBattle().length; - globalScene.pushPhase(new SummonPhase(0, true, true)); + globalScene.phaseManager.pushPhase(new SummonPhase(0, true, true)); if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.pushPhase(new SummonPhase(1, true, true)); + globalScene.phaseManager.pushPhase(new SummonPhase(1, true, true)); } if ( @@ -329,9 +329,9 @@ export class TitlePhase extends Phase { ) { const minPartySize = globalScene.currentBattle.double ? 2 : 1; if (availablePartyMembers > minPartySize) { - globalScene.pushPhase(new CheckSwitchPhase(0, globalScene.currentBattle.double)); + globalScene.phaseManager.pushPhase(new CheckSwitchPhase(0, globalScene.currentBattle.double)); if (globalScene.currentBattle.double) { - globalScene.pushPhase(new CheckSwitchPhase(1, globalScene.currentBattle.double)); + globalScene.phaseManager.pushPhase(new CheckSwitchPhase(1, globalScene.currentBattle.double)); } } } diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts index bd035248530..5b7b26d52fb 100644 --- a/src/phases/trainer-victory-phase.ts +++ b/src/phases/trainer-victory-phase.ts @@ -20,11 +20,13 @@ export class TrainerVictoryPhase extends BattlePhase { globalScene.playBgm(globalScene.currentBattle.trainer?.config.victoryBgm); - globalScene.unshiftPhase(new MoneyRewardPhase(globalScene.currentBattle.trainer?.config.moneyMultiplier!)); // TODO: is this bang correct? + globalScene.phaseManager.unshiftPhase( + new MoneyRewardPhase(globalScene.currentBattle.trainer?.config.moneyMultiplier!), + ); // TODO: is this bang correct? const modifierRewardFuncs = globalScene.currentBattle.trainer?.config.modifierRewardFuncs!; // TODO: is this bang correct? for (const modifierRewardFunc of modifierRewardFuncs) { - globalScene.unshiftPhase(new ModifierRewardPhase(modifierRewardFunc)); + globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierRewardFunc)); } const trainerType = globalScene.currentBattle.trainer?.config.trainerType!; // TODO: is this bang correct? @@ -35,7 +37,7 @@ export class TrainerVictoryPhase extends BattlePhase { globalScene.currentBattle.trainer?.config.isBoss ) { if (timedEventManager.getUpgradeUnlockedVouchers()) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new ModifierRewardPhase( [ modifierTypes.VOUCHER_PLUS, @@ -46,7 +48,7 @@ export class TrainerVictoryPhase extends BattlePhase { ), ); } else { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new ModifierRewardPhase( [modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][ vouchers[TrainerType[trainerType]].voucherType diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index 4d486c4bbd6..832f60043ce 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -25,7 +25,7 @@ export class TurnEndPhase extends FieldPhase { globalScene.currentBattle.incrementTurn(); globalScene.eventTarget.dispatchEvent(new TurnEndEvent(globalScene.currentBattle.turn)); - globalScene.hideAbilityBar(); + globalScene.phaseManager.hideAbilityBar(); const handlePokemon = (pokemon: Pokemon) => { if (!pokemon.switchOutStatus) { @@ -34,7 +34,7 @@ export class TurnEndPhase extends FieldPhase { globalScene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon); if (globalScene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) { - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new PokemonHealPhase( pokemon.getBattlerIndex(), Math.max(pokemon.getMaxHp() >> 4, 1), diff --git a/src/phases/turn-init-phase.ts b/src/phases/turn-init-phase.ts index 7f94acd3b32..61ec5fd8a71 100644 --- a/src/phases/turn-init-phase.ts +++ b/src/phases/turn-init-phase.ts @@ -22,14 +22,18 @@ export class TurnInitPhase extends FieldPhase { globalScene.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()) { - globalScene.queueMessage(i18next.t("challenges:illegalEvolution", { pokemon: p.name }), null, true); + globalScene.phaseManager.queueMessage( + i18next.t("challenges:illegalEvolution", { pokemon: p.name }), + null, + true, + ); const allowedPokemon = globalScene.getPokemonAllowedInBattle(); if (!allowedPokemon.length) { // If there are no longer any legal pokemon in the party, game over. - globalScene.clearPhaseQueue(); - globalScene.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.unshiftPhase(new GameOverPhase()); } else if ( allowedPokemon.length >= globalScene.currentBattle.getBattlerCount() || (globalScene.currentBattle.double && !allowedPokemon[0].isActive(true)) @@ -42,7 +46,7 @@ export class TurnInitPhase extends FieldPhase { p.leaveField(); } if (allowedPokemon.length === 1 && globalScene.currentBattle.double) { - globalScene.unshiftPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.unshiftPhase(new ToggleDoublePositionPhase(true)); } } }); @@ -65,11 +69,13 @@ export class TurnInitPhase extends FieldPhase { pokemon.resetTurnData(); - globalScene.pushPhase(pokemon.isPlayer() ? new CommandPhase(i) : new EnemyCommandPhase(i - BattlerIndex.ENEMY)); + globalScene.phaseManager.pushPhase( + pokemon.isPlayer() ? new CommandPhase(i) : new EnemyCommandPhase(i - BattlerIndex.ENEMY), + ); } }); - globalScene.pushPhase(new TurnStartPhase()); + globalScene.phaseManager.pushPhase(new TurnStartPhase()); this.end(); } diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index 2d009b30bf3..c07feac0888 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -153,7 +153,7 @@ export class TurnStartPhase extends FieldPhase { switch (preTurnCommand?.command) { case Command.TERA: - globalScene.pushPhase(new TeraPhase(pokemon)); + globalScene.phaseManager.pushPhase(new TeraPhase(pokemon)); } } @@ -176,11 +176,13 @@ export class TurnStartPhase extends FieldPhase { pokemon.getMoveset().find(m => m.moveId === queuedMove.move && m.ppUsed < m.getMovePp()) || new PokemonMove(queuedMove.move); if (move.getMove().hasAttr(MoveHeaderAttr)) { - globalScene.unshiftPhase(new MoveHeaderPhase(pokemon, move)); + globalScene.phaseManager.unshiftPhase(new MoveHeaderPhase(pokemon, move)); } if (pokemon.isPlayer()) { if (turnCommand.cursor === -1) { - globalScene.pushPhase(new MovePhase(pokemon, turnCommand.targets || turnCommand.move!.targets, move)); //TODO: is the bang correct here? + globalScene.phaseManager.pushPhase( + new MovePhase(pokemon, turnCommand.targets || turnCommand.move!.targets, move), + ); //TODO: is the bang correct here? } else { const playerPhase = new MovePhase( pokemon, @@ -189,10 +191,10 @@ export class TurnStartPhase extends FieldPhase { false, queuedMove.ignorePP, ); //TODO: is the bang correct here? - globalScene.pushPhase(playerPhase); + globalScene.phaseManager.pushPhase(playerPhase); } } else { - globalScene.pushPhase( + globalScene.phaseManager.pushPhase( new MovePhase( pokemon, turnCommand.targets || turnCommand.move!.targets, @@ -204,11 +206,13 @@ export class TurnStartPhase extends FieldPhase { } break; case Command.BALL: - globalScene.unshiftPhase(new AttemptCapturePhase(turnCommand.targets![0] % 2, turnCommand.cursor!)); //TODO: is the bang correct here? + globalScene.phaseManager.unshiftPhase( + new AttemptCapturePhase(turnCommand.targets![0] % 2, turnCommand.cursor!), + ); //TODO: is the bang correct here? break; case Command.POKEMON: const switchType = turnCommand.args?.[0] ? SwitchType.BATON_PASS : SwitchType.SWITCH; - globalScene.unshiftPhase( + globalScene.phaseManager.unshiftPhase( new SwitchSummonPhase(switchType, pokemon.getFieldIndex(), turnCommand.cursor!, true, pokemon.isPlayer()), ); break; @@ -233,18 +237,18 @@ export class TurnStartPhase extends FieldPhase { runningPokemon = hasRunAway !== undefined ? hasRunAway : fasterPokemon; } } - globalScene.unshiftPhase(new AttemptRunPhase(runningPokemon.getFieldIndex())); + globalScene.phaseManager.unshiftPhase(new AttemptRunPhase(runningPokemon.getFieldIndex())); break; } } - globalScene.pushPhase(new WeatherEffectPhase()); - globalScene.pushPhase(new BerryPhase()); + globalScene.phaseManager.pushPhase(new WeatherEffectPhase()); + globalScene.phaseManager.pushPhase(new BerryPhase()); /** Add a new phase to check who should be taking status damage */ - globalScene.pushPhase(new CheckStatusEffectPhase(moveOrder)); + globalScene.phaseManager.pushPhase(new CheckStatusEffectPhase(moveOrder)); - globalScene.pushPhase(new TurnEndPhase()); + globalScene.phaseManager.pushPhase(new TurnEndPhase()); /** * this.end() will call shiftPhase(), which dumps everything from PrependQueue (aka everything that is unshifted()) to the front diff --git a/src/phases/unavailable-phase.ts b/src/phases/unavailable-phase.ts index 4c4333ceb90..a6fc4a1be61 100644 --- a/src/phases/unavailable-phase.ts +++ b/src/phases/unavailable-phase.ts @@ -7,7 +7,7 @@ export class UnavailablePhase extends Phase { public readonly phaseName = "UnavailablePhase"; start(): void { globalScene.ui.setMode(UiMode.UNAVAILABLE, () => { - globalScene.unshiftPhase(new LoginPhase(true)); + globalScene.phaseManager.unshiftPhase(new LoginPhase(true)); this.end(); }); } diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index 2d21f8abc08..0a08e010720 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -51,12 +51,12 @@ export class VictoryPhase extends PokemonPhase { .getEnemyParty() .find(p => (globalScene.currentBattle.battleType === BattleType.WILD ? p.isOnField() : !p?.isFainted(true))) ) { - globalScene.pushPhase(new BattleEndPhase(true)); + globalScene.phaseManager.pushPhase(new BattleEndPhase(true)); if (globalScene.currentBattle.battleType === BattleType.TRAINER) { - globalScene.pushPhase(new TrainerVictoryPhase()); + globalScene.phaseManager.pushPhase(new TrainerVictoryPhase()); } if (globalScene.gameMode.isEndless || !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) { - globalScene.pushPhase(new EggLapsePhase()); + globalScene.phaseManager.pushPhase(new EggLapsePhase()); if (globalScene.gameMode.isClassic) { switch (globalScene.currentBattle.waveIndex) { case ClassicFixedBossWaves.RIVAL_1: @@ -64,34 +64,36 @@ export class VictoryPhase extends PokemonPhase { // Get event modifiers for this wave timedEventManager .getFixedBattleEventRewards(globalScene.currentBattle.waveIndex) - .map(r => globalScene.pushPhase(new ModifierRewardPhase(modifierTypes[r]))); + .map(r => globalScene.phaseManager.pushPhase(new ModifierRewardPhase(modifierTypes[r]))); break; case ClassicFixedBossWaves.EVIL_BOSS_2: // Should get Lock Capsule on 165 before shop phase so it can be used in the rewards shop - globalScene.pushPhase(new ModifierRewardPhase(modifierTypes.LOCK_CAPSULE)); + globalScene.phaseManager.pushPhase(new ModifierRewardPhase(modifierTypes.LOCK_CAPSULE)); break; } } if (globalScene.currentBattle.waveIndex % 10) { - globalScene.pushPhase(new SelectModifierPhase(undefined, undefined, this.getFixedBattleCustomModifiers())); + globalScene.phaseManager.pushPhase( + new SelectModifierPhase(undefined, undefined, this.getFixedBattleCustomModifiers()), + ); } else if (globalScene.gameMode.isDaily) { - globalScene.pushPhase(new ModifierRewardPhase(modifierTypes.EXP_CHARM)); + globalScene.phaseManager.pushPhase(new ModifierRewardPhase(modifierTypes.EXP_CHARM)); if ( globalScene.currentBattle.waveIndex > 10 && !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex) ) { - globalScene.pushPhase(new ModifierRewardPhase(modifierTypes.GOLDEN_POKEBALL)); + globalScene.phaseManager.pushPhase(new ModifierRewardPhase(modifierTypes.GOLDEN_POKEBALL)); } } else { const superExpWave = !globalScene.gameMode.isEndless ? (globalScene.offsetGym ? 0 : 20) : 10; if (globalScene.gameMode.isEndless && globalScene.currentBattle.waveIndex === 10) { - globalScene.pushPhase(new ModifierRewardPhase(modifierTypes.EXP_SHARE)); + globalScene.phaseManager.pushPhase(new ModifierRewardPhase(modifierTypes.EXP_SHARE)); } if ( globalScene.currentBattle.waveIndex <= 750 && (globalScene.currentBattle.waveIndex <= 500 || globalScene.currentBattle.waveIndex % 30 === superExpWave) ) { - globalScene.pushPhase( + globalScene.phaseManager.pushPhase( new ModifierRewardPhase( globalScene.currentBattle.waveIndex % 30 !== superExpWave || globalScene.currentBattle.waveIndex > 250 ? modifierTypes.EXP_CHARM @@ -100,30 +102,30 @@ export class VictoryPhase extends PokemonPhase { ); } if (globalScene.currentBattle.waveIndex <= 150 && !(globalScene.currentBattle.waveIndex % 50)) { - globalScene.pushPhase(new ModifierRewardPhase(modifierTypes.GOLDEN_POKEBALL)); + globalScene.phaseManager.pushPhase(new ModifierRewardPhase(modifierTypes.GOLDEN_POKEBALL)); } if (globalScene.gameMode.isEndless && !(globalScene.currentBattle.waveIndex % 50)) { - globalScene.pushPhase( + globalScene.phaseManager.pushPhase( new ModifierRewardPhase( !(globalScene.currentBattle.waveIndex % 250) ? modifierTypes.VOUCHER_PREMIUM : modifierTypes.VOUCHER_PLUS, ), ); - globalScene.pushPhase(new AddEnemyBuffModifierPhase()); + globalScene.phaseManager.pushPhase(new AddEnemyBuffModifierPhase()); } } if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { - globalScene.pushPhase(new SelectBiomePhase()); + globalScene.phaseManager.pushPhase(new SelectBiomePhase()); } - globalScene.pushPhase(new NewBattlePhase()); + globalScene.phaseManager.pushPhase(new NewBattlePhase()); } else { globalScene.currentBattle.battleType = BattleType.CLEAR; globalScene.score += globalScene.gameMode.getClearScoreBonus(); globalScene.updateScoreText(); - globalScene.pushPhase(new GameOverPhase(true)); + globalScene.phaseManager.pushPhase(new GameOverPhase(true)); } } diff --git a/src/phases/weather-effect-phase.ts b/src/phases/weather-effect-phase.ts index cd91b89771c..2918cd462df 100644 --- a/src/phases/weather-effect-phase.ts +++ b/src/phases/weather-effect-phase.ts @@ -65,7 +65,7 @@ export class WeatherEffectPhase extends CommonAnimPhase { const damage = toDmgValue(pokemon.getMaxHp() / 16); - globalScene.queueMessage(getWeatherDamageMessage(this.weather!.weatherType, pokemon) ?? ""); + globalScene.phaseManager.queueMessage(getWeatherDamageMessage(this.weather!.weatherType, pokemon) ?? ""); pokemon.damageAndUpdate(damage, { result: HitResult.INDIRECT, ignoreSegments: true }); }; diff --git a/src/system/game-data.ts b/src/system/game-data.ts index d9b8d73d3c3..9a24194b8e9 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -426,8 +426,8 @@ export class GameData { globalScene.ui.savingIcon.hide(); if (error) { if (error.startsWith("session out of date")) { - globalScene.clearPhaseQueue(); - globalScene.unshiftPhase(new ReloadSessionPhase()); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.unshiftPhase(new ReloadSessionPhase()); } console.error(error); return resolve(false); @@ -459,7 +459,7 @@ export class GameData { saveDataOrErr[0] !== "{" ) { if (saveDataOrErr === 404) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( "Save data could not be found. If this is a new account, you can safely ignore this message.", null, true, @@ -467,7 +467,7 @@ export class GameData { return resolve(true); } if (typeof saveDataOrErr === "string" && saveDataOrErr?.includes("Too many connections")) { - globalScene.queueMessage( + globalScene.phaseManager.queueMessage( "Too many people are trying to connect and the server is overloaded. Please try again later.", null, true, @@ -746,8 +746,8 @@ export class GameData { }); if (systemData) { - globalScene.clearPhaseQueue(); - globalScene.unshiftPhase(new ReloadSessionPhase(JSON.stringify(systemData))); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.unshiftPhase(new ReloadSessionPhase(JSON.stringify(systemData))); this.clearLocalData(); return false; } @@ -1248,8 +1248,8 @@ export class GameData { pokerogueApi.savedata.session.delete({ slot: slotId, clientSessionId }).then(error => { if (error) { if (error.startsWith("session out of date")) { - globalScene.clearPhaseQueue(); - globalScene.unshiftPhase(new ReloadSessionPhase()); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.unshiftPhase(new ReloadSessionPhase()); } console.error(error); resolve(false); @@ -1320,8 +1320,8 @@ export class GameData { localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`); } else { if (jsonResponse?.error?.startsWith("session out of date")) { - globalScene.clearPhaseQueue(); - globalScene.unshiftPhase(new ReloadSessionPhase()); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.unshiftPhase(new ReloadSessionPhase()); } console.error(jsonResponse); @@ -1458,8 +1458,8 @@ export class GameData { } if (error) { if (error.startsWith("session out of date")) { - globalScene.clearPhaseQueue(); - globalScene.unshiftPhase(new ReloadSessionPhase()); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.unshiftPhase(new ReloadSessionPhase()); } console.error(error); return resolve(false); diff --git a/src/ui/ball-ui-handler.ts b/src/ui/ball-ui-handler.ts index 66d7847213f..bb1e8d0e85e 100644 --- a/src/ui/ball-ui-handler.ts +++ b/src/ui/ball-ui-handler.ts @@ -78,7 +78,7 @@ export default class BallUiHandler extends UiHandler { const pokeballTypeCount = Object.keys(globalScene.pokeballCounts).length; if (button === Button.ACTION || button === Button.CANCEL) { - const commandPhase = globalScene.getCurrentPhase() as CommandPhase; + const commandPhase = globalScene.phaseManager.getCurrentPhase() as CommandPhase; success = true; if (button === Button.ACTION && this.cursor < pokeballTypeCount) { if (globalScene.pokeballCounts[this.cursor]) { diff --git a/src/ui/challenges-select-ui-handler.ts b/src/ui/challenges-select-ui-handler.ts index d1df16a457b..d2c2dbc6c0d 100644 --- a/src/ui/challenges-select-ui-handler.ts +++ b/src/ui/challenges-select-ui-handler.ts @@ -383,16 +383,16 @@ export default class GameChallengesUiHandler extends UiHandler { this.cursorObj?.setVisible(true); this.updateChallengeArrows(this.startCursor.visible); } else { - globalScene.clearPhaseQueue(); - globalScene.pushPhase(new TitlePhase()); - globalScene.getCurrentPhase()?.end(); + globalScene.phaseManager.clearPhaseQueue(); + globalScene.phaseManager.pushPhase(new TitlePhase()); + globalScene.phaseManager.getCurrentPhase()?.end(); } success = true; } else if (button === Button.SUBMIT || button === Button.ACTION) { if (this.hasSelectedChallenge) { if (this.startCursor.visible) { - globalScene.unshiftPhase(new SelectStarterPhase()); - globalScene.getCurrentPhase()?.end(); + globalScene.phaseManager.unshiftPhase(new SelectStarterPhase()); + globalScene.phaseManager.getCurrentPhase()?.end(); } else { this.startCursor.setVisible(true); this.cursorObj?.setVisible(false); diff --git a/src/ui/command-ui-handler.ts b/src/ui/command-ui-handler.ts index 0d67fdea624..0d38672323a 100644 --- a/src/ui/command-ui-handler.ts +++ b/src/ui/command-ui-handler.ts @@ -74,11 +74,11 @@ export default class CommandUiHandler extends UiHandler { this.commandsContainer.setVisible(true); let commandPhase: CommandPhase; - const currentPhase = globalScene.getCurrentPhase(); + const currentPhase = globalScene.phaseManager.getCurrentPhase(); if (currentPhase?.is("CommandPhase")) { commandPhase = currentPhase; } else { - commandPhase = globalScene.getStandbyPhase() as CommandPhase; + commandPhase = globalScene.phaseManager.getStandbyPhase() as CommandPhase; } if (this.canTera()) { @@ -124,7 +124,7 @@ export default class CommandUiHandler extends UiHandler { switch (cursor) { // Fight case Command.FIGHT: - ui.setMode(UiMode.FIGHT, (globalScene.getCurrentPhase() as CommandPhase).getFieldIndex()); + ui.setMode(UiMode.FIGHT, (globalScene.phaseManager.getCurrentPhase() as CommandPhase).getFieldIndex()); success = true; break; // Ball @@ -137,7 +137,7 @@ export default class CommandUiHandler extends UiHandler { ui.setMode( UiMode.PARTY, PartyUiMode.SWITCH, - (globalScene.getCurrentPhase() as CommandPhase).getPokemon().getFieldIndex(), + (globalScene.phaseManager.getCurrentPhase() as CommandPhase).getPokemon().getFieldIndex(), null, PartyUiHandler.FilterNonFainted, ); @@ -145,16 +145,20 @@ export default class CommandUiHandler extends UiHandler { break; // Run case Command.RUN: - (globalScene.getCurrentPhase() as CommandPhase).handleCommand(Command.RUN, 0); + (globalScene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.RUN, 0); success = true; break; case Command.TERA: - ui.setMode(UiMode.FIGHT, (globalScene.getCurrentPhase() as CommandPhase).getFieldIndex(), Command.TERA); + ui.setMode( + UiMode.FIGHT, + (globalScene.phaseManager.getCurrentPhase() as CommandPhase).getFieldIndex(), + Command.TERA, + ); success = true; break; } } else { - (globalScene.getCurrentPhase() as CommandPhase).cancel(); + (globalScene.phaseManager.getCurrentPhase() as CommandPhase).cancel(); } } else { switch (button) { diff --git a/src/ui/egg-hatch-scene-handler.ts b/src/ui/egg-hatch-scene-handler.ts index b2863a213f8..85c4199ff1d 100644 --- a/src/ui/egg-hatch-scene-handler.ts +++ b/src/ui/egg-hatch-scene-handler.ts @@ -44,7 +44,7 @@ export default class EggHatchSceneHandler extends UiHandler { processInput(button: Button): boolean { if (button === Button.ACTION || button === Button.CANCEL) { - const phase = globalScene.getCurrentPhase(); + const phase = globalScene.phaseManager.getCurrentPhase(); if (phase?.is("EggHatchPhase") && phase.trySkip()) { return true; } diff --git a/src/ui/egg-summary-ui-handler.ts b/src/ui/egg-summary-ui-handler.ts index ed2506ba0c1..90dc60e16b7 100644 --- a/src/ui/egg-summary-ui-handler.ts +++ b/src/ui/egg-summary-ui-handler.ts @@ -221,7 +221,7 @@ export default class EggSummaryUiHandler extends MessageUiHandler { let error = false; if (button === Button.CANCEL) { if (!this.blockExit) { - const phase = globalScene.getCurrentPhase(); + const phase = globalScene.phaseManager.getCurrentPhase(); if (phase?.is("EggSummaryPhase")) { phase.end(); } diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index 208e627023b..3fe06cdf039 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -126,7 +126,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { messageHandler.bg.setVisible(false); messageHandler.commandWindow.setVisible(false); messageHandler.movesWindowContainer.setVisible(true); - const pokemon = (globalScene.getCurrentPhase() as CommandPhase).getPokemon(); + const pokemon = (globalScene.phaseManager.getCurrentPhase() as CommandPhase).getPokemon(); if (pokemon.tempSummonData.turnCount <= 1) { this.setCursor(0); } else { @@ -147,7 +147,9 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { if (button === Button.CANCEL || button === Button.ACTION) { if (button === Button.ACTION) { - if ((globalScene.getCurrentPhase() as CommandPhase).handleCommand(this.fromCommand, cursor, false)) { + if ( + (globalScene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(this.fromCommand, cursor, false) + ) { success = true; } else { ui.playError(); @@ -237,7 +239,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { ui.add(this.cursorObj); } - const pokemon = (globalScene.getCurrentPhase() as CommandPhase).getPokemon(); + const pokemon = (globalScene.phaseManager.getCurrentPhase() as CommandPhase).getPokemon(); const moveset = pokemon.getMoveset(); const hasMove = cursor < moveset.length; @@ -318,7 +320,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { } displayMoves() { - const pokemon = (globalScene.getCurrentPhase() as CommandPhase).getPokemon(); + const pokemon = (globalScene.phaseManager.getCurrentPhase() as CommandPhase).getPokemon(); const moveset = pokemon.getMoveset(); for (let moveIndex = 0; moveIndex < 4; moveIndex++) { @@ -389,7 +391,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { clearMoves() { this.movesContainer.removeAll(true); - const opponents = (globalScene.getCurrentPhase() as CommandPhase).getPokemon().getOpponents(); + const opponents = (globalScene.phaseManager.getCurrentPhase() as CommandPhase).getPokemon().getOpponents(); opponents.forEach(opponent => { (opponent as EnemyPokemon).updateEffectiveness(); }); diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index b6c3a9d7b8e..e68cc706aba 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -125,7 +125,7 @@ export default class MenuUiHandler extends MessageUiHandler { const ui = this.getUi(); this.excludedMenus = () => [ { - condition: !!globalScene.getCurrentPhase()?.is("SelectModifierPhase"), + condition: !!globalScene.phaseManager.getCurrentPhase()?.is("SelectModifierPhase"), options: [MenuOptions.EGG_GACHA], }, { condition: bypassLogin, options: [MenuOptions.LOG_OUT] }, diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 0866ed8788e..83ce88714f5 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -156,7 +156,9 @@ export default class MysteryEncounterUiHandler extends UiHandler { ) { success = false; } else { - if ((globalScene.getCurrentPhase() as MysteryEncounterPhase).handleOptionSelect(selected, cursor)) { + if ( + (globalScene.phaseManager.getCurrentPhase() as MysteryEncounterPhase).handleOptionSelect(selected, cursor) + ) { success = true; } else { ui.playError(); diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index e26ecb531e9..6ce192751df 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -750,7 +750,7 @@ export default class PartyUiHandler extends MessageUiHandler { // TODO: This risks hitting the other options (.MOVE_i and ALL) so does it? Do we need an extra check? if ( option >= PartyOption.FORM_CHANGE_ITEM && - globalScene.getCurrentPhase()?.is("SelectModifierPhase") && + globalScene.phaseManager.getCurrentPhase()?.is("SelectModifierPhase") && this.partyUiMode === PartyUiMode.CHECK ) { const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); @@ -804,7 +804,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.partyUiMode === PartyUiMode.SWITCH ) { this.clearOptions(); - (globalScene.getCurrentPhase() as CommandPhase).handleCommand( + (globalScene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand( Command.POKEMON, this.cursor, option === PartyOption.PASS_BATON, @@ -1337,7 +1337,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.addCommonOptions(pokemon); break; case PartyUiMode.CHECK: - if (globalScene.getCurrentPhase()?.is("SelectModifierPhase")) { + if (globalScene.phaseManager.getCurrentPhase()?.is("SelectModifierPhase")) { const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); for (let i = 0; i < formChangeItemModifiers.length; i++) { this.options.push(PartyOption.FORM_CHANGE_ITEM + i); diff --git a/src/ui/pokedex-page-ui-handler.ts b/src/ui/pokedex-page-ui-handler.ts index 5875e3394a2..a81265e1a55 100644 --- a/src/ui/pokedex-page-ui-handler.ts +++ b/src/ui/pokedex-page-ui-handler.ts @@ -666,7 +666,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { show(args: any[]): boolean { // Allow the use of candies if we are in one of the whitelisted phases this.canUseCandies = ["TitlePhase", "SelectStarterPhase", "CommandPhase"].includes( - globalScene.getCurrentPhase()?.phaseName ?? "", + globalScene.phaseManager.getCurrentPhase()?.phaseName ?? "", ); if (args.length >= 1 && args[0] === "refresh") { diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index b7a7e0f3e6e..973805e4ca1 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -4305,15 +4305,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler { UiMode.CONFIRM, () => { ui.setMode(UiMode.STARTER_SELECT); - globalScene.clearPhaseQueue(); + globalScene.phaseManager.clearPhaseQueue(); if (globalScene.gameMode.isChallenge) { - globalScene.pushPhase(new SelectChallengePhase()); - globalScene.pushPhase(new EncounterPhase()); + globalScene.phaseManager.pushPhase(new SelectChallengePhase()); + globalScene.phaseManager.pushPhase(new EncounterPhase()); } else { - globalScene.pushPhase(new TitlePhase()); + globalScene.phaseManager.pushPhase(new TitlePhase()); } this.clearText(); - globalScene.getCurrentPhase()?.end(); + globalScene.phaseManager.getCurrentPhase()?.end(); }, cancel, null, diff --git a/test/abilities/cud_chew.test.ts b/test/abilities/cud_chew.test.ts index 3c918f01330..8a80c6625cc 100644 --- a/test/abilities/cud_chew.test.ts +++ b/test/abilities/cud_chew.test.ts @@ -67,7 +67,7 @@ describe("Abilities - Cud Chew", () => { }); it("shows ability popup for eating berry, even if berry is useless", async () => { - const abDisplaySpy = vi.spyOn(globalScene, "queueAbilityDisplay"); + const abDisplaySpy = vi.spyOn(globalScene.phaseManager, "queueAbilityDisplay"); game.override.enemyMoveset([MoveId.SPLASH, MoveId.HEAL_PULSE]); await game.classicMode.startBattle([SpeciesId.FARIGIRAF]); @@ -89,7 +89,7 @@ describe("Abilities - Cud Chew", () => { await game.move.selectEnemyMove(MoveId.HEAL_PULSE); await game.phaseInterceptor.to("TurnEndPhase"); - // globalScene.queueAbilityDisplay should be called twice: + // globalScene.phaseManager.queueAbilityDisplay should be called twice: // once to show cud chew text before regurgitating berries, // once to hide ability text after finishing. expect(abDisplaySpy).toBeCalledTimes(2); diff --git a/test/abilities/dancer.test.ts b/test/abilities/dancer.test.ts index 086c69300b4..ae702e4b1e7 100644 --- a/test/abilities/dancer.test.ts +++ b/test/abilities/dancer.test.ts @@ -44,7 +44,7 @@ describe("Abilities - Dancer", () => { await game.phaseInterceptor.to("MovePhase"); // feebas uses swords dance await game.phaseInterceptor.to("MovePhase", false); // oricorio copies swords dance - let currentPhase = game.scene.getCurrentPhase() as MovePhase; + let currentPhase = game.scene.phaseManager.getCurrentPhase() as MovePhase; expect(currentPhase.pokemon).toBe(oricorio); expect(currentPhase.move.moveId).toBe(MoveId.SWORDS_DANCE); @@ -54,7 +54,7 @@ describe("Abilities - Dancer", () => { await game.phaseInterceptor.to("MovePhase"); // magikarp (left) uses victory dance await game.phaseInterceptor.to("MovePhase", false); // oricorio copies magikarp's victory dance - currentPhase = game.scene.getCurrentPhase() as MovePhase; + currentPhase = game.scene.phaseManager.getCurrentPhase() as MovePhase; expect(currentPhase.pokemon).toBe(oricorio); expect(currentPhase.move.moveId).toBe(MoveId.VICTORY_DANCE); @@ -91,13 +91,13 @@ describe("Abilities - Dancer", () => { await game.phaseInterceptor.to("MovePhase"); // shuckle 2 mirror moves oricorio await game.phaseInterceptor.to("MovePhase"); // calls instructed rev dance - let currentPhase = game.scene.getCurrentPhase() as MovePhase; + let currentPhase = game.scene.phaseManager.getCurrentPhase() as MovePhase; expect(currentPhase.pokemon).toBe(shuckle2); expect(currentPhase.move.moveId).toBe(MoveId.REVELATION_DANCE); await game.phaseInterceptor.to("MovePhase"); // shuckle 1 instructs oricorio await game.phaseInterceptor.to("MovePhase"); - currentPhase = game.scene.getCurrentPhase() as MovePhase; + currentPhase = game.scene.phaseManager.getCurrentPhase() as MovePhase; expect(currentPhase.pokemon).toBe(oricorio); expect(currentPhase.move.moveId).toBe(MoveId.REVELATION_DANCE); }); diff --git a/test/abilities/desolate-land.test.ts b/test/abilities/desolate-land.test.ts index c5238a40762..bcd980187ac 100644 --- a/test/abilities/desolate-land.test.ts +++ b/test/abilities/desolate-land.test.ts @@ -146,7 +146,7 @@ describe("Abilities - Desolate Land", () => { vi.spyOn(game.scene.getPlayerPokemon()!, "randBattleSeedInt").mockReturnValue(0); - const commandPhase = game.scene.getCurrentPhase() as CommandPhase; + const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); await game.phaseInterceptor.to("BerryPhase"); diff --git a/test/abilities/disguise.test.ts b/test/abilities/disguise.test.ts index dd05c540620..15ded41527a 100644 --- a/test/abilities/disguise.test.ts +++ b/test/abilities/disguise.test.ts @@ -201,7 +201,7 @@ describe("Abilities - Disguise", () => { game.move.select(MoveId.SHADOW_SNEAK); await game.toNextWave(); - expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); + expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); expect(game.scene.currentBattle.waveIndex).toBe(2); }); diff --git a/test/abilities/honey_gather.test.ts b/test/abilities/honey_gather.test.ts index e2f87f0af37..a069654ae21 100644 --- a/test/abilities/honey_gather.test.ts +++ b/test/abilities/honey_gather.test.ts @@ -64,7 +64,7 @@ describe("Abilities - Honey Gather", () => { const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "scene", "get").mockReturnValue(game.scene); - const commandPhase = game.scene.getCurrentPhase() as CommandPhase; + const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); await game.toNextTurn(); diff --git a/test/abilities/imposter.test.ts b/test/abilities/imposter.test.ts index c27e679ec54..e9ae6bd64bb 100644 --- a/test/abilities/imposter.test.ts +++ b/test/abilities/imposter.test.ts @@ -140,7 +140,7 @@ describe("Abilities - Imposter", () => { await game.doKillOpponents(); await game.toNextWave(); - expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); + expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); expect(game.scene.currentBattle.waveIndex).toBe(2); await game.reload.reloadSession(); @@ -176,7 +176,7 @@ describe("Abilities - Imposter", () => { await game.doKillOpponents(); await game.toNextWave(); - expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); + expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); expect(game.scene.currentBattle.waveIndex).toBe(2); await game.reload.reloadSession(); diff --git a/test/abilities/mycelium_might.test.ts b/test/abilities/mycelium_might.test.ts index 6e4da4bc933..1f236f2c2fe 100644 --- a/test/abilities/mycelium_might.test.ts +++ b/test/abilities/mycelium_might.test.ts @@ -52,7 +52,7 @@ describe("Abilities - Mycelium Might", () => { game.move.select(MoveId.BABY_DOLL_EYES); await game.phaseInterceptor.to(TurnStartPhase, false); - const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const speedOrder = phase.getSpeedOrder(); const commandOrder = phase.getCommandOrder(); // The opponent Pokemon (without Mycelium Might) goes first despite having lower speed than the player Pokemon. @@ -76,7 +76,7 @@ describe("Abilities - Mycelium Might", () => { game.move.select(MoveId.BABY_DOLL_EYES); await game.phaseInterceptor.to(TurnStartPhase, false); - const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const speedOrder = phase.getSpeedOrder(); const commandOrder = phase.getCommandOrder(); // The player Pokemon (with M.M.) goes first because its move is still within a higher priority bracket than its opponent. @@ -97,7 +97,7 @@ describe("Abilities - Mycelium Might", () => { game.move.select(MoveId.QUICK_ATTACK); await game.phaseInterceptor.to(TurnStartPhase, false); - const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const speedOrder = phase.getSpeedOrder(); const commandOrder = phase.getCommandOrder(); // The player Pokemon (with M.M.) goes first because it has a higher speed and did not use a status move. diff --git a/test/abilities/neutralizing_gas.test.ts b/test/abilities/neutralizing_gas.test.ts index 7c78cc9db64..2d1fef29688 100644 --- a/test/abilities/neutralizing_gas.test.ts +++ b/test/abilities/neutralizing_gas.test.ts @@ -166,7 +166,7 @@ describe("Abilities - Neutralizing Gas", () => { vi.spyOn(game.scene.getPlayerPokemon()!, "randBattleSeedInt").mockReturnValue(0); - const commandPhase = game.scene.getCurrentPhase() as CommandPhase; + const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); await game.phaseInterceptor.to("BerryPhase"); diff --git a/test/abilities/no_guard.test.ts b/test/abilities/no_guard.test.ts index 1cb5945e9ff..5b127204f9f 100644 --- a/test/abilities/no_guard.test.ts +++ b/test/abilities/no_guard.test.ts @@ -45,7 +45,7 @@ describe("Abilities - No Guard", () => { await game.phaseInterceptor.to(MoveEffectPhase, false); - const moveEffectPhase = game.scene.getCurrentPhase() as MoveEffectPhase; + const moveEffectPhase = game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase; vi.spyOn(moveEffectPhase, "hitCheck"); await game.phaseInterceptor.to(MoveEndPhase); diff --git a/test/abilities/shield_dust.test.ts b/test/abilities/shield_dust.test.ts index e99b7563cb7..03b175c3ca5 100644 --- a/test/abilities/shield_dust.test.ts +++ b/test/abilities/shield_dust.test.ts @@ -51,7 +51,7 @@ describe("Abilities - Shield Dust", () => { await game.phaseInterceptor.to(MoveEffectPhase, false); // Shield Dust negates secondary effect - const phase = game.scene.getCurrentPhase() as MoveEffectPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase; const move = phase.move; expect(move.id).toBe(MoveId.AIR_SLASH); diff --git a/test/abilities/speed_boost.test.ts b/test/abilities/speed_boost.test.ts index 9890f22ffcd..245bf22345d 100644 --- a/test/abilities/speed_boost.test.ts +++ b/test/abilities/speed_boost.test.ts @@ -98,9 +98,9 @@ describe("Abilities - Speed Boost", () => { it("should not trigger if pokemon fails to escape", async () => { await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - const commandPhase = game.scene.getCurrentPhase() as CommandPhase; + const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); - const runPhase = game.scene.getCurrentPhase() as AttemptRunPhase; + const runPhase = game.scene.phaseManager.getCurrentPhase() as AttemptRunPhase; runPhase.forceFailEscape = true; await game.phaseInterceptor.to(AttemptRunPhase); await game.toNextTurn(); diff --git a/test/abilities/stall.test.ts b/test/abilities/stall.test.ts index 78e7d49b48b..df40bed3e90 100644 --- a/test/abilities/stall.test.ts +++ b/test/abilities/stall.test.ts @@ -46,7 +46,7 @@ describe("Abilities - Stall", () => { game.move.select(MoveId.QUICK_ATTACK); await game.phaseInterceptor.to(TurnStartPhase, false); - const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const speedOrder = phase.getSpeedOrder(); const commandOrder = phase.getCommandOrder(); // The player Pokemon (without Stall) goes first despite having lower speed than the opponent. @@ -64,7 +64,7 @@ describe("Abilities - Stall", () => { game.move.select(MoveId.TACKLE); await game.phaseInterceptor.to(TurnStartPhase, false); - const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const speedOrder = phase.getSpeedOrder(); const commandOrder = phase.getCommandOrder(); // The opponent Pokemon (with Stall) goes first because its move is still within a higher priority bracket than its opponent. @@ -83,7 +83,7 @@ describe("Abilities - Stall", () => { game.move.select(MoveId.TACKLE); await game.phaseInterceptor.to(TurnStartPhase, false); - const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const speedOrder = phase.getSpeedOrder(); const commandOrder = phase.getCommandOrder(); diff --git a/test/battle/battle-order.test.ts b/test/battle/battle-order.test.ts index 7983f1db1d2..c12760a7f30 100644 --- a/test/battle/battle-order.test.ts +++ b/test/battle/battle-order.test.ts @@ -44,7 +44,7 @@ describe("Battle order", () => { const playerPokemonIndex = playerPokemon.getBattlerIndex(); const enemyPokemonIndex = enemyPokemon.getBattlerIndex(); - const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const order = phase.getCommandOrder(); expect(order[0]).toBe(enemyPokemonIndex); expect(order[1]).toBe(playerPokemonIndex); @@ -63,7 +63,7 @@ describe("Battle order", () => { const playerPokemonIndex = playerPokemon.getBattlerIndex(); const enemyPokemonIndex = enemyPokemon.getBattlerIndex(); - const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const order = phase.getCommandOrder(); expect(order[0]).toBe(playerPokemonIndex); expect(order[1]).toBe(enemyPokemonIndex); @@ -85,7 +85,7 @@ describe("Battle order", () => { game.move.select(MoveId.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); - const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const order = phase.getCommandOrder(); expect(order.slice(0, 2).includes(enemyIndices[0])).toBe(true); expect(order.slice(0, 2).includes(enemyIndices[1])).toBe(true); @@ -109,7 +109,7 @@ describe("Battle order", () => { game.move.select(MoveId.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); - const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const order = phase.getCommandOrder(); expect(order[0]).toBe(enemyIndices[1]); expect(order.slice(1, 4).includes(enemyIndices[0])).toBe(true); @@ -134,7 +134,7 @@ describe("Battle order", () => { game.move.select(MoveId.TACKLE, 1); await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); - const phase = game.scene.getCurrentPhase() as TurnStartPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const order = phase.getCommandOrder(); expect(order.slice(0, 2).includes(playerIndices[1])).toBe(true); expect(order.slice(0, 2).includes(enemyIndices[1])).toBe(true); diff --git a/test/battle/battle.test.ts b/test/battle/battle.test.ts index 03faf369dd7..b93c913d09a 100644 --- a/test/battle/battle.test.ts +++ b/test/battle/battle.test.ts @@ -87,7 +87,7 @@ describe("Test Battle Phase", () => { it("newGame one-liner", async () => { await game.classicMode.startBattle(); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("do attack wave 3 - single battle - regular - OHKO", async () => { @@ -196,7 +196,7 @@ describe("Test Battle Phase", () => { game.scene.gameMode = getGameMode(GameModes.CLASSIC); const starters = generateStarter(game.scene); const selectStarterPhase = new SelectStarterPhase(); - game.scene.pushPhase(new EncounterPhase(false)); + game.scene.phaseManager.pushPhase(new EncounterPhase(false)); selectStarterPhase.initBattle(starters); }); await game.phaseInterceptor.runFrom(SelectGenderPhase).to(SummonPhase); @@ -209,7 +209,7 @@ describe("Test Battle Phase", () => { game.override.ability(AbilityId.HYDRATION); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("1vs1", async () => { @@ -219,7 +219,7 @@ describe("Test Battle Phase", () => { game.override.ability(AbilityId.HYDRATION); await game.classicMode.startBattle([SpeciesId.BLASTOISE]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("2vs2", async () => { @@ -230,7 +230,7 @@ describe("Test Battle Phase", () => { game.override.startingWave(3); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("4vs2", async () => { @@ -241,7 +241,7 @@ describe("Test Battle Phase", () => { game.override.startingWave(3); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD, SpeciesId.DARKRAI, SpeciesId.GABITE]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("kill opponent pokemon", async () => { diff --git a/test/battle/special_battle.test.ts b/test/battle/special_battle.test.ts index c1a948a0759..f0c77668093 100644 --- a/test/battle/special_battle.test.ts +++ b/test/battle/special_battle.test.ts @@ -36,62 +36,62 @@ describe("Test Battle Phase", () => { game.override.battleStyle("single").startingWave(10); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 boss", async () => { game.override.battleStyle("double").startingWave(10); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 trainer", async () => { game.override.battleStyle("double").startingWave(5); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs1 trainer", async () => { game.override.battleStyle("single").startingWave(5); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs1 rival", async () => { game.override.battleStyle("single").startingWave(8); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 rival", async () => { game.override.battleStyle("double").startingWave(8); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 1vs1 trainer", async () => { game.override.battleStyle("single").startingWave(5); await game.classicMode.startBattle([SpeciesId.BLASTOISE]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 trainer", async () => { game.override.battleStyle("double").startingWave(5); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 4vs2 trainer", async () => { game.override.battleStyle("double").startingWave(5); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD, SpeciesId.DARKRAI, SpeciesId.GABITE]); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); }); diff --git a/test/battlerTags/octolock.test.ts b/test/battlerTags/octolock.test.ts index 6189bd7febe..63784ed7f1b 100644 --- a/test/battlerTags/octolock.test.ts +++ b/test/battlerTags/octolock.test.ts @@ -28,7 +28,7 @@ describe("BattlerTag - OctolockTag", () => { const subject = new OctolockTag(1); - vi.spyOn(game.scene, "unshiftPhase").mockImplementation(phase => { + vi.spyOn(game.scene.phaseManager, "unshiftPhase").mockImplementation(phase => { expect(phase).toBeInstanceOf(StatStageChangePhase); expect((phase as StatStageChangePhase)["stages"]).toEqual(-1); expect((phase as StatStageChangePhase)["stats"]).toEqual([Stat.DEF, Stat.SPDEF]); @@ -36,7 +36,7 @@ describe("BattlerTag - OctolockTag", () => { subject.lapse(mockPokemon, BattlerTagLapseType.TURN_END); - expect(game.scene.unshiftPhase).toBeCalledTimes(1); + expect(game.scene.phaseManager.unshiftPhase).toBeCalledTimes(1); }); }); diff --git a/test/battlerTags/stockpiling.test.ts b/test/battlerTags/stockpiling.test.ts index 20fade13d92..37873db9eab 100644 --- a/test/battlerTags/stockpiling.test.ts +++ b/test/battlerTags/stockpiling.test.ts @@ -32,11 +32,11 @@ describe("BattlerTag - StockpilingTag", () => { getBattlerIndex: () => 0, } as Pokemon; - vi.spyOn(game.scene, "queueMessage").mockImplementation(() => {}); + vi.spyOn(game.scene.phaseManager, "queueMessage").mockImplementation(() => {}); const subject = new StockpilingTag(1); - vi.spyOn(game.scene, "unshiftPhase").mockImplementation(phase => { + vi.spyOn(game.scene.phaseManager, "unshiftPhase").mockImplementation(phase => { expect(phase).toBeInstanceOf(StatStageChangePhase); expect((phase as StatStageChangePhase)["stages"]).toEqual(1); expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([Stat.DEF, Stat.SPDEF])); @@ -46,7 +46,7 @@ describe("BattlerTag - StockpilingTag", () => { subject.onAdd(mockPokemon); - expect(game.scene.unshiftPhase).toBeCalledTimes(1); + expect(game.scene.phaseManager.unshiftPhase).toBeCalledTimes(1); }); it("unshifts a StatStageChangePhase with expected stat changes on add (one stat maxed)", async () => { @@ -55,14 +55,14 @@ describe("BattlerTag - StockpilingTag", () => { getBattlerIndex: () => 0, } as unknown as Pokemon; - vi.spyOn(game.scene, "queueMessage").mockImplementation(() => {}); + vi.spyOn(game.scene.phaseManager, "queueMessage").mockImplementation(() => {}); mockPokemon.summonData.statStages[Stat.DEF - 1] = 6; mockPokemon.summonData.statStages[Stat.SPD - 1] = 5; const subject = new StockpilingTag(1); - vi.spyOn(game.scene, "unshiftPhase").mockImplementation(phase => { + vi.spyOn(game.scene.phaseManager, "unshiftPhase").mockImplementation(phase => { expect(phase).toBeInstanceOf(StatStageChangePhase); expect((phase as StatStageChangePhase)["stages"]).toEqual(1); expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([Stat.DEF, Stat.SPDEF])); @@ -72,7 +72,7 @@ describe("BattlerTag - StockpilingTag", () => { subject.onAdd(mockPokemon); - expect(game.scene.unshiftPhase).toBeCalledTimes(1); + expect(game.scene.phaseManager.unshiftPhase).toBeCalledTimes(1); }); }); @@ -82,11 +82,11 @@ describe("BattlerTag - StockpilingTag", () => { getBattlerIndex: () => 0, } as Pokemon; - vi.spyOn(game.scene, "queueMessage").mockImplementation(() => {}); + vi.spyOn(game.scene.phaseManager, "queueMessage").mockImplementation(() => {}); const subject = new StockpilingTag(1); - vi.spyOn(game.scene, "unshiftPhase").mockImplementation(phase => { + vi.spyOn(game.scene.phaseManager, "unshiftPhase").mockImplementation(phase => { expect(phase).toBeInstanceOf(StatStageChangePhase); expect((phase as StatStageChangePhase)["stages"]).toEqual(1); expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([Stat.DEF, Stat.SPDEF])); @@ -96,7 +96,7 @@ describe("BattlerTag - StockpilingTag", () => { subject.onOverlap(mockPokemon); - expect(game.scene.unshiftPhase).toBeCalledTimes(1); + expect(game.scene.phaseManager.unshiftPhase).toBeCalledTimes(1); }); }); @@ -107,14 +107,14 @@ describe("BattlerTag - StockpilingTag", () => { getBattlerIndex: () => 0, } as Pokemon; - vi.spyOn(game.scene, "queueMessage").mockImplementation(() => {}); + vi.spyOn(game.scene.phaseManager, "queueMessage").mockImplementation(() => {}); mockPokemon.summonData.statStages[Stat.DEF - 1] = 5; mockPokemon.summonData.statStages[Stat.SPD - 1] = 4; const subject = new StockpilingTag(1); - vi.spyOn(game.scene, "unshiftPhase").mockImplementationOnce(phase => { + vi.spyOn(game.scene.phaseManager, "unshiftPhase").mockImplementationOnce(phase => { expect(phase).toBeInstanceOf(StatStageChangePhase); expect((phase as StatStageChangePhase)["stages"]).toEqual(1); expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([Stat.DEF, Stat.SPDEF])); @@ -126,7 +126,7 @@ describe("BattlerTag - StockpilingTag", () => { subject.onAdd(mockPokemon); expect(subject.stockpiledCount).toBe(1); - vi.spyOn(game.scene, "unshiftPhase").mockImplementationOnce(phase => { + vi.spyOn(game.scene.phaseManager, "unshiftPhase").mockImplementationOnce(phase => { expect(phase).toBeInstanceOf(StatStageChangePhase); expect((phase as StatStageChangePhase)["stages"]).toEqual(1); expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([Stat.DEF, Stat.SPDEF])); @@ -138,7 +138,7 @@ describe("BattlerTag - StockpilingTag", () => { subject.onOverlap(mockPokemon); expect(subject.stockpiledCount).toBe(2); - vi.spyOn(game.scene, "unshiftPhase").mockImplementationOnce(phase => { + vi.spyOn(game.scene.phaseManager, "unshiftPhase").mockImplementationOnce(phase => { expect(phase).toBeInstanceOf(StatStageChangePhase); expect((phase as StatStageChangePhase)["stages"]).toEqual(1); expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([Stat.DEF, Stat.SPDEF])); @@ -149,7 +149,7 @@ describe("BattlerTag - StockpilingTag", () => { subject.onOverlap(mockPokemon); expect(subject.stockpiledCount).toBe(3); - vi.spyOn(game.scene, "unshiftPhase").mockImplementationOnce(_phase => { + vi.spyOn(game.scene.phaseManager, "unshiftPhase").mockImplementationOnce(_phase => { throw new Error("Should not be called a fourth time"); }); @@ -162,14 +162,14 @@ describe("BattlerTag - StockpilingTag", () => { }); // removing tag should reverse stat changes - vi.spyOn(game.scene, "unshiftPhase").mockImplementationOnce(phase => { + vi.spyOn(game.scene.phaseManager, "unshiftPhase").mockImplementationOnce(phase => { expect(phase).toBeInstanceOf(StatStageChangePhase); expect((phase as StatStageChangePhase)["stages"]).toEqual(-2); expect((phase as StatStageChangePhase)["stats"]).toEqual(expect.arrayContaining([Stat.SPDEF])); }); subject.onRemove(mockPokemon); - expect(game.scene.unshiftPhase).toHaveBeenCalledOnce(); // note that re-spying each add/overlap has been refreshing call count + expect(game.scene.phaseManager.unshiftPhase).toHaveBeenCalledOnce(); // note that re-spying each add/overlap has been refreshing call count }); }); }); diff --git a/test/battlerTags/substitute.test.ts b/test/battlerTags/substitute.test.ts index ce16cfdbcd9..703e0ae75f6 100644 --- a/test/battlerTags/substitute.test.ts +++ b/test/battlerTags/substitute.test.ts @@ -55,7 +55,7 @@ describe("BattlerTag - SubstituteTag", () => { const subject = new SubstituteTag(MoveId.SUBSTITUTE, mockPokemon.id); vi.spyOn(mockPokemon.scene as BattleScene, "triggerPokemonBattleAnim").mockReturnValue(true); - vi.spyOn(mockPokemon.scene as BattleScene, "queueMessage").mockReturnValue(); + vi.spyOn((mockPokemon.scene as BattleScene).phaseManager, "queueMessage").mockReturnValue(); subject.onAdd(mockPokemon); @@ -72,19 +72,19 @@ describe("BattlerTag - SubstituteTag", () => { }, ); - vi.spyOn(mockPokemon.scene as BattleScene, "queueMessage").mockReturnValue(); + const msgSpy = vi.spyOn((mockPokemon.scene as BattleScene).phaseManager, "queueMessage").mockReturnValue(); subject.onAdd(mockPokemon); expect(subject.sourceInFocus).toBeFalsy(); expect((mockPokemon.scene as BattleScene).triggerPokemonBattleAnim).toHaveBeenCalledTimes(1); - expect((mockPokemon.scene as BattleScene).queueMessage).toHaveBeenCalledTimes(1); + expect(msgSpy).toHaveBeenCalledOnce(); }); it("removes effects that trap the source", async () => { const subject = new SubstituteTag(MoveId.SUBSTITUTE, mockPokemon.id); - vi.spyOn(mockPokemon.scene as BattleScene, "queueMessage").mockReturnValue(); + vi.spyOn((mockPokemon.scene as BattleScene).phaseManager, "queueMessage").mockReturnValue(); subject.onAdd(mockPokemon); expect(mockPokemon.findAndRemoveTags).toHaveBeenCalledTimes(1); @@ -114,12 +114,12 @@ describe("BattlerTag - SubstituteTag", () => { }, ); - vi.spyOn(mockPokemon.scene as BattleScene, "queueMessage").mockReturnValue(); + const msgSpy = vi.spyOn((mockPokemon.scene as BattleScene).phaseManager, "queueMessage").mockReturnValue(); subject.onRemove(mockPokemon); expect((mockPokemon.scene as BattleScene).triggerPokemonBattleAnim).toHaveBeenCalledTimes(1); - expect((mockPokemon.scene as BattleScene).queueMessage).toHaveBeenCalledTimes(1); + expect(msgSpy).toHaveBeenCalledOnce(); }); }); @@ -150,13 +150,12 @@ describe("BattlerTag - SubstituteTag", () => { }, ); - vi.spyOn(mockPokemon.scene as BattleScene, "queueMessage").mockReturnValue(); + vi.spyOn((mockPokemon.scene as BattleScene).phaseManager, "queueMessage").mockReturnValue(); expect(subject.lapse(mockPokemon, BattlerTagLapseType.PRE_MOVE)).toBeTruthy(); expect(subject.sourceInFocus).toBeTruthy(); expect((mockPokemon.scene as BattleScene).triggerPokemonBattleAnim).toHaveBeenCalledTimes(1); - expect((mockPokemon.scene as BattleScene).queueMessage).not.toHaveBeenCalled(); }); it("AFTER_MOVE lapse triggers post-move animation", async () => { @@ -169,13 +168,13 @@ describe("BattlerTag - SubstituteTag", () => { }, ); - vi.spyOn(mockPokemon.scene as BattleScene, "queueMessage").mockReturnValue(); + const msgSpy = vi.spyOn((mockPokemon.scene as BattleScene).phaseManager, "queueMessage").mockReturnValue(); expect(subject.lapse(mockPokemon, BattlerTagLapseType.AFTER_MOVE)).toBeTruthy(); expect(subject.sourceInFocus).toBeFalsy(); expect((mockPokemon.scene as BattleScene).triggerPokemonBattleAnim).toHaveBeenCalledTimes(1); - expect((mockPokemon.scene as BattleScene).queueMessage).not.toHaveBeenCalled(); + expect(msgSpy).not.toHaveBeenCalled(); }); // TODO: Figure out how to mock a MoveEffectPhase correctly for this test @@ -183,27 +182,27 @@ describe("BattlerTag - SubstituteTag", () => { const subject = new SubstituteTag(MoveId.SUBSTITUTE, mockPokemon.id); vi.spyOn(mockPokemon.scene as BattleScene, "triggerPokemonBattleAnim").mockReturnValue(true); - vi.spyOn(mockPokemon.scene as BattleScene, "queueMessage").mockReturnValue(); + const msgSpy = vi.spyOn((mockPokemon.scene as BattleScene).phaseManager, "queueMessage").mockReturnValue(); const moveEffectPhase = { move: allMoves[MoveId.TACKLE], getUserPokemon: vi.fn().mockReturnValue(undefined) as MoveEffectPhase["getUserPokemon"], } as MoveEffectPhase; - vi.spyOn(mockPokemon.scene as BattleScene, "getCurrentPhase").mockReturnValue(moveEffectPhase); + vi.spyOn((mockPokemon.scene as BattleScene).phaseManager, "getCurrentPhase").mockReturnValue(moveEffectPhase); vi.spyOn(allMoves[MoveId.TACKLE], "hitsSubstitute").mockReturnValue(true); expect(subject.lapse(mockPokemon, BattlerTagLapseType.HIT)).toBeTruthy(); expect((mockPokemon.scene as BattleScene).triggerPokemonBattleAnim).not.toHaveBeenCalled(); - expect((mockPokemon.scene as BattleScene).queueMessage).toHaveBeenCalledTimes(1); + expect(msgSpy).toHaveBeenCalledOnce(); }); it("CUSTOM lapse flags the tag for removal", async () => { const subject = new SubstituteTag(MoveId.SUBSTITUTE, mockPokemon.id); vi.spyOn(mockPokemon.scene as BattleScene, "triggerPokemonBattleAnim").mockReturnValue(true); - vi.spyOn(mockPokemon.scene as BattleScene, "queueMessage").mockReturnValue(); + vi.spyOn((mockPokemon.scene as BattleScene).phaseManager, "queueMessage").mockReturnValue(); expect(subject.lapse(mockPokemon, BattlerTagLapseType.CUSTOM)).toBeFalsy(); }); @@ -212,12 +211,12 @@ describe("BattlerTag - SubstituteTag", () => { const subject = new SubstituteTag(MoveId.SUBSTITUTE, mockPokemon.id); vi.spyOn(mockPokemon.scene as BattleScene, "triggerPokemonBattleAnim").mockReturnValue(true); - vi.spyOn(mockPokemon.scene as BattleScene, "queueMessage").mockReturnValue(); + const msgSpy = vi.spyOn((mockPokemon.scene as BattleScene).phaseManager, "queueMessage").mockReturnValue(); expect(subject.lapse(mockPokemon, BattlerTagLapseType.TURN_END)).toBeTruthy(); expect((mockPokemon.scene as BattleScene).triggerPokemonBattleAnim).not.toHaveBeenCalled(); - expect((mockPokemon.scene as BattleScene).queueMessage).not.toHaveBeenCalled(); + expect(msgSpy).not.toHaveBeenCalled(); }); }); }); diff --git a/test/escape-calculations.test.ts b/test/escape-calculations.test.ts index 854274eb948..fd1e411e786 100644 --- a/test/escape-calculations.test.ts +++ b/test/escape-calculations.test.ts @@ -40,11 +40,11 @@ describe("Escape chance calculations", () => { // set enemyPokemon's speed to 100 vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemySpeed]); - const commandPhase = game.scene.getCurrentPhase() as CommandPhase; + const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); await game.phaseInterceptor.to(AttemptRunPhase, false); - const phase = game.scene.getCurrentPhase() as AttemptRunPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as AttemptRunPhase; const escapePercentage = new NumberHolder(0); // this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping @@ -113,11 +113,11 @@ describe("Escape chance calculations", () => { // set enemyBPokemon's speed to 30 vi.spyOn(enemyField[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyBSpeed]); - const commandPhase = game.scene.getCurrentPhase() as CommandPhase; + const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); await game.phaseInterceptor.to(AttemptRunPhase, false); - const phase = game.scene.getCurrentPhase() as AttemptRunPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as AttemptRunPhase; const escapePercentage = new NumberHolder(0); // this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping @@ -192,11 +192,11 @@ describe("Escape chance calculations", () => { // set enemyPokemon's speed to 100 vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemySpeed]); - const commandPhase = game.scene.getCurrentPhase() as CommandPhase; + const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); await game.phaseInterceptor.to(AttemptRunPhase, false); - const phase = game.scene.getCurrentPhase() as AttemptRunPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as AttemptRunPhase; const escapePercentage = new NumberHolder(0); // this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping @@ -279,11 +279,11 @@ describe("Escape chance calculations", () => { // set enemyBPokemon's speed to 30 vi.spyOn(enemyField[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyBSpeed]); - const commandPhase = game.scene.getCurrentPhase() as CommandPhase; + const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase; commandPhase.handleCommand(Command.RUN, 0); await game.phaseInterceptor.to(AttemptRunPhase, false); - const phase = game.scene.getCurrentPhase() as AttemptRunPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as AttemptRunPhase; const escapePercentage = new NumberHolder(0); // this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping diff --git a/test/items/lock_capsule.test.ts b/test/items/lock_capsule.test.ts index 15a1b4e73d1..292031a2bf0 100644 --- a/test/items/lock_capsule.test.ts +++ b/test/items/lock_capsule.test.ts @@ -34,7 +34,7 @@ describe("Items - Lock Capsule", () => { it("doesn't set the cost of common tier items to 0", async () => { await game.classicMode.startBattle(); - game.scene.overridePhase( + game.scene.phaseManager.overridePhase( new SelectModifierPhase(0, undefined, { guaranteedModifierTiers: [ModifierTier.COMMON, ModifierTier.COMMON, ModifierTier.COMMON], fillRemaining: false, @@ -42,7 +42,7 @@ describe("Items - Lock Capsule", () => { ); game.onNextPrompt("SelectModifierPhase", UiMode.MODIFIER_SELECT, () => { - const selectModifierPhase = game.scene.getCurrentPhase() as SelectModifierPhase; + const selectModifierPhase = game.scene.phaseManager.getCurrentPhase() as SelectModifierPhase; const rerollCost = selectModifierPhase.getRerollCost(true); expect(rerollCost).toBe(150); }); diff --git a/test/moves/after_you.test.ts b/test/moves/after_you.test.ts index adf9cae707a..0e5ee1e7a8f 100644 --- a/test/moves/after_you.test.ts +++ b/test/moves/after_you.test.ts @@ -42,7 +42,7 @@ describe("Moves - After You", () => { await game.phaseInterceptor.to("MoveEffectPhase"); await game.phaseInterceptor.to(MovePhase, false); - const phase = game.scene.getCurrentPhase() as MovePhase; + const phase = game.scene.phaseManager.getCurrentPhase() as MovePhase; expect(phase.pokemon).toBe(game.scene.getPlayerField()[1]); await game.phaseInterceptor.to("MoveEndPhase"); }); diff --git a/test/moves/dynamax_cannon.test.ts b/test/moves/dynamax_cannon.test.ts index ada3361f3ef..6207ef9b6ca 100644 --- a/test/moves/dynamax_cannon.test.ts +++ b/test/moves/dynamax_cannon.test.ts @@ -56,7 +56,7 @@ describe("Moves - Dynamax Cannon", () => { game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100); }, 20000); @@ -68,7 +68,7 @@ describe("Moves - Dynamax Cannon", () => { game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100); }, 20000); @@ -80,7 +80,7 @@ describe("Moves - Dynamax Cannon", () => { game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); - const phase = game.scene.getCurrentPhase() as MoveEffectPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase; expect(phase.move.id).toBe(dynamaxCannon.id); // Force level cap to be 100 vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); @@ -95,7 +95,7 @@ describe("Moves - Dynamax Cannon", () => { game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); - const phase = game.scene.getCurrentPhase() as MoveEffectPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase; expect(phase.move.id).toBe(dynamaxCannon.id); // Force level cap to be 100 vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); @@ -110,7 +110,7 @@ describe("Moves - Dynamax Cannon", () => { game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); - const phase = game.scene.getCurrentPhase() as MoveEffectPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase; expect(phase.move.id).toBe(dynamaxCannon.id); // Force level cap to be 100 vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); @@ -125,7 +125,7 @@ describe("Moves - Dynamax Cannon", () => { game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); - const phase = game.scene.getCurrentPhase() as MoveEffectPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase; expect(phase.move.id).toBe(dynamaxCannon.id); // Force level cap to be 100 vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); @@ -140,7 +140,7 @@ describe("Moves - Dynamax Cannon", () => { game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); - const phase = game.scene.getCurrentPhase() as MoveEffectPhase; + const phase = game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase; expect(phase.move.id).toBe(dynamaxCannon.id); // Force level cap to be 100 vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); @@ -156,7 +156,7 @@ describe("Moves - Dynamax Cannon", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); diff --git a/test/moves/focus_punch.test.ts b/test/moves/focus_punch.test.ts index 9734f9f6422..38b57b201c0 100644 --- a/test/moves/focus_punch.test.ts +++ b/test/moves/focus_punch.test.ts @@ -115,8 +115,8 @@ describe("Moves - Focus Punch", () => { await game.phaseInterceptor.to(TurnStartPhase); - expect(game.scene.getCurrentPhase() instanceof SwitchSummonPhase).toBeTruthy(); - expect(game.scene.phaseQueue.find(phase => phase instanceof MoveHeaderPhase)).toBeDefined(); + expect(game.scene.phaseManager.getCurrentPhase() instanceof SwitchSummonPhase).toBeTruthy(); + expect(game.scene.phaseManager.phaseQueue.find(phase => phase instanceof MoveHeaderPhase)).toBeDefined(); }); it("should replace the 'but it failed' text when the user gets hit", async () => { game.override.enemyMoveset([MoveId.TACKLE]); diff --git a/test/moves/fusion_flare_bolt.test.ts b/test/moves/fusion_flare_bolt.test.ts index bceb2c862b5..1a5d9e44bab 100644 --- a/test/moves/fusion_flare_bolt.test.ts +++ b/test/moves/fusion_flare_bolt.test.ts @@ -56,12 +56,12 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); @@ -76,12 +76,12 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); @@ -96,7 +96,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100); @@ -106,7 +106,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { await game.phaseInterceptor.runFrom(MovePhase).to(MoveEndPhase); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); @@ -122,7 +122,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100); @@ -131,7 +131,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { await game.phaseInterceptor.runFrom(MovePhase).to(MoveEndPhase); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); }, 20000); @@ -146,12 +146,12 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); @@ -190,22 +190,22 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); @@ -244,22 +244,22 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); + expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); diff --git a/test/moves/instruct.test.ts b/test/moves/instruct.test.ts index 4a701ed6ac5..e0cfa93cf59 100644 --- a/test/moves/instruct.test.ts +++ b/test/moves/instruct.test.ts @@ -56,12 +56,12 @@ describe("Moves - Instruct", () => { await game.phaseInterceptor.to("MovePhase"); // enemy attacks us await game.phaseInterceptor.to("MovePhase", false); // instruct - let currentPhase = game.scene.getCurrentPhase() as MovePhase; + let currentPhase = game.scene.phaseManager.getCurrentPhase() as MovePhase; expect(currentPhase.pokemon).toBe(game.scene.getPlayerPokemon()); await game.phaseInterceptor.to("MoveEndPhase"); await game.phaseInterceptor.to("MovePhase", false); // enemy repeats move - currentPhase = game.scene.getCurrentPhase() as MovePhase; + currentPhase = game.scene.phaseManager.getCurrentPhase() as MovePhase; expect(currentPhase.pokemon).toBe(enemy); expect(currentPhase.move.moveId).toBe(MoveId.SONIC_BOOM); await game.phaseInterceptor.to("TurnEndPhase", false); diff --git a/test/moves/round.test.ts b/test/moves/round.test.ts index c42734bdc41..503ce125582 100644 --- a/test/moves/round.test.ts +++ b/test/moves/round.test.ts @@ -54,7 +54,9 @@ describe("Moves - Round", () => { for (let i = 0; i < 4; i++) { await game.phaseInterceptor.to("MoveEffectPhase", false); - actualTurnOrder.push((game.scene.getCurrentPhase() as MoveEffectPhase).getUserPokemon()!.getBattlerIndex()); + actualTurnOrder.push( + (game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).getUserPokemon()!.getBattlerIndex(), + ); await game.phaseInterceptor.to("MoveEndPhase"); } diff --git a/test/moves/shell_trap.test.ts b/test/moves/shell_trap.test.ts index 4dfc0e72d0a..9a38bf4486b 100644 --- a/test/moves/shell_trap.test.ts +++ b/test/moves/shell_trap.test.ts @@ -50,7 +50,7 @@ describe("Moves - Shell Trap", () => { await game.phaseInterceptor.to(MoveEndPhase); - const movePhase = game.scene.getCurrentPhase(); + const movePhase = game.scene.phaseManager.getCurrentPhase(); expect(movePhase instanceof MovePhase).toBeTruthy(); expect((movePhase as MovePhase).pokemon).toBe(playerPokemon[1]); @@ -73,7 +73,7 @@ describe("Moves - Shell Trap", () => { await game.phaseInterceptor.to(MoveEndPhase); - const movePhase = game.scene.getCurrentPhase(); + const movePhase = game.scene.phaseManager.getCurrentPhase(); expect(movePhase instanceof MovePhase).toBeTruthy(); expect((movePhase as MovePhase).pokemon).not.toBe(playerPokemon[1]); @@ -96,7 +96,7 @@ describe("Moves - Shell Trap", () => { await game.phaseInterceptor.to(MoveEndPhase); - const movePhase = game.scene.getCurrentPhase(); + const movePhase = game.scene.phaseManager.getCurrentPhase(); expect(movePhase instanceof MovePhase).toBeTruthy(); expect((movePhase as MovePhase).pokemon).not.toBe(playerPokemon[1]); @@ -117,7 +117,7 @@ describe("Moves - Shell Trap", () => { await game.phaseInterceptor.to(MoveEndPhase); - const movePhase = game.scene.getCurrentPhase(); + const movePhase = game.scene.phaseManager.getCurrentPhase(); expect(movePhase instanceof MovePhase).toBeTruthy(); expect((movePhase as MovePhase).pokemon).not.toBe(playerPokemon[1]); diff --git a/test/moves/substitute.test.ts b/test/moves/substitute.test.ts index 97296be7d8f..454e729a67b 100644 --- a/test/moves/substitute.test.ts +++ b/test/moves/substitute.test.ts @@ -400,7 +400,7 @@ describe("Moves - Substitute", () => { // Simulate a Baton switch for the player this turn game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.POKEMON, 1, true); + (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.POKEMON, 1, true); }); await game.phaseInterceptor.to("MovePhase", false); diff --git a/test/moves/transform.test.ts b/test/moves/transform.test.ts index e8ed133b827..ca326da5748 100644 --- a/test/moves/transform.test.ts +++ b/test/moves/transform.test.ts @@ -132,7 +132,7 @@ describe("Moves - Transform", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.toNextWave(); - expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); + expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); expect(game.scene.currentBattle.waveIndex).toBe(2); await game.reload.reloadSession(); @@ -173,7 +173,7 @@ describe("Moves - Transform", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.toNextWave(); - expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); + expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); expect(game.scene.currentBattle.waveIndex).toBe(2); await game.reload.reloadSession(); diff --git a/test/moves/whirlwind.test.ts b/test/moves/whirlwind.test.ts index 56c4017fbb5..c457bdb67d7 100644 --- a/test/moves/whirlwind.test.ts +++ b/test/moves/whirlwind.test.ts @@ -180,7 +180,7 @@ describe("Moves - Whirlwind", () => { expect(eligibleEnemy.length).toBe(1); // Spy on the queueMessage function - const queueSpy = vi.spyOn(globalScene, "queueMessage"); + const queueSpy = vi.spyOn(globalScene.phaseManager, "queueMessage"); // Player uses Whirlwind; opponent uses Splash game.move.select(MoveId.WHIRLWIND); diff --git a/test/mystery-encounter/encounter-test-utils.ts b/test/mystery-encounter/encounter-test-utils.ts index 977f40bc90e..6954d6212cc 100644 --- a/test/mystery-encounter/encounter-test-utils.ts +++ b/test/mystery-encounter/encounter-test-utils.ts @@ -71,9 +71,9 @@ export async function runMysteryEncounterToEnd( // If a battle is started, fast forward to end of the battle game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { - game.scene.clearPhaseQueue(); - game.scene.clearPhaseQueueSplice(); - game.scene.unshiftPhase(new VictoryPhase(0)); + game.scene.phaseManager.clearPhaseQueue(); + game.scene.phaseManager.clearPhaseQueueSplice(); + game.scene.phaseManager.unshiftPhase(new VictoryPhase(0)); game.endPhase(); }); @@ -197,14 +197,14 @@ async function handleSecondaryOptionSelect(game: GameManager, pokemonNo: number, * @param runRewardsPhase */ export async function skipBattleRunMysteryEncounterRewardsPhase(game: GameManager, runRewardsPhase = true) { - game.scene.clearPhaseQueue(); - game.scene.clearPhaseQueueSplice(); + game.scene.phaseManager.clearPhaseQueue(); + game.scene.phaseManager.clearPhaseQueueSplice(); game.scene.getEnemyParty().forEach(p => { p.hp = 0; p.status = new Status(StatusEffect.FAINT); game.scene.field.remove(p); }); - game.scene.pushPhase(new VictoryPhase(0)); + game.scene.phaseManager.pushPhase(new VictoryPhase(0)); game.phaseInterceptor.superEndPhase(); game.setMode(UiMode.MESSAGE); await game.phaseInterceptor.to(MysteryEncounterRewardsPhase, runRewardsPhase); diff --git a/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts b/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts index 3c6305a77dc..d713db0aff8 100644 --- a/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts +++ b/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts @@ -107,7 +107,7 @@ describe("A Trainer's Test - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(scene.currentBattle.trainer).toBeDefined(); expect( @@ -132,7 +132,7 @@ describe("A Trainer's Test - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const eggsAfter = scene.gameData.eggs; expect(eggsAfter).toBeDefined(); @@ -162,7 +162,7 @@ describe("A Trainer's Test - Mystery Encounter", () => { }); it("Should fully heal the party", async () => { - const phaseSpy = vi.spyOn(scene, "unshiftPhase"); + const phaseSpy = vi.spyOn(scene.phaseManager, "unshiftPhase"); await game.runToMysteryEncounter(MysteryEncounterType.A_TRAINERS_TEST, defaultParty); await runMysteryEncounterToEnd(game, 2); @@ -180,7 +180,7 @@ describe("A Trainer's Test - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const eggsAfter = scene.gameData.eggs; expect(eggsAfter).toBeDefined(); diff --git a/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts b/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts index f13f6e0b072..f5bc1a62528 100644 --- a/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts +++ b/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts @@ -129,13 +129,13 @@ describe("Absolute Avarice - Mystery Encounter", () => { }); it("should start battle against Greedent", async () => { - const phaseSpy = vi.spyOn(scene, "pushPhase"); + const phaseSpy = vi.spyOn(scene.phaseManager, "pushPhase"); await game.runToMysteryEncounter(MysteryEncounterType.ABSOLUTE_AVARICE, defaultParty); await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(SpeciesId.GREEDENT); const moveset = enemyField[0].moveset.map(m => m.moveId); @@ -152,7 +152,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); for (const partyPokemon of scene.getPlayerParty()) { const pokemonId = partyPokemon.id; diff --git a/test/mystery-encounter/encounters/berries-abound-encounter.test.ts b/test/mystery-encounter/encounters/berries-abound-encounter.test.ts index c3af2d9fe13..566576995a5 100644 --- a/test/mystery-encounter/encounters/berries-abound-encounter.test.ts +++ b/test/mystery-encounter/encounters/berries-abound-encounter.test.ts @@ -116,7 +116,7 @@ describe("Berries Abound - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); }); @@ -137,7 +137,7 @@ describe("Berries Abound - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const berriesAfter = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const berriesAfterCount = berriesAfter.reduce((a, b) => a + b.stackCount, 0); @@ -150,7 +150,7 @@ describe("Berries Abound - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -190,7 +190,7 @@ describe("Berries Abound - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); @@ -214,7 +214,7 @@ describe("Berries Abound - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); @@ -235,7 +235,7 @@ describe("Berries Abound - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); diff --git a/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts b/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts index 0cc990a405a..7e569d0cdf7 100644 --- a/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts +++ b/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts @@ -232,7 +232,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyParty = scene.getEnemyParty(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyParty.length).toBe(2); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); @@ -245,7 +245,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyParty = scene.getEnemyParty(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyParty.length).toBe(3); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); @@ -259,7 +259,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyParty = scene.getEnemyParty(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyParty.length).toBe(4); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); @@ -274,7 +274,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyParty = scene.getEnemyParty(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyParty.length).toBe(5); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); @@ -290,7 +290,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyParty = scene.getEnemyParty(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyParty.length).toBe(5); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); @@ -308,7 +308,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyParty = scene.getEnemyParty(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyParty.length).toBe(5); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); @@ -326,7 +326,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyParty = scene.getEnemyParty(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyParty.length).toBe(5); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); @@ -344,7 +344,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyParty = scene.getEnemyParty(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyParty.length).toBe(5); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); @@ -366,7 +366,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterRewardsPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterRewardsPhase.name); game.phaseInterceptor["prompts"] = []; // Clear out prompt handlers game.onNextPrompt("MysteryEncounterRewardsPhase", UiMode.OPTION_SELECT, () => { game.phaseInterceptor.superEndPhase(); @@ -398,7 +398,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, [SpeciesId.ABRA]); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -407,7 +407,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -417,7 +417,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, defaultParty); await runMysteryEncounterToEnd(game, 2); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -436,7 +436,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { ]); await runMysteryEncounterToEnd(game, 2); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -458,7 +458,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { ]); await runMysteryEncounterToEnd(game, 2); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -482,7 +482,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { ]); await runMysteryEncounterToEnd(game, 2); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -534,7 +534,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await game.phaseInterceptor.to(MysteryEncounterPhase, false); game.scene.modifiers = []; - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -543,7 +543,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 3); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -558,7 +558,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 }); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); diff --git a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts index d83f8d0642c..fc0ef0e5a86 100644 --- a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts +++ b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts @@ -167,13 +167,13 @@ describe("Clowning Around - Mystery Encounter", () => { }); it("should start double battle against the clown", async () => { - const phaseSpy = vi.spyOn(scene, "pushPhase"); + const phaseSpy = vi.spyOn(scene.phaseManager, "pushPhase"); await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty); await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(2); expect(enemyField[0].species.speciesId).toBe(SpeciesId.MR_MIME); expect(enemyField[0].moveset).toEqual([ @@ -202,7 +202,7 @@ describe("Clowning Around - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); const abilityToTrain = scene.currentBattle.mysteryEncounter?.misc.ability; @@ -217,7 +217,7 @@ describe("Clowning Around - Mystery Encounter", () => { vi.spyOn(partyUiHandler, "show"); game.endPhase(); await game.phaseInterceptor.to(PostMysteryEncounterPhase); - expect(scene.getCurrentPhase()?.constructor.name).toBe(PostMysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(PostMysteryEncounterPhase.name); // Wait for Yes/No confirmation to appear await vi.waitFor(() => expect(optionSelectUiHandler.show).toHaveBeenCalled()); diff --git a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts index 501ff0c45e8..76a562b5851 100644 --- a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts +++ b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts @@ -98,7 +98,7 @@ describe("Dancing Lessons - Mystery Encounter", () => { }); it("should start battle against Oricorio", async () => { - const phaseSpy = vi.spyOn(scene, "pushPhase"); + const phaseSpy = vi.spyOn(scene.phaseManager, "pushPhase"); await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty); // Make party lead's level arbitrarily high to not get KOed by move @@ -108,7 +108,7 @@ describe("Dancing Lessons - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(SpeciesId.ORICORIO); expect(enemyField[0].summonData.statStages).toEqual([1, 1, 1, 1, 0, 0, 0]); @@ -129,7 +129,7 @@ describe("Dancing Lessons - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -158,7 +158,7 @@ describe("Dancing Lessons - Mystery Encounter", () => { }); it("Should select a pokemon to learn Revelation Dance", async () => { - const phaseSpy = vi.spyOn(scene, "unshiftPhase"); + const phaseSpy = vi.spyOn(scene.phaseManager, "unshiftPhase"); await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty); scene.getPlayerParty()[0].moveset = []; @@ -219,7 +219,7 @@ describe("Dancing Lessons - Mystery Encounter", () => { scene.getPlayerParty().forEach(p => (p.moveset = [])); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -229,7 +229,7 @@ describe("Dancing Lessons - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 3); const partyCountAfter = scene.getPlayerParty().length; - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); diff --git a/test/mystery-encounter/encounters/delibirdy-encounter.test.ts b/test/mystery-encounter/encounters/delibirdy-encounter.test.ts index 58496e957c0..0d1094831bc 100644 --- a/test/mystery-encounter/encounters/delibirdy-encounter.test.ts +++ b/test/mystery-encounter/encounters/delibirdy-encounter.test.ts @@ -153,7 +153,7 @@ describe("Delibird-y - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -162,7 +162,7 @@ describe("Delibird-y - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 1); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -308,7 +308,7 @@ describe("Delibird-y - Mystery Encounter", () => { await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -317,7 +317,7 @@ describe("Delibird-y - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -441,7 +441,7 @@ describe("Delibird-y - Mystery Encounter", () => { await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -450,7 +450,7 @@ describe("Delibird-y - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 3); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); diff --git a/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts b/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts index c2974def16e..3feb44bbe91 100644 --- a/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts +++ b/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts @@ -95,7 +95,7 @@ describe("Department Store Sale - Mystery Encounter", () => { it("should have shop with only TMs", async () => { await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); await runMysteryEncounterToEnd(game, 1); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -132,7 +132,7 @@ describe("Department Store Sale - Mystery Encounter", () => { it("should have shop with only Vitamins", async () => { await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); await runMysteryEncounterToEnd(game, 2); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -172,7 +172,7 @@ describe("Department Store Sale - Mystery Encounter", () => { it("should have shop with only X Items", async () => { await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); await runMysteryEncounterToEnd(game, 3); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -212,7 +212,7 @@ describe("Department Store Sale - Mystery Encounter", () => { it("should have shop with only Pokeballs", async () => { await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); await runMysteryEncounterToEnd(game, 4); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); diff --git a/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts b/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts index ba9ea4126da..76b15106ef1 100644 --- a/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts +++ b/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -157,13 +157,13 @@ describe("Fiery Fallout - Mystery Encounter", () => { }); it("should start battle against 2 Volcarona", async () => { - const phaseSpy = vi.spyOn(scene, "pushPhase"); + const phaseSpy = vi.spyOn(scene.phaseManager, "pushPhase"); await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(2); expect(enemyField[0].species.speciesId).toBe(SpeciesId.VOLCARONA); expect(enemyField[1].species.speciesId).toBe(SpeciesId.VOLCARONA); @@ -179,7 +179,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const leadPokemonId = scene.getPlayerParty()?.[0].id; const leadPokemonItems = scene.findModifiers( @@ -268,7 +268,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); await runMysteryEncounterToEnd(game, 3); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const leadPokemonItems = scene.getPlayerParty()?.[0].getHeldItems() as PokemonHeldItemModifier[]; const item = leadPokemonItems.find(i => i instanceof AttackTypeBoosterModifier); @@ -288,13 +288,13 @@ describe("Fiery Fallout - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, [SpeciesId.MAGIKARP]); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const continueEncounterSpy = vi.spyOn(encounterPhase as MysteryEncounterPhase, "continueEncounter"); await runSelectMysteryEncounterOption(game, 3); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(continueEncounterSpy).not.toHaveBeenCalled(); }); }); diff --git a/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts b/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts index 1b5bd9fc649..7038fff3117 100644 --- a/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts +++ b/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts @@ -111,7 +111,7 @@ describe("Fight or Flight - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); }); @@ -124,7 +124,7 @@ describe("Fight or Flight - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -158,7 +158,7 @@ describe("Fight or Flight - Mystery Encounter", () => { scene.getPlayerParty().forEach(p => (p.moveset = [])); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -167,7 +167,7 @@ describe("Fight or Flight - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -184,7 +184,7 @@ describe("Fight or Flight - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); diff --git a/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts b/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts index fcc2eda28f7..4eaf2cef1da 100644 --- a/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts +++ b/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts @@ -123,7 +123,7 @@ describe("Fun And Games! - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -132,7 +132,7 @@ describe("Fun And Games! - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 1); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -144,7 +144,7 @@ describe("Fun And Games! - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(scene.getEnemyPokemon()?.species.speciesId).toBe(SpeciesId.WOBBUFFET); expect(scene.getEnemyPokemon()?.ivs).toEqual([0, 0, 0, 0, 0, 0]); expect(scene.getEnemyPokemon()?.nature).toBe(Nature.MILD); @@ -154,19 +154,19 @@ describe("Fun And Games! - Mystery Encounter", () => { }); // Turn 1 - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); + (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); await game.phaseInterceptor.to(CommandPhase); // Turn 2 - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); + (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); await game.phaseInterceptor.to(CommandPhase); // Turn 3 - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); + (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); await game.phaseInterceptor.to(SelectModifierPhase, false); // Rewards - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); }); it("should have no items in rewards if Wubboffet doesn't take enough damage", async () => { @@ -174,18 +174,18 @@ describe("Fun And Games! - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.endPhase(); }); // Skip minigame scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0; - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); + (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); await game.phaseInterceptor.to(SelectModifierPhase, false); // Rewards - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -201,7 +201,7 @@ describe("Fun And Games! - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.endPhase(); }); @@ -210,11 +210,11 @@ describe("Fun And Games! - Mystery Encounter", () => { const wobbuffet = scene.getEnemyPokemon()!; wobbuffet.hp = Math.floor(0.2 * wobbuffet.getMaxHp()); scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0; - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); + (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); await game.phaseInterceptor.to(SelectModifierPhase, false); // Rewards - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -231,7 +231,7 @@ describe("Fun And Games! - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.endPhase(); }); @@ -240,11 +240,11 @@ describe("Fun And Games! - Mystery Encounter", () => { const wobbuffet = scene.getEnemyPokemon()!; wobbuffet.hp = Math.floor(0.1 * wobbuffet.getMaxHp()); scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0; - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); + (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); await game.phaseInterceptor.to(SelectModifierPhase, false); // Rewards - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -261,7 +261,7 @@ describe("Fun And Games! - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.endPhase(); }); @@ -270,11 +270,11 @@ describe("Fun And Games! - Mystery Encounter", () => { const wobbuffet = scene.getEnemyPokemon()!; wobbuffet.hp = 1; scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0; - (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); + (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, false); await game.phaseInterceptor.to(SelectModifierPhase, false); // Rewards - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); diff --git a/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts b/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts index 73bf3cc4eba..96c4adf67c2 100644 --- a/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts +++ b/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts @@ -228,7 +228,7 @@ describe("Global Trade System - Mystery Encounter", () => { await scene.updateModifiers(true); await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 }); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); diff --git a/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index 10b96d84667..8f041a14002 100644 --- a/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -137,7 +137,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, [SpeciesId.ARCANINE]); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -146,7 +146,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 1); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -202,7 +202,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.LOST_AT_SEA, [SpeciesId.ARCANINE]); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -211,7 +211,7 @@ describe("Lost at Sea - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); diff --git a/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts b/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts index b93fbeb2673..948e42547de 100644 --- a/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts +++ b/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts @@ -155,7 +155,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); await runMysteryEncounterToEnd(game, 1, undefined, true); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); }); @@ -165,7 +165,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -199,7 +199,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); await runMysteryEncounterToEnd(game, 2, undefined, true); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); }); @@ -209,7 +209,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -256,7 +256,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); await runMysteryEncounterToEnd(game, 3, undefined, true); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); }); @@ -266,7 +266,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 3, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); diff --git a/test/mystery-encounter/encounters/part-timer-encounter.test.ts b/test/mystery-encounter/encounters/part-timer-encounter.test.ts index 1b9d24b5ce6..fa85f373a35 100644 --- a/test/mystery-encounter/encounters/part-timer-encounter.test.ts +++ b/test/mystery-encounter/encounters/part-timer-encounter.test.ts @@ -238,7 +238,7 @@ describe("Part-Timer - Mystery Encounter", () => { scene.getPlayerParty().forEach(p => (p.moveset = [])); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -247,7 +247,7 @@ describe("Part-Timer - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 3); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); diff --git a/test/mystery-encounter/encounters/safari-zone.test.ts b/test/mystery-encounter/encounters/safari-zone.test.ts index c6bde3a7e7a..dcaf25dd512 100644 --- a/test/mystery-encounter/encounters/safari-zone.test.ts +++ b/test/mystery-encounter/encounters/safari-zone.test.ts @@ -115,7 +115,7 @@ describe("Safari Zone - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.SAFARI_ZONE, defaultParty); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -124,7 +124,7 @@ describe("Safari Zone - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 1); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); diff --git a/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts b/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts index 039b1dacf31..6c8daed998c 100644 --- a/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts +++ b/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts @@ -153,7 +153,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -162,7 +162,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 1); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -172,7 +172,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty); await runMysteryEncounterToEnd(game, 1, undefined, true); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); }); it("should transport to a new area", async () => { @@ -225,7 +225,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, [SpeciesId.BLASTOISE]); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -234,7 +234,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -244,7 +244,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, [SpeciesId.METAGROSS]); await runMysteryEncounterToEnd(game, 2, undefined, true); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); }); it("should transport to a new area", async () => { @@ -305,7 +305,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 3, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); diff --git a/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts b/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts index 14c9287f5f3..0d5f67b8815 100644 --- a/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts @@ -160,7 +160,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => { expect(successfullyLoaded).toBe(true); // Check usual battle stuff - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); expect(scene.getPlayerParty().length).toBe(1); @@ -179,7 +179,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const eggsAfter = scene.gameData.eggs; const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon1CommonEggs; @@ -245,7 +245,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => { expect(successfullyLoaded).toBe(true); // Check usual battle stuff - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); expect(scene.getPlayerParty().length).toBe(1); @@ -264,7 +264,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const eggsAfter = scene.gameData.eggs; const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon2CommonEggs; @@ -327,7 +327,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => { expect(successfullyLoaded).toBe(true); // Check usual battle stuff - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); expect(scene.getPlayerParty().length).toBe(1); @@ -346,7 +346,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 3, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const eggsAfter = scene.gameData.eggs; const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon3CommonEggs; diff --git a/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts b/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts index 73632990dbf..13d7c2502a6 100644 --- a/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts @@ -175,7 +175,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.THE_POKEMON_SALESMAN, defaultParty); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -184,7 +184,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 1); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); diff --git a/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts b/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts index 2187bad5775..e50a19a0a80 100644 --- a/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts @@ -189,13 +189,13 @@ describe("The Strong Stuff - Mystery Encounter", () => { }); it("should start battle against Shuckle", async () => { - const phaseSpy = vi.spyOn(scene, "pushPhase"); + const phaseSpy = vi.spyOn(scene.phaseManager, "pushPhase"); await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty); await runMysteryEncounterToEnd(game, 2, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(SpeciesId.SHUCKLE); expect(enemyField[0].summonData.statStages).toEqual([0, 1, 0, 1, 0, 0, 0]); @@ -233,7 +233,7 @@ describe("The Strong Stuff - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); diff --git a/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts b/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts index 57b6e881683..87a3852615d 100644 --- a/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts @@ -265,7 +265,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty); await runMysteryEncounterToEnd(game, 1, undefined, true); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.trainer!.config.trainerType).toBe(TrainerType.VICTOR); expect(scene.currentBattle.mysteryEncounter?.enemyPartyConfigs.length).toBe(4); @@ -298,7 +298,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { // Should have Macho Brace in the rewards await skipBattleToNextBattle(game, true); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -328,7 +328,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { }); it("Should fully heal the party", async () => { - const phaseSpy = vi.spyOn(scene, "unshiftPhase"); + const phaseSpy = vi.spyOn(scene.phaseManager, "unshiftPhase"); await game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty); await runMysteryEncounterToEnd(game, 2); @@ -340,7 +340,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { it("should have a Rarer Candy in the rewards", async () => { await game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty); await runMysteryEncounterToEnd(game, 2); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -359,8 +359,8 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { * @param isFinalBattle */ async function skipBattleToNextBattle(game: GameManager, isFinalBattle = false) { - game.scene.clearPhaseQueue(); - game.scene.clearPhaseQueueSplice(); + game.scene.phaseManager.clearPhaseQueue(); + game.scene.phaseManager.clearPhaseQueueSplice(); const commandUiHandler = game.scene.ui.handlers[UiMode.COMMAND]; commandUiHandler.clear(); game.scene.getEnemyParty().forEach(p => { @@ -369,7 +369,7 @@ async function skipBattleToNextBattle(game: GameManager, isFinalBattle = false) game.scene.field.remove(p); }); game.phaseInterceptor["onHold"] = []; - game.scene.pushPhase(new VictoryPhase(0)); + game.scene.phaseManager.pushPhase(new VictoryPhase(0)); game.phaseInterceptor.superEndPhase(); if (isFinalBattle) { await game.phaseInterceptor.to(MysteryEncounterRewardsPhase); diff --git a/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts b/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts index 94011b4b01d..999936c8832 100644 --- a/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts +++ b/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts @@ -173,7 +173,7 @@ describe("Trash to Treasure - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty); await runMysteryEncounterToEnd(game, 1); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const leftovers = scene.findModifier(m => m instanceof TurnHealModifier) as TurnHealModifier; expect(leftovers).toBeDefined(); @@ -215,13 +215,13 @@ describe("Trash to Treasure - Mystery Encounter", () => { }); it("should start battle against Garbodor", async () => { - const phaseSpy = vi.spyOn(scene, "pushPhase"); + const phaseSpy = vi.spyOn(scene.phaseManager, "pushPhase"); await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty); await runMysteryEncounterToEnd(game, 2, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(SpeciesId.GARBODOR); expect(enemyField[0].moveset).toEqual([ @@ -243,7 +243,7 @@ describe("Trash to Treasure - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); diff --git a/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts b/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts index 4fb0a231853..0c3131de1d2 100644 --- a/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts +++ b/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts @@ -113,8 +113,8 @@ describe("Uncommon Breed - Mystery Encounter", () => { }); it.skip("should start a fight against the boss below wave 50", async () => { - const phaseSpy = vi.spyOn(scene, "pushPhase"); - const unshiftPhaseSpy = vi.spyOn(scene, "unshiftPhase"); + const phaseSpy = vi.spyOn(scene.phaseManager, "pushPhase"); + const unshiftPhaseSpy = vi.spyOn(scene.phaseManager, "unshiftPhase"); await game.runToMysteryEncounter(MysteryEncounterType.UNCOMMON_BREED, defaultParty); const config = game.scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]; @@ -123,7 +123,7 @@ describe("Uncommon Breed - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); @@ -140,8 +140,8 @@ describe("Uncommon Breed - Mystery Encounter", () => { it.skip("should start a fight against the boss above wave 50", async () => { game.override.startingWave(57); - const phaseSpy = vi.spyOn(scene, "pushPhase"); - const unshiftPhaseSpy = vi.spyOn(scene, "unshiftPhase"); + const phaseSpy = vi.spyOn(scene.phaseManager, "pushPhase"); + const unshiftPhaseSpy = vi.spyOn(scene.phaseManager, "unshiftPhase"); await game.runToMysteryEncounter(MysteryEncounterType.UNCOMMON_BREED, defaultParty); const config = game.scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]; @@ -150,7 +150,7 @@ describe("Uncommon Breed - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); @@ -193,7 +193,7 @@ describe("Uncommon Breed - Mystery Encounter", () => { await scene.updateModifiers(true); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -202,7 +202,7 @@ describe("Uncommon Breed - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 2); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); @@ -253,7 +253,7 @@ describe("Uncommon Breed - Mystery Encounter", () => { scene.getPlayerParty().forEach(p => (p.moveset = [])); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - const encounterPhase = scene.getCurrentPhase(); + const encounterPhase = scene.phaseManager.getCurrentPhase(); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); const mysteryEncounterPhase = encounterPhase as MysteryEncounterPhase; vi.spyOn(mysteryEncounterPhase, "continueEncounter"); @@ -262,7 +262,7 @@ describe("Uncommon Breed - Mystery Encounter", () => { await runSelectMysteryEncounterOption(game, 3); - expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); diff --git a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts index 163a15a715f..2ad74b48540 100644 --- a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts +++ b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts @@ -118,7 +118,7 @@ describe("Weird Dream - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); const pokemonAfter = scene.getPlayerParty(); const bstsAfter = pokemonAfter.map(pokemon => pokemon.getSpeciesForm().getBaseStatTotal()); @@ -141,7 +141,7 @@ describe("Weird Dream - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty); await runMysteryEncounterToEnd(game, 1); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -187,7 +187,7 @@ describe("Weird Dream - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2, undefined, true); const enemyField = scene.getEnemyField(); - expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(scene.getEnemyParty().length).toBe(scene.getPlayerParty().length); }); @@ -197,7 +197,7 @@ describe("Weird Dream - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2, undefined, true); await skipBattleRunMysteryEncounterRewardsPhase(game); await game.phaseInterceptor.to(SelectModifierPhase, false); - expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); + expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); diff --git a/test/mystery-encounter/mystery-encounter-utils.test.ts b/test/mystery-encounter/mystery-encounter-utils.test.ts index cd29a203e62..80e2fb77f2b 100644 --- a/test/mystery-encounter/mystery-encounter-utils.test.ts +++ b/test/mystery-encounter/mystery-encounter-utils.test.ts @@ -291,8 +291,8 @@ describe("Mystery Encounter Utils", () => { it("queues a message with encounter dialogue tokens", async () => { scene.currentBattle.mysteryEncounter = new MysteryEncounter(null); scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value"); - const spy = vi.spyOn(game.scene, "queueMessage"); - const phaseSpy = vi.spyOn(game.scene, "unshiftPhase"); + const spy = vi.spyOn(game.scene.phaseManager, "queueMessage"); + const phaseSpy = vi.spyOn(game.scene.phaseManager, "unshiftPhase"); queueEncounterMessage("mysteryEncounter:unit_test_dialogue"); expect(spy).toHaveBeenCalledWith("mysteryEncounter:unit_test_dialogue", null, true); diff --git a/test/mystery-encounter/mystery-encounter.test.ts b/test/mystery-encounter/mystery-encounter.test.ts index 19dd17d5381..1f1d0f5826e 100644 --- a/test/mystery-encounter/mystery-encounter.test.ts +++ b/test/mystery-encounter/mystery-encounter.test.ts @@ -35,7 +35,7 @@ describe("Mystery Encounters", () => { ]); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - expect(game.scene.getCurrentPhase()!.constructor.name).toBe(MysteryEncounterPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(MysteryEncounterPhase.name); }); it("Encounters should not run below wave 10", async () => { diff --git a/test/phases/mystery-encounter-phase.test.ts b/test/phases/mystery-encounter-phase.test.ts index ece5a221e00..d078c2398b4 100644 --- a/test/phases/mystery-encounter-phase.test.ts +++ b/test/phases/mystery-encounter-phase.test.ts @@ -41,7 +41,7 @@ describe("Mystery Encounter Phases", () => { ]); await game.phaseInterceptor.to(MysteryEncounterPhase, false); - expect(game.scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); + expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); }); it("Runs MysteryEncounterPhase", async () => { @@ -87,7 +87,9 @@ describe("Mystery Encounter Phases", () => { // Waitfor required so that option select messages and preOptionPhase logic are handled await vi.waitFor(() => - expect(game.scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterOptionSelectedPhase.name), + expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe( + MysteryEncounterOptionSelectedPhase.name, + ), ); expect(ui.getMode()).toBe(UiMode.MESSAGE); expect(ui.showDialogue).toHaveBeenCalledTimes(1); diff --git a/test/phases/phases.test.ts b/test/phases/phases.test.ts index 2483cfb317f..8f7b1a1ea66 100644 --- a/test/phases/phases.test.ts +++ b/test/phases/phases.test.ts @@ -30,7 +30,7 @@ describe("Phases", () => { describe("LoginPhase", () => { it("should start the login phase", async () => { const loginPhase = new LoginPhase(); - scene.unshiftPhase(loginPhase); + scene.phaseManager.unshiftPhase(loginPhase); await game.phaseInterceptor.to(LoginPhase); expect(scene.ui.getMode()).to.equal(UiMode.MESSAGE); }); @@ -39,7 +39,7 @@ describe("Phases", () => { describe("TitlePhase", () => { it("should start the title phase", async () => { const titlePhase = new TitlePhase(); - scene.unshiftPhase(titlePhase); + scene.phaseManager.unshiftPhase(titlePhase); await game.phaseInterceptor.to(TitlePhase); expect(scene.ui.getMode()).to.equal(UiMode.TITLE); }); @@ -48,7 +48,7 @@ describe("Phases", () => { describe("UnavailablePhase", () => { it("should start the unavailable phase", async () => { const unavailablePhase = new UnavailablePhase(); - scene.unshiftPhase(unavailablePhase); + scene.phaseManager.unshiftPhase(unavailablePhase); await game.phaseInterceptor.to(UnavailablePhase); expect(scene.ui.getMode()).to.equal(UiMode.UNAVAILABLE); }, 20000); diff --git a/test/phases/select-modifier-phase.test.ts b/test/phases/select-modifier-phase.test.ts index 72496d5f17b..083b7d16f10 100644 --- a/test/phases/select-modifier-phase.test.ts +++ b/test/phases/select-modifier-phase.test.ts @@ -48,7 +48,7 @@ describe("SelectModifierPhase", () => { it("should start a select modifier phase", async () => { initSceneWithoutEncounterPhase(scene, [SpeciesId.ABRA, SpeciesId.VOLCARONA]); const selectModifierPhase = new SelectModifierPhase(); - scene.unshiftPhase(selectModifierPhase); + scene.phaseManager.unshiftPhase(selectModifierPhase); await game.phaseInterceptor.to(SelectModifierPhase); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); @@ -96,7 +96,7 @@ describe("SelectModifierPhase", () => { await game.phaseInterceptor.to("SelectModifierPhase"); // TODO: nagivate the ui to reroll somehow - //const smphase = scene.getCurrentPhase() as SelectModifierPhase; + //const smphase = scene.phaseManager.getCurrentPhase() as SelectModifierPhase; expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, @@ -164,7 +164,7 @@ describe("SelectModifierPhase", () => { ], }; const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers); - scene.unshiftPhase(selectModifierPhase); + scene.phaseManager.unshiftPhase(selectModifierPhase); game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("SelectModifierPhase"); @@ -201,7 +201,7 @@ describe("SelectModifierPhase", () => { scene.getPlayerParty().push(pokemon, pokemon, pokemon, pokemon, pokemon, pokemon); const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers); - scene.unshiftPhase(selectModifierPhase); + scene.phaseManager.unshiftPhase(selectModifierPhase); game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("SelectModifierPhase"); @@ -240,7 +240,7 @@ describe("SelectModifierPhase", () => { guaranteedModifierTiers: [ModifierTier.MASTER, ModifierTier.MASTER], }; const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers); - scene.unshiftPhase(selectModifierPhase); + scene.phaseManager.unshiftPhase(selectModifierPhase); game.move.select(MoveId.SPLASH); await game.phaseInterceptor.run(SelectModifierPhase); @@ -264,7 +264,7 @@ describe("SelectModifierPhase", () => { fillRemaining: true, }; const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers); - scene.unshiftPhase(selectModifierPhase); + scene.phaseManager.unshiftPhase(selectModifierPhase); game.move.select(MoveId.SPLASH); await game.phaseInterceptor.run(SelectModifierPhase); diff --git a/test/testUtils/gameManager.ts b/test/testUtils/gameManager.ts index 68d218b3988..edf0301447d 100644 --- a/test/testUtils/gameManager.ts +++ b/test/testUtils/gameManager.ts @@ -101,13 +101,13 @@ export default class GameManager { if (!firstTimeScene) { this.scene.reset(false, true); (this.scene.ui.handlers[UiMode.STARTER_SELECT] as StarterSelectUiHandler).clearStarterPreferences(); - this.scene.clearAllPhases(); + this.scene.phaseManager.clearAllPhases(); // Must be run after phase interceptor has been initialized. - this.scene.pushPhase(new LoginPhase()); - this.scene.pushPhase(new TitlePhase()); - this.scene.shiftPhase(); + this.scene.phaseManager.pushPhase(new LoginPhase()); + this.scene.phaseManager.pushPhase(new TitlePhase()); + this.scene.phaseManager.shiftPhase(); this.gameWrapper.scene = this.scene; } @@ -154,7 +154,7 @@ export default class GameManager { * Ends the current phase. */ endPhase() { - this.scene.getCurrentPhase()?.end(); + this.scene.phaseManager.getCurrentPhase()?.end(); } /** @@ -211,7 +211,7 @@ export default class GameManager { this.scene.gameMode = getGameMode(mode); const starters = generateStarter(this.scene, species); const selectStarterPhase = new SelectStarterPhase(); - this.scene.pushPhase(new EncounterPhase(false)); + this.scene.phaseManager.pushPhase(new EncounterPhase(false)); selectStarterPhase.initBattle(starters); }); @@ -247,7 +247,7 @@ export default class GameManager { this.scene.gameMode = getGameMode(GameModes.CLASSIC); const starters = generateStarter(this.scene, species); const selectStarterPhase = new SelectStarterPhase(); - this.scene.pushPhase(new EncounterPhase(false)); + this.scene.phaseManager.pushPhase(new EncounterPhase(false)); selectStarterPhase.initBattle(starters); }, () => this.isCurrentPhase(EncounterPhase), @@ -282,7 +282,7 @@ export default class GameManager { UiMode.TARGET_SELECT, () => { const handler = this.scene.ui.getHandler() as TargetSelectUiHandler; - const move = (this.scene.getCurrentPhase() as SelectTargetPhase) + const move = (this.scene.phaseManager.getCurrentPhase() as SelectTargetPhase) .getPokemon() .getMoveset() [movePosition].getMove(); @@ -397,7 +397,7 @@ export default class GameManager { */ isCurrentPhase(phaseTarget) { const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name; - return this.scene.getCurrentPhase()?.constructor.name === targetName; + return this.scene.phaseManager.getCurrentPhase()?.constructor.name === targetName; } /** @@ -449,7 +449,7 @@ export default class GameManager { async killPokemon(pokemon: PlayerPokemon | EnemyPokemon) { return new Promise(async (resolve, reject) => { pokemon.hp = 0; - this.scene.pushPhase(new FaintPhase(pokemon.getBattlerIndex(), true)); + this.scene.phaseManager.pushPhase(new FaintPhase(pokemon.getBattlerIndex(), true)); await this.phaseInterceptor.to(FaintPhase).catch(e => reject(e)); resolve(); }); @@ -529,7 +529,7 @@ export default class GameManager { async setTurnOrder(order: BattlerIndex[]): Promise { await this.phaseInterceptor.to(TurnStartPhase, false); - vi.spyOn(this.scene.getCurrentPhase() as TurnStartPhase, "getSpeedOrder").mockReturnValue(order); + vi.spyOn(this.scene.phaseManager.getCurrentPhase() as TurnStartPhase, "getSpeedOrder").mockReturnValue(order); } /** diff --git a/test/testUtils/helpers/challengeModeHelper.ts b/test/testUtils/helpers/challengeModeHelper.ts index d0533d3b5ba..f0b4b151d22 100644 --- a/test/testUtils/helpers/challengeModeHelper.ts +++ b/test/testUtils/helpers/challengeModeHelper.ts @@ -45,7 +45,7 @@ export class ChallengeModeHelper extends GameManagerHelper { this.game.scene.gameMode.challenges = this.challenges; const starters = generateStarter(this.game.scene, species); const selectStarterPhase = new SelectStarterPhase(); - this.game.scene.pushPhase(new EncounterPhase(false)); + this.game.scene.phaseManager.pushPhase(new EncounterPhase(false)); selectStarterPhase.initBattle(starters); }); diff --git a/test/testUtils/helpers/classicModeHelper.ts b/test/testUtils/helpers/classicModeHelper.ts index d5f0ceb4072..575000c2193 100644 --- a/test/testUtils/helpers/classicModeHelper.ts +++ b/test/testUtils/helpers/classicModeHelper.ts @@ -30,7 +30,7 @@ export class ClassicModeHelper extends GameManagerHelper { this.game.scene.gameMode = getGameMode(GameModes.CLASSIC); const starters = generateStarter(this.game.scene, species); const selectStarterPhase = new SelectStarterPhase(); - this.game.scene.pushPhase(new EncounterPhase(false)); + this.game.scene.phaseManager.pushPhase(new EncounterPhase(false)); selectStarterPhase.initBattle(starters); }); diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index 02b1efd837f..878a265ce84 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -23,7 +23,7 @@ export class MoveHelper extends GameManagerHelper { */ public async forceHit(): Promise { await this.game.phaseInterceptor.to(MoveEffectPhase, false); - const moveEffectPhase = this.game.scene.getCurrentPhase() as MoveEffectPhase; + const moveEffectPhase = this.game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase; vi.spyOn(moveEffectPhase.move, "calculateBattleAccuracy").mockReturnValue(-1); } @@ -34,7 +34,7 @@ export class MoveHelper extends GameManagerHelper { */ public async forceMiss(firstTargetOnly = false): Promise { await this.game.phaseInterceptor.to(MoveEffectPhase, false); - const moveEffectPhase = this.game.scene.getCurrentPhase() as MoveEffectPhase; + const moveEffectPhase = this.game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase; const accuracy = vi.spyOn(moveEffectPhase.move, "calculateBattleAccuracy"); if (firstTargetOnly) { @@ -54,10 +54,17 @@ export class MoveHelper extends GameManagerHelper { const movePosition = getMovePosition(this.game.scene, pkmIndex, move); this.game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { - this.game.scene.ui.setMode(UiMode.FIGHT, (this.game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); + this.game.scene.ui.setMode( + UiMode.FIGHT, + (this.game.scene.phaseManager.getCurrentPhase() as CommandPhase).getFieldIndex(), + ); }); this.game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => { - (this.game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); + (this.game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand( + Command.FIGHT, + movePosition, + false, + ); }); if (targetIndex !== null) { @@ -79,12 +86,12 @@ export class MoveHelper extends GameManagerHelper { this.game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { this.game.scene.ui.setMode( UiMode.FIGHT, - (this.game.scene.getCurrentPhase() as CommandPhase).getFieldIndex(), + (this.game.scene.phaseManager.getCurrentPhase() as CommandPhase).getFieldIndex(), Command.TERA, ); }); this.game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => { - (this.game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.TERA, movePosition, false); + (this.game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.TERA, movePosition, false); }); if (targetIndex !== null) { @@ -171,7 +178,9 @@ export class MoveHelper extends GameManagerHelper { // Wait for the next EnemyCommandPhase to start await this.game.phaseInterceptor.to("EnemyCommandPhase", false); const enemy = - this.game.scene.getEnemyField()[(this.game.scene.getCurrentPhase() as EnemyCommandPhase).getFieldIndex()]; + this.game.scene.getEnemyField()[ + (this.game.scene.phaseManager.getCurrentPhase() as EnemyCommandPhase).getFieldIndex() + ]; const legalTargets = getMoveTargets(enemy, moveId); vi.spyOn(enemy, "getNextMove").mockReturnValueOnce({ @@ -205,7 +214,9 @@ export class MoveHelper extends GameManagerHelper { await this.game.phaseInterceptor.to("EnemyCommandPhase", false); const enemy = - this.game.scene.getEnemyField()[(this.game.scene.getCurrentPhase() as EnemyCommandPhase).getFieldIndex()]; + this.game.scene.getEnemyField()[ + (this.game.scene.phaseManager.getCurrentPhase() as EnemyCommandPhase).getFieldIndex() + ]; if ([Overrides.OPP_MOVESET_OVERRIDE].flat().length > 0) { vi.spyOn(Overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([]); diff --git a/test/testUtils/helpers/reloadHelper.ts b/test/testUtils/helpers/reloadHelper.ts index 4a9e5356968..4f9d6c810f8 100644 --- a/test/testUtils/helpers/reloadHelper.ts +++ b/test/testUtils/helpers/reloadHelper.ts @@ -35,7 +35,7 @@ export class ReloadHelper extends GameManagerHelper { const scene = this.game.scene; const titlePhase = new TitlePhase(); - scene.clearPhaseQueue(); + scene.phaseManager.clearPhaseQueue(); // Set the last saved session to the desired session data vi.spyOn(scene.gameData, "getSession").mockReturnValue( @@ -43,7 +43,7 @@ export class ReloadHelper extends GameManagerHelper { resolve(this.sessionData); }), ); - scene.unshiftPhase(titlePhase); + scene.phaseManager.unshiftPhase(titlePhase); this.game.endPhase(); // End the currently ongoing battle // remove all persistent mods before loading diff --git a/test/testUtils/phaseInterceptor.ts b/test/testUtils/phaseInterceptor.ts index b7577550568..34fba2c145d 100644 --- a/test/testUtils/phaseInterceptor.ts +++ b/test/testUtils/phaseInterceptor.ts @@ -301,7 +301,7 @@ export default class PhaseInterceptor { pop() { this.onHold.pop(); - this.scene.shiftPhase(); + this.scene.phaseManager.shiftPhase(); } /** @@ -316,7 +316,7 @@ export default class PhaseInterceptor { shift(shouldRun = false): void { this.onHold.shift(); if (shouldRun) { - this.scene.shiftPhase(); + this.scene.phaseManager.shiftPhase(); } } @@ -345,7 +345,7 @@ export default class PhaseInterceptor { */ startPhase(phase: PhaseClass) { this.log.push(phase.name); - const instance = this.scene.getCurrentPhase(); + const instance = this.scene.phaseManager.getCurrentPhase(); this.onHold.push({ name: phase.name, call: () => { @@ -364,7 +364,7 @@ export default class PhaseInterceptor { * @param phase - The phase to start. */ superEndPhase() { - const instance = this.scene.getCurrentPhase(); + const instance = this.scene.phaseManager.getCurrentPhase(); this.originalSuperEnd.apply(instance); this.inProgress?.callback(); this.inProgress = undefined; @@ -376,7 +376,7 @@ export default class PhaseInterceptor { * @param args - Additional arguments to pass to the original method. */ setMode(mode: UiMode, ...args: unknown[]): Promise { - const currentPhase = this.scene.getCurrentPhase(); + const currentPhase = this.scene.phaseManager.getCurrentPhase(); const instance = this.scene.ui; console.log("setMode", `${UiMode[mode]} (=${mode})`, args); const ret = this.originalSetMode.apply(instance, [mode, ...args]); @@ -413,7 +413,7 @@ export default class PhaseInterceptor { const actionForNextPrompt = this.prompts[0]; const expireFn = actionForNextPrompt.expireFn?.(); const currentMode = this.scene.ui.getMode(); - const currentPhase = this.scene.getCurrentPhase()?.constructor.name; + const currentPhase = this.scene.phaseManager.getCurrentPhase()?.constructor.name; const currentHandler = this.scene.ui.getHandler(); if (expireFn) { this.prompts.shift(); diff --git a/test/ui/starter-select.test.ts b/test/ui/starter-select.test.ts index 10a804b805d..2884323b4ea 100644 --- a/test/ui/starter-select.test.ts +++ b/test/ui/starter-select.test.ts @@ -45,7 +45,7 @@ describe("UI - Starter select", () => { expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { - const currentPhase = game.scene.getCurrentPhase() as TitlePhase; + const currentPhase = game.scene.phaseManager.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); @@ -105,7 +105,7 @@ describe("UI - Starter select", () => { expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { - const currentPhase = game.scene.getCurrentPhase() as TitlePhase; + const currentPhase = game.scene.phaseManager.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); @@ -167,7 +167,7 @@ describe("UI - Starter select", () => { expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { - const currentPhase = game.scene.getCurrentPhase() as TitlePhase; + const currentPhase = game.scene.phaseManager.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); @@ -232,7 +232,7 @@ describe("UI - Starter select", () => { expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { - const currentPhase = game.scene.getCurrentPhase() as TitlePhase; + const currentPhase = game.scene.phaseManager.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); @@ -293,7 +293,7 @@ describe("UI - Starter select", () => { expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { - const currentPhase = game.scene.getCurrentPhase() as TitlePhase; + const currentPhase = game.scene.phaseManager.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); @@ -353,7 +353,7 @@ describe("UI - Starter select", () => { expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { - const currentPhase = game.scene.getCurrentPhase() as TitlePhase; + const currentPhase = game.scene.phaseManager.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); @@ -415,7 +415,7 @@ describe("UI - Starter select", () => { expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { - const currentPhase = game.scene.getCurrentPhase() as TitlePhase; + const currentPhase = game.scene.phaseManager.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); @@ -476,7 +476,7 @@ describe("UI - Starter select", () => { expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { - const currentPhase = game.scene.getCurrentPhase() as TitlePhase; + const currentPhase = game.scene.phaseManager.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); @@ -540,7 +540,7 @@ describe("UI - Starter select", () => { expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { - const currentPhase = game.scene.getCurrentPhase() as TitlePhase; + const currentPhase = game.scene.phaseManager.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); From 09e30070f904c7993fa621a924fb057546591233 Mon Sep 17 00:00:00 2001 From: Bertie690 <136088738+Bertie690@users.noreply.github.com> Date: Sat, 7 Jun 2025 21:02:25 -0400 Subject: [PATCH 20/23] [Docs] Fixed test helper functions to conform with TSDoc standard; deprecated `runToSummon`/`startBattle` without args (#5912) * Updated doc comments for test-related functions * Marked `classicMode.runToSummon` and `classicMode.startBattle` without species as deprecated Having the species being used depend on daily run RNG is both unintuitive, janky and prone to flaking out (as happened with the Gastro Acid tests) * Fixed the bug * Update field-helper.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- test/testUtils/helpers/classicModeHelper.ts | 25 ++++++++++++++--- test/testUtils/helpers/field-helper.ts | 30 +++++++++++---------- test/testUtils/helpers/moveHelper.ts | 26 +++++++++++------- 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/test/testUtils/helpers/classicModeHelper.ts b/test/testUtils/helpers/classicModeHelper.ts index 575000c2193..c4f086bd628 100644 --- a/test/testUtils/helpers/classicModeHelper.ts +++ b/test/testUtils/helpers/classicModeHelper.ts @@ -11,14 +11,24 @@ import { generateStarter } from "../gameManagerUtils"; import { GameManagerHelper } from "./gameManagerHelper"; /** - * Helper to handle classic mode specifics + * Helper to handle classic-mode specific operations. */ export class ClassicModeHelper extends GameManagerHelper { /** * Runs the classic game to the summon phase. - * @param species - Optional array of species to summon. + * @param species - An array of {@linkcode Species} to summon. * @returns A promise that resolves when the summon phase is reached. */ + async runToSummon(species: SpeciesId[]): Promise; + /** + * Runs the classic game to the summon phase. + * Selects 3 daily run starters with a fixed seed of "test" + * (see `DailyRunConfig.getDailyRunStarters` in `daily-run.ts` for more info). + * @returns A promise that resolves when the summon phase is reached. + * @deprecated - Specifying the starters helps prevent inconsistencies from internal RNG changes. + */ + async runToSummon(): Promise; + async runToSummon(species: SpeciesId[] | undefined): Promise; async runToSummon(species?: SpeciesId[]): Promise { await this.game.runToTitle(); @@ -42,9 +52,18 @@ export class ClassicModeHelper extends GameManagerHelper { /** * Transitions to the start of a battle. - * @param species - Optional array of species to start the battle with. + * @param species - An array of {@linkcode Species} to start the battle with. * @returns A promise that resolves when the battle is started. */ + async startBattle(species: SpeciesId[]): Promise; + /** + * Transitions to the start of a battle. + * Will select 3 daily run starters with a fixed seed of "test" + * (see `DailyRunConfig.getDailyRunStarters` in `daily-run.ts` for more info). + * @returns A promise that resolves when the battle is started. + * @deprecated - Specifying the starters helps prevent inconsistencies from internal RNG changes. + */ + async startBattle(): Promise; async startBattle(species?: SpeciesId[]): Promise { await this.runToSummon(species); diff --git a/test/testUtils/helpers/field-helper.ts b/test/testUtils/helpers/field-helper.ts index 08b7a210e68..aa01ef7497d 100644 --- a/test/testUtils/helpers/field-helper.ts +++ b/test/testUtils/helpers/field-helper.ts @@ -20,9 +20,9 @@ export class FieldHelper extends GameManagerHelper { * Passthrough for {@linkcode globalScene.getPlayerPokemon} that adds an `undefined` check for * the Pokemon so that the return type for the function doesn't have `undefined`. * This removes the need to add a `!` like when calling `game.scene.getPlayerPokemon()!`. - * @param includeSwitching Whether a pokemon that is currently switching out is valid, default `true` - * @returns The first {@linkcode PlayerPokemon} that is {@linkcode globalScene.getPlayerField on the field} - * and {@linkcode PlayerPokemon.isActive is active} + * @param includeSwitching - Whether a pokemon that is currently switching out is valid, default `true` + * @returns The first {@linkcode PlayerPokemon} that is {@linkcode globalScene.getPlayerField | on the field} + * and {@linkcode PlayerPokemon.isActive | is active} * (aka {@linkcode PlayerPokemon.isAllowedInBattle is allowed in battle}). */ public getPlayerPokemon(includeSwitching = true): PlayerPokemon { @@ -36,9 +36,9 @@ export class FieldHelper extends GameManagerHelper { * the Pokemon so that the return type for the function doesn't have `undefined`. * This removes the need to add a `!` like when calling `game.scene.getEnemyPokemon()!`. * @param includeSwitching Whether a pokemon that is currently switching out is valid, default `true` - * @returns The first {@linkcode EnemyPokemon} that is {@linkcode globalScene.getEnemyField on the field} - * and {@linkcode EnemyPokemon.isActive is active} - * (aka {@linkcode EnemyPokemon.isAllowedInBattle is allowed in battle}). + * @returns The first {@linkcode EnemyPokemon} that is {@linkcode globalScene.getEnemyField | on the field} + * and {@linkcode EnemyPokemon.isActive | is active} + * (aka {@linkcode EnemyPokemon.isAllowedInBattle | is allowed in battle}). */ public getEnemyPokemon(includeSwitching = true): EnemyPokemon { const pokemon = this.game.scene.getEnemyPokemon(includeSwitching); @@ -50,7 +50,9 @@ export class FieldHelper extends GameManagerHelper { * @returns The {@linkcode BattlerIndex | indexes} of Pokemon on the field in order of decreasing Speed. * Speed ties are returned in increasing order of index. * - * Note: Trick Room does not modify the speed of Pokemon on the field. + * @remarks + * This does not account for Trick Room as it does not modify the _speed_ of Pokemon on the field, + * only their turn order. */ public getSpeedOrder(): BattlerIndex[] { return this.game.scene @@ -60,7 +62,8 @@ export class FieldHelper extends GameManagerHelper { } /** - * Mocks a pokemon's ability, overriding its existing ability (takes precedence over global overrides) + * Mocks a pokemon's ability, overriding its existing ability (takes precedence over global overrides). + * Useful for giving exactly 1 Pokemon in a double battle a certain ability (rather than all pokemon). * @param pokemon - The pokemon to mock the ability of * @param ability - The ability to be mocked * @returns A {@linkcode MockInstance} object @@ -72,16 +75,15 @@ export class FieldHelper extends GameManagerHelper { } /** - * Forces a pokemon to be terastallized. Defaults to the pokemon's primary type if not specified. - * - * This function only mocks the Pokemon's tera-related variables; it does NOT activate any tera-related abilities. + * Force a given Pokemon to be terastallized to the given type. * * @param pokemon - The pokemon to terastallize. - * @param teraType - (optional) The {@linkcode PokemonType} to terastallize it as. + * @param teraType - The {@linkcode PokemonType} to terastallize into; defaults to the pokemon's primary type. + * @remarks + * This function only mocks the Pokemon's tera-related variables; it does NOT activate any tera-related abilities. */ - public forceTera(pokemon: Pokemon, teraType?: PokemonType): void { + public forceTera(pokemon: Pokemon, teraType: PokemonType = pokemon.getSpeciesForm(true).type1): void { vi.spyOn(pokemon, "isTerastallized", "get").mockReturnValue(true); - teraType ??= pokemon.getSpeciesForm(true).type1; vi.spyOn(pokemon, "teraType", "get").mockReturnValue(teraType); } } diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index 878a265ce84..c7dea05b095 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -154,7 +154,7 @@ export class MoveHelper extends GameManagerHelper { * Changes a pokemon's moveset to the given move(s). * Used when the normal moveset override can't be used (such as when it's necessary to check or update properties of the moveset). * @param pokemon - The {@linkcode Pokemon} being modified - * @param moveset - The {@linkcode MoveId} (single or array) to change the Pokemon's moveset to + * @param moveset - The {@linkcode MoveId} (single or array) to change the Pokemon's moveset to. */ public changeMoveset(pokemon: Pokemon, moveset: MoveId | MoveId[]): void { if (!Array.isArray(moveset)) { @@ -169,10 +169,14 @@ export class MoveHelper extends GameManagerHelper { } /** - * Forces the next enemy selecting a move to use the given move in its moveset + * Forces the next enemy selecting a move to use the given move _in its moveset_ * against the given target (if applicable). - * @param moveId The {@linkcode MoveId | move} the enemy will use - * @param target (Optional) the {@linkcode BattlerIndex | target} which the enemy will use the given move against + * @param moveId - The {@linkcode Move | move ID} the enemy will be forced to use. + * @param target - The {@linkcode BattlerIndex | target} against which the enemy will use the given move; + * defaults to normal target selection priorities if omitted or not single-target. + * @remarks + * If you do not need to check for changes in the enemy's moveset as part of the test, it may be + * best to use {@linkcode forceEnemyMove} instead. */ public async selectEnemyMove(moveId: MoveId, target?: BattlerIndex) { // Wait for the next EnemyCommandPhase to start @@ -200,14 +204,18 @@ export class MoveHelper extends GameManagerHelper { } /** - * Forces the next enemy selecting a move to use the given move against the given target (if applicable). + * Modify the moveset of the next enemy selecting a move to contain only the given move, and then + * selects it to be used during the next {@linkcode EnemyCommandPhase} against the given targets. * - * Warning: Overwrites the pokemon's moveset and disables the moveset override! + * Does not require the given move to be in the enemy's moveset beforehand, + * but **overwrites the pokemon's moveset** and **disables any prior moveset overrides**! * - * Note: If you need to check for changes in the enemy's moveset as part of the test, it may be + * @param moveId - The {@linkcode Move | move ID} the enemy will be forced to use. + * @param target - The {@linkcode BattlerIndex | target} against which the enemy will use the given move; + * defaults to normal target selection priorities if omitted or not single-target. + * @remarks + * If you need to check for changes in the enemy's moveset as part of the test, it may be * best to use {@linkcode changeMoveset} and {@linkcode selectEnemyMove} instead. - * @param moveId The {@linkcode MoveId | move} the enemy will use - * @param target (Optional) the {@linkcode BattlerIndex | target} which the enemy will use the given move against */ public async forceEnemyMove(moveId: MoveId, target?: BattlerIndex) { // Wait for the next EnemyCommandPhase to start From 35a09af4750229c7fb588affd0c59cf40c6db4f7 Mon Sep 17 00:00:00 2001 From: Bertie690 <136088738+Bertie690@users.noreply.github.com> Date: Sat, 7 Jun 2025 22:29:10 -0400 Subject: [PATCH 21/23] [Utils] Create utility function `randSeedFloat` (#5767) --- src/battle.ts | 3 ++- src/data/abilities/ability.ts | 6 +++--- src/data/moves/move.ts | 6 +++--- src/data/pokemon-species.ts | 16 ++++++++++++---- src/field/pokemon.ts | 3 ++- src/modifier/modifier.ts | 28 ++++++++++++++-------------- src/utils/common.ts | 31 +++++++++++++++++++++++-------- 7 files changed, 59 insertions(+), 34 deletions(-) diff --git a/src/battle.ts b/src/battle.ts index dbfed57ae41..2ebfb634751 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -8,6 +8,7 @@ import { shiftCharCodes, randSeedItem, randInt, + randSeedFloat, } from "#app/utils/common"; import Trainer, { TrainerVariant } from "./field/trainer"; import type { GameMode } from "./game-mode"; @@ -150,7 +151,7 @@ export default class Battle { randSeedGaussForLevel(value: number): number { let rand = 0; for (let i = value; i > 0; i--) { - rand += Phaser.Math.RND.realInRange(0, 1); + rand += randSeedFloat(); } return rand / value; } diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index e680a12f9fe..8c95364237b 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -1,5 +1,5 @@ import { HitResult, MoveResult, PlayerPokemon } from "#app/field/pokemon"; -import { BooleanHolder, NumberHolder, toDmgValue, isNullOrUndefined, randSeedItem, randSeedInt, type Constructor } from "#app/utils/common"; +import { BooleanHolder, NumberHolder, toDmgValue, isNullOrUndefined, randSeedItem, randSeedInt, type Constructor, randSeedFloat } from "#app/utils/common"; import { getPokemonNameWithAffix } from "#app/messages"; import { BattlerTagLapseType, GroundedTag } from "#app/data/battler-tags"; import { getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "#app/data/status-effect"; @@ -4092,8 +4092,8 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr { } // Clamp procChance to [0, 1]. Skip if didn't proc (less than pass) - const pass = Phaser.Math.RND.realInRange(0, 1); - return Phaser.Math.Clamp(this.procChance(pokemon), 0, 1) >= pass; + const pass = randSeedFloat(); + return this.procChance(pokemon) >= pass; } override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 94d0ae50523..d44ecbed4cf 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -29,7 +29,7 @@ import { } from "../status-effect"; import { getTypeDamageMultiplier } from "../type"; import { PokemonType } from "#enums/pokemon-type"; -import { BooleanHolder, NumberHolder, isNullOrUndefined, toDmgValue, randSeedItem, randSeedInt, getEnumValues, toReadableString, type Constructor } from "#app/utils/common"; +import { BooleanHolder, NumberHolder, isNullOrUndefined, toDmgValue, randSeedItem, randSeedInt, getEnumValues, toReadableString, type Constructor, randSeedFloat } from "#app/utils/common"; import { WeatherType } from "#enums/weather-type"; import type { ArenaTrapTag } from "../arena-tag"; import { ArenaTagSide, WeakenMoveTypeTag } from "../arena-tag"; @@ -2547,8 +2547,8 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const rand = Phaser.Math.RND.realInRange(0, 1); - if (rand >= this.chance) { + const rand = randSeedFloat(); + if (rand > this.chance) { return false; } diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index c5d798b5841..36a8bbb0520 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -8,7 +8,14 @@ import type { AnySound } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; import type { GameMode } from "#app/game-mode"; import { DexAttr, type StarterMoveset } from "#app/system/game-data"; -import { isNullOrUndefined, capitalizeString, randSeedInt, randSeedGauss, randSeedItem } from "#app/utils/common"; +import { + isNullOrUndefined, + capitalizeString, + randSeedInt, + randSeedGauss, + randSeedItem, + randSeedFloat, +} from "#app/utils/common"; import { uncatchableSpecies } from "#app/data/balance/biomes"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { GrowthRate } from "#app/data/exp"; @@ -750,7 +757,7 @@ export abstract class PokemonSpeciesForm { let paletteColors: Map = new Map(); const originalRandom = Math.random; - Math.random = Phaser.Math.RND.frac; + Math.random = randSeedFloat; globalScene.executeWithSeedOffset( () => { @@ -773,6 +780,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali readonly mythical: boolean; readonly species: string; readonly growthRate: GrowthRate; + /** The chance (as a decimal) for this Species to be male, or `null` for genderless species */ readonly malePercent: number | null; readonly genderDiffs: boolean; readonly canChangeForm: boolean; @@ -889,7 +897,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali return Gender.GENDERLESS; } - if (Phaser.Math.RND.realInRange(0, 1) <= this.malePercent) { + if (randSeedFloat() <= this.malePercent) { return Gender.MALE; } return Gender.FEMALE; @@ -1138,7 +1146,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali } } - if (noEvolutionChance === 1 || Phaser.Math.RND.realInRange(0, 1) < noEvolutionChance) { + if (noEvolutionChance === 1 || randSeedFloat() <= noEvolutionChance) { return this.speciesId; } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index a6d41074700..5b38419e708 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -65,6 +65,7 @@ import { rgbToHsv, deltaRgb, isBetween, + randSeedFloat, type nil, type Constructor, randSeedIntRange, @@ -5225,7 +5226,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let fusionPaletteColors: Map; const originalRandom = Math.random; - Math.random = () => Phaser.Math.RND.realInRange(0, 1); + Math.random = () => randSeedFloat(); globalScene.executeWithSeedOffset( () => { diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index e1517b3bcde..81d9bf7189c 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -15,7 +15,7 @@ import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import type { VoucherType } from "#app/system/voucher"; import { Command } from "#app/ui/command-ui-handler"; import { addTextObject, TextStyle } from "#app/ui/text"; -import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, toDmgValue } from "#app/utils/common"; +import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, randSeedFloat, toDmgValue } from "#app/utils/common"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; import type { MoveId } from "#enums/move-id"; @@ -3351,7 +3351,7 @@ export class ContactHeldItemTransferChanceModifier extends HeldItemTransferModif } getTransferredItemCount(): number { - return Phaser.Math.RND.realInRange(0, 1) < this.chance * this.getStackCount() ? 1 : 0; + return randSeedFloat() <= this.chance * this.getStackCount() ? 1 : 0; } getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierType): string { @@ -3629,7 +3629,7 @@ export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifi * @returns `true` if the {@linkcode Pokemon} was affected */ override apply(enemyPokemon: Pokemon): boolean { - if (Phaser.Math.RND.realInRange(0, 1) < this.chance * this.getStackCount()) { + if (randSeedFloat() <= this.chance * this.getStackCount()) { return enemyPokemon.trySetStatus(this.effect, true); } @@ -3664,21 +3664,21 @@ export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifier } /** - * Applies {@linkcode EnemyStatusEffectHealChanceModifier} - * @param enemyPokemon The {@linkcode Pokemon} to heal + * Applies {@linkcode EnemyStatusEffectHealChanceModifier} to randomly heal status. + * @param enemyPokemon - The {@linkcode Pokemon} to heal * @returns `true` if the {@linkcode Pokemon} was healed */ override apply(enemyPokemon: Pokemon): boolean { - if (enemyPokemon.status && Phaser.Math.RND.realInRange(0, 1) < this.chance * this.getStackCount()) { - globalScene.phaseManager.queueMessage( - getStatusEffectHealText(enemyPokemon.status.effect, getPokemonNameWithAffix(enemyPokemon)), - ); - enemyPokemon.resetStatus(); - enemyPokemon.updateInfo(); - return true; + if (!enemyPokemon.status || randSeedFloat() > this.chance * this.getStackCount()) { + return false; } - return false; + globalScene.phaseManager.queueMessage( + getStatusEffectHealText(enemyPokemon.status.effect, getPokemonNameWithAffix(enemyPokemon)), + ); + enemyPokemon.resetStatus(); + enemyPokemon.updateInfo(); + return true; } getMaxStackCount(): number { @@ -3757,7 +3757,7 @@ export class EnemyFusionChanceModifier extends EnemyPersistentModifier { * @returns `true` if the {@linkcode EnemyPokemon} is a fusion */ override apply(isFusion: BooleanHolder): boolean { - if (Phaser.Math.RND.realInRange(0, 1) >= this.chance * this.getStackCount()) { + if (randSeedFloat() > this.chance * this.getStackCount()) { return false; } diff --git a/src/utils/common.ts b/src/utils/common.ts index 29923d7ddd4..56fa3b5c698 100644 --- a/src/utils/common.ts +++ b/src/utils/common.ts @@ -58,8 +58,8 @@ export function randSeedGauss(stdev: number, mean = 0): number { if (!stdev) { return 0; } - const u = 1 - Phaser.Math.RND.realInRange(0, 1); - const v = Phaser.Math.RND.realInRange(0, 1); + const u = 1 - randSeedFloat(); + const v = randSeedFloat(); const z = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v); return z * stdev + mean; } @@ -88,9 +88,9 @@ export function randInt(range: number, min = 0): number { } /** - * Generates a random number using the global seed, or the current battle's seed if called via `Battle.randSeedInt` - * @param range How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min} - * @param min The minimum integer to pick, default `0` + * Generate a random integer using the global seed, or the current battle's seed if called via `Battle.randSeedInt` + * @param range - How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min} + * @param min - The minimum integer to pick, default `0` * @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1) */ export function randSeedInt(range: number, min = 0): number { @@ -119,6 +119,14 @@ export function randIntRange(min: number, max: number): number { return randInt(max - min, min); } +/** + * Generate and return a random real number between `0` and `1` using the global seed. + * @returns A random floating-point number between `0` and `1` + */ +export function randSeedFloat(): number { + return Phaser.Math.RND.frac(); +} + export function randItem(items: T[]): T { return items.length === 1 ? items[0] : items[randInt(items.length)]; } @@ -517,12 +525,19 @@ export function capitalizeString(str: string, sep: string, lowerFirstChar = true return null; } -export function isNullOrUndefined(object: any): object is null | undefined { - return object === null || object === undefined; +/** + * Report whether a given value is nullish (`null`/`undefined`). + * @param val - The value whose nullishness is being checked + * @returns `true` if `val` is either `null` or `undefined` + */ +export function isNullOrUndefined(val: any): val is null | undefined { + return val === null || val === undefined; } /** - * Capitalizes the first letter of a string + * Capitalize the first letter of a string. + * @param str - The string whose first letter is being capitalized + * @return The original string with its first letter capitalized */ export function capitalizeFirstLetter(str: string) { return str.charAt(0).toUpperCase() + str.slice(1); From 75beec12a892d28b51d8cbd859a28fc793617736 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sat, 7 Jun 2025 21:43:30 -0700 Subject: [PATCH 22/23] [Dev] Enable Biome checking of `ability.ts` (#5948) --- biome.jsonc | 1 - src/data/abilities/ability.ts | 4084 +++++++++++++++++++++++---------- 2 files changed, 2915 insertions(+), 1170 deletions(-) diff --git a/biome.jsonc b/biome.jsonc index 141c44dc87d..82ce7c308dc 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -31,7 +31,6 @@ "src/overrides.ts", // TODO: these files are too big and complex, ignore them until their respective refactors "src/data/moves/move.ts", - "src/data/abilities/ability.ts", // this file is just too big: "src/data/balance/tms.ts" diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 8c95364237b..9269f84d269 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -1,8 +1,21 @@ -import { HitResult, MoveResult, PlayerPokemon } from "#app/field/pokemon"; -import { BooleanHolder, NumberHolder, toDmgValue, isNullOrUndefined, randSeedItem, randSeedInt, type Constructor, randSeedFloat } from "#app/utils/common"; +import { HitResult, MoveResult } from "#app/field/pokemon"; +import { + BooleanHolder, + NumberHolder, + toDmgValue, + isNullOrUndefined, + randSeedItem, + randSeedInt, + type Constructor, + randSeedFloat, +} from "#app/utils/common"; import { getPokemonNameWithAffix } from "#app/messages"; import { BattlerTagLapseType, GroundedTag } from "#app/data/battler-tags"; -import { getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "#app/data/status-effect"; +import { + getNonVolatileStatusEffects, + getStatusEffectDescriptor, + getStatusEffectHealText, +} from "#app/data/status-effect"; import { Gender } from "#app/data/gender"; import { AttackMove, @@ -24,7 +37,11 @@ import { allMoves } from "../data-lists"; import { ArenaTagSide } from "#app/data/arena-tag"; import { BerryModifier, HitHealModifier, PokemonHeldItemModifier } from "#app/modifier/modifier"; import { TerrainType } from "#app/data/terrain"; -import { SpeciesFormChangeAbilityTrigger, SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "#app/data/pokemon-forms"; +import { + SpeciesFormChangeAbilityTrigger, + SpeciesFormChangeRevertWeatherFormTrigger, + SpeciesFormChangeWeatherTrigger, +} from "#app/data/pokemon-forms"; import i18next from "i18next"; import { Command } from "#app/ui/command-ui-handler"; import { BerryModifierType } from "#app/modifier/modifier-type"; @@ -65,13 +82,19 @@ import { CommonAnim } from "../battle-anims"; import { getBerryEffectFunc } from "../berry"; import { BerryUsedEvent } from "#app/events/battle-scene"; - // Type imports import type { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import type { Weather } from "#app/data/weather"; import type { BattlerTag } from "#app/data/battler-tags"; -import type { AbAttrCondition, PokemonDefendCondition, PokemonStatStageChangeCondition, PokemonAttackCondition, AbAttrApplyFunc, AbAttrSuccessFunc } from "#app/@types/ability-types"; +import type { + AbAttrCondition, + PokemonDefendCondition, + PokemonStatStageChangeCondition, + PokemonAttackCondition, + AbAttrApplyFunc, + AbAttrSuccessFunc, +} from "#app/@types/ability-types"; import type { BattlerIndex } from "#app/battle"; import type Move from "#app/data/moves/move"; import type { ArenaTrapTag, SuppressAbilitiesTag } from "#app/data/arena-tag"; @@ -83,12 +106,21 @@ export class BlockRecoilDamageAttr extends AbAttr { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]) { - return i18next.t("abilityTriggers:blockRecoilDamage", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName }); + return i18next.t("abilityTriggers:blockRecoilDamage", { + pokemonName: getPokemonNameWithAffix(pokemon), + abilityName: abilityName, + }); } } @@ -106,7 +138,13 @@ export class DoubleBattleChanceAbAttr extends AbAttr { * Increases the chance of a double battle occurring * @param args [0] {@linkcode NumberHolder} for double battle chance */ - override apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { const doubleBattleChance = args[0] as NumberHolder; // This is divided because the chance is generated as a number from 0 to doubleBattleChance.value using Utils.randSeedInt // A double battle will initiate if the generated number is 0 @@ -115,28 +153,28 @@ export class DoubleBattleChanceAbAttr extends AbAttr { } export class PostBattleInitAbAttr extends AbAttr { - canApplyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + canApplyPostBattleInit(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return true; } - applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} + applyPostBattleInit(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} } export class PostBattleInitFormChangeAbAttr extends PostBattleInitAbAttr { private formFunc: (p: Pokemon) => number; - constructor(formFunc: ((p: Pokemon) => number)) { + constructor(formFunc: (p: Pokemon) => number) { super(false); this.formFunc = formFunc; } - override canApplyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostBattleInit(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { const formIndex = this.formFunc(pokemon); return formIndex !== pokemon.formIndex && !simulated; } - override applyPostBattleInit(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostBattleInit(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } } @@ -152,7 +190,13 @@ export class PostTeraFormChangeStatChangeAbAttr extends AbAttr { this.stages = stages; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder | null, args: any[]): void { + override apply( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _cancelled: BooleanHolder | null, + _args: any[], + ): void { const statStageChangePhases: StatStageChangePhase[] = []; if (!simulated) { @@ -180,11 +224,17 @@ export class ClearWeatherAbAttr extends AbAttr { this.weather = weather; } - public override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + public override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return globalScene.arena.canSetWeather(WeatherType.NONE); } - public override apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: BooleanHolder, args: any[]): void { + public override apply( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _cancelled: BooleanHolder, + _args: any[], + ): void { if (!simulated) { globalScene.arena.trySetWeather(WeatherType.NONE, pokemon); } @@ -206,11 +256,17 @@ export class ClearTerrainAbAttr extends AbAttr { this.terrain = terrain; } - public override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + public override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return globalScene.arena.canSetTerrain(TerrainType.NONE); } - public override apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: BooleanHolder, args: any[]): void { + public override apply( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _cancelled: BooleanHolder, + _args: any[], + ): void { if (!simulated) { globalScene.arena.trySetTerrain(TerrainType.NONE, true, pokemon); } @@ -221,35 +277,56 @@ type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: Move export class PreDefendAbAttr extends AbAttr { canApplyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move | null, - cancelled: BooleanHolder | null, - args: any[]): boolean { + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move | null, + _cancelled: BooleanHolder | null, + _args: any[], + ): boolean { return true; } applyPreDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move | null, - cancelled: BooleanHolder | null, - args: any[], + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move | null, + _cancelled: BooleanHolder | null, + _args: any[], ): void {} } export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move | null, cancelled: BooleanHolder | null, args: any[]): boolean { - return pokemon.isFullHp() - && pokemon.getMaxHp() > 1 //Checks if pokemon has wonder_guard (which forces 1hp) - && (args[0] as NumberHolder).value >= pokemon.hp; //Damage >= hp + override canApplyPreDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move | null, + _cancelled: BooleanHolder | null, + args: any[], + ): boolean { + return ( + pokemon.isFullHp() && + // Checks if pokemon has wonder_guard (which forces 1hp) + pokemon.getMaxHp() > 1 && + // Damage >= hp + (args[0] as NumberHolder).value >= pokemon.hp + ); } - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _attacker: Pokemon, + _move: Move, + _cancelled: BooleanHolder, + _args: any[], + ): void { if (!simulated) { pokemon.addTag(BattlerTagType.STURDY, 1); } @@ -257,14 +334,20 @@ export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { } export class BlockItemTheftAbAttr extends AbAttr { - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]) { + getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]) { return i18next.t("abilityTriggers:blockItemTheft", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - abilityName + abilityName, }); } } @@ -274,11 +357,17 @@ export class StabBoostAbAttr extends AbAttr { super(false); } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { return (args[0] as NumberHolder).value > 1; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[0] as NumberHolder).value += 0.5; } } @@ -287,18 +376,34 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr { protected condition: PokemonDefendCondition; private damageMultiplier: number; - constructor(condition: PokemonDefendCondition, damageMultiplier: number, showAbility: boolean = false) { + constructor(condition: PokemonDefendCondition, damageMultiplier: number, showAbility = false) { super(showAbility); this.condition = condition; this.damageMultiplier = damageMultiplier; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder | null, args: any[]): boolean { + override canApplyPreDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _cancelled: BooleanHolder | null, + _args: any[], + ): boolean { return this.condition(pokemon, attacker, move); } - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[0] as NumberHolder).value = toDmgValue((args[0] as NumberHolder).value * this.damageMultiplier); } } @@ -320,7 +425,15 @@ export class AlliedFieldDamageReductionAbAttr extends PreDefendAbAttr { * @param args * - `[0]` {@linkcode NumberHolder} - The damage being dealt */ - override applyPreDefend(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _attacker: Pokemon, _move: Move, _cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move, + _cancelled: BooleanHolder, + args: any[], + ): void { const damage = args[0] as NumberHolder; damage.value = toDmgValue(damage.value * this.damageMultiplier); } @@ -328,7 +441,7 @@ export class AlliedFieldDamageReductionAbAttr extends PreDefendAbAttr { export class ReceivedTypeDamageMultiplierAbAttr extends ReceivedMoveDamageMultiplierAbAttr { constructor(moveType: PokemonType, damageMultiplier: number) { - super((target, user, move) => user.getMoveType(move) === moveType, damageMultiplier, false); + super((_target, user, move) => user.getMoveType(move) === moveType, damageMultiplier, false); } } @@ -342,6 +455,7 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { private immuneType: PokemonType | null; private condition: AbAttrCondition | null; + // TODO: `immuneType` shouldn't be able to be `null` constructor(immuneType: PokemonType | null, condition?: AbAttrCondition) { super(true); @@ -349,21 +463,41 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { this.condition = condition ?? null; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder | null, args: any[]): boolean { - return ![ MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE ].includes(move.moveTarget) && attacker !== pokemon && attacker.getMoveType(move) === this.immuneType; + override canApplyPreDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _cancelled: BooleanHolder | null, + _args: any[], + ): boolean { + return ( + ![MoveTarget.BOTH_SIDES, MoveTarget.ENEMY_SIDE, MoveTarget.USER_SIDE].includes(move.moveTarget) && + attacker !== pokemon && + attacker.getMoveType(move) === this.immuneType + ); } /** * Applies immunity if this ability grants immunity to the type of the given move. - * @param pokemon {@linkcode Pokemon} The defending Pokemon. - * @param passive - Whether the ability is passive. - * @param attacker {@linkcode Pokemon} The attacking Pokemon. - * @param move {@linkcode Move} The attacking move. - * @param cancelled {@linkcode BooleanHolder} - A holder for a boolean value indicating if the move was cancelled. + * @param _pokemon {@linkcode Pokemon} The defending Pokemon. + * @param _passive - Whether the ability is passive. + * @param _attacker {@linkcode Pokemon} The attacking Pokemon. + * @param _move {@linkcode Move} The attacking move. + * @param _cancelled {@linkcode BooleanHolder} - A holder for a boolean value indicating if the move was cancelled. * @param args [0] {@linkcode NumberHolder} gets set to 0 if move is immuned by an ability. * @param args [1] - Whether the move is simulated. */ - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[0] as NumberHolder).value = 0; } @@ -377,13 +511,25 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { } export class AttackTypeImmunityAbAttr extends TypeImmunityAbAttr { + // biome-ignore lint/complexity/noUselessConstructor: Changes the type of `immuneType` constructor(immuneType: PokemonType, condition?: AbAttrCondition) { super(immuneType, condition); } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder | null, args: any[]): boolean { - return move.category !== MoveCategory.STATUS && !move.hasAttr(NeutralDamageAgainstFlyingTypeMultiplierAttr) - && super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); + override canApplyPreDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + cancelled: BooleanHolder | null, + args: any[], + ): boolean { + return ( + move.category !== MoveCategory.STATUS && + !move.hasAttr(NeutralDamageAgainstFlyingTypeMultiplierAttr) && + super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args) + ); } /** @@ -391,27 +537,61 @@ export class AttackTypeImmunityAbAttr extends TypeImmunityAbAttr { * Type immunity abilities that do not give additional benefits (HP recovery, stat boosts, etc) are not immune to status moves of the type * Example: Levitate */ - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + cancelled: BooleanHolder, + args: any[], + ): void { // this is a hacky way to fix the Levitate/Thousand Arrows interaction, but it works for now... super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); } } export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { + // biome-ignore lint/complexity/noUselessConstructor: Changes the type of `immuneType` constructor(immuneType: PokemonType) { super(immuneType); } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder | null, args: any[]): boolean { + override canApplyPreDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + cancelled: BooleanHolder | null, + args: any[], + ): boolean { return super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); } - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + cancelled: BooleanHolder, + args: any[], + ): void { super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); if (!pokemon.isFullHp() && !simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / 4), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + globalScene.phaseManager.unshiftPhase( + new PokemonHealPhase( + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / 4), + i18next.t("abilityTriggers:typeImmunityHeal", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + }), + true, + ), + ); cancelled.value = true; // Suppresses "No Effect" message } } @@ -428,15 +608,33 @@ class TypeImmunityStatStageChangeAbAttr extends TypeImmunityAbAttr { this.stages = stages; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder | null, args: any[]): boolean { + override canApplyPreDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + cancelled: BooleanHolder | null, + args: any[], + ): boolean { return super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); } - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + cancelled: BooleanHolder, + args: any[], + ): void { super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); cancelled.value = true; // Suppresses "No Effect" message if (!simulated) { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), true, [this.stat], this.stages), + ); } } } @@ -452,11 +650,27 @@ class TypeImmunityAddBattlerTagAbAttr extends TypeImmunityAbAttr { this.turnCount = turnCount; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder | null, args: any[]): boolean { + override canApplyPreDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + cancelled: BooleanHolder | null, + args: any[], + ): boolean { return super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); } - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + cancelled: BooleanHolder, + args: any[], + ): void { super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); cancelled.value = true; // Suppresses "No Effect" message if (!simulated) { @@ -470,22 +684,39 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { super(null, condition); } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder | null, args: any[]): boolean { - const modifierValue = args.length > 0 - ? (args[0] as NumberHolder).value - : pokemon.getAttackTypeEffectiveness(attacker.getMoveType(move), attacker, undefined, undefined, move); + override canApplyPreDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _cancelled: BooleanHolder | null, + args: any[], + ): boolean { + const modifierValue = + args.length > 0 + ? (args[0] as NumberHolder).value + : pokemon.getAttackTypeEffectiveness(attacker.getMoveType(move), attacker, undefined, undefined, move); return move instanceof AttackMove && modifierValue < 2; } - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move, + cancelled: BooleanHolder, + args: any[], + ): void { cancelled.value = true; // Suppresses "No Effect" message (args[0] as NumberHolder).value = 0; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:nonSuperEffectiveImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - abilityName + abilityName, }); } } @@ -496,94 +727,128 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { * @extends PreDefendAbAttr */ export class FullHpResistTypeAbAttr extends PreDefendAbAttr { - - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move | null, cancelled: BooleanHolder | null, args: any[]): boolean { + override canApplyPreDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + move: Move | null, + _cancelled: BooleanHolder | null, + args: any[], + ): boolean { const typeMultiplier = args[0]; - return (typeMultiplier && typeMultiplier instanceof NumberHolder) && !(move && move.hasAttr(FixedDamageAttr)) && pokemon.isFullHp() && typeMultiplier.value > 0.5; + return ( + typeMultiplier instanceof NumberHolder && + !move?.hasAttr(FixedDamageAttr) && + pokemon.isFullHp() && + typeMultiplier.value > 0.5 + ); } /** * Reduces a type multiplier to 0.5 if the source is at full HP. * @param pokemon {@linkcode Pokemon} the Pokemon with this ability - * @param passive n/a - * @param simulated n/a (this doesn't change game state) - * @param attacker n/a - * @param move {@linkcode Move} the move being used on the source - * @param cancelled n/a + * @param _passive n/a + * @param _simulated n/a (this doesn't change game state) + * @param _attacker n/a + * @param _move {@linkcode Move} the move being used on the source + * @param _cancelled n/a * @param args `[0]` a container for the move's current type effectiveness multiplier */ override applyPreDefend( pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move | null, - cancelled: BooleanHolder | null, - args: any[]): void { + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move | null, + _cancelled: BooleanHolder | null, + args: any[], + ): void { const typeMultiplier = args[0]; typeMultiplier.value = 0.5; pokemon.turnData.moveEffectiveness = 0.5; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:fullHpResistType", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }); } } export class PostDefendAbAttr extends AbAttr { canApplyPostDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult | null, - args: any[]): boolean { + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { return true; } applyPostDefend( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - attacker: Pokemon, - move: Move, - hitResult: HitResult | null, - args: any[], + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move, + _hitResult: HitResult | null, + _args: any[], ): void {} } export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { - - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder | null, args: any[]): boolean { - return !(move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY) && move.getPriority(attacker) > 0 && !move.isMultiTarget(); + override canApplyPreDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _cancelled: BooleanHolder | null, + _args: any[], + ): boolean { + return ( + !(move.moveTarget === MoveTarget.USER || move.moveTarget === MoveTarget.NEAR_ALLY) && + move.getPriority(attacker) > 0 && + !move.isMultiTarget() + ); } - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } } export class PostStatStageChangeAbAttr extends AbAttr { canApplyPostStatStageChange( - pokemon: Pokemon, - simulated: boolean, - statsChanged: BattleStat[], - stagesChanged: number, - selfTarget: boolean, - args: any[]): boolean { + _pokemon: Pokemon, + _simulated: boolean, + _statsChanged: BattleStat[], + _stagesChanged: number, + _selfTarget: boolean, + _args: any[], + ): boolean { return true; } applyPostStatStageChange( - pokemon: Pokemon, - simulated: boolean, - statsChanged: BattleStat[], - stagesChanged: number, - selfTarget: boolean, - args: any[], + _pokemon: Pokemon, + _simulated: boolean, + _statsChanged: BattleStat[], + _stagesChanged: number, + _selfTarget: boolean, + _args: any[], ): void {} } @@ -596,15 +861,31 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr { this.immuneCondition = immuneCondition; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder | null, args: any[]): boolean { + override canApplyPreDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _cancelled: BooleanHolder | null, + _args: any[], + ): boolean { return this.immuneCondition(pokemon, attacker, move); } - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:moveImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }); } } @@ -616,17 +897,32 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr { * @extends PreDefendAbAttr */ export class WonderSkinAbAttr extends PreDefendAbAttr { - constructor() { super(false); } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder | null, args: any[]): boolean { + override canApplyPreDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + move: Move, + _cancelled: BooleanHolder | null, + args: any[], + ): boolean { const moveAccuracy = args[0] as NumberHolder; return move.category === MoveCategory.STATUS && moveAccuracy.value >= 50; } - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move, + _cancelled: BooleanHolder, + args: any[], + ): void { const moveAccuracy = args[0] as NumberHolder; moveAccuracy.value = 50; } @@ -642,13 +938,31 @@ export class MoveImmunityStatStageChangeAbAttr extends MoveImmunityAbAttr { this.stages = stages; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder | null, args: any[]): boolean { + override canApplyPreDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + cancelled: BooleanHolder | null, + args: any[], + ): boolean { return !simulated && super.canApplyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); } - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + cancelled: BooleanHolder, + args: any[], + ): void { super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), true, [this.stat], this.stages), + ); } } /** @@ -657,8 +971,15 @@ export class MoveImmunityStatStageChangeAbAttr extends MoveImmunityAbAttr { * @see {@linkcode applyPostDefend} */ export class ReverseDrainAbAttr extends PostDefendAbAttr { - - override canApplyPostDefend(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _attacker: Pokemon, move: Move, _hitResult: HitResult | null, args: any[]): boolean { + override canApplyPostDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { return move.hasAttr(HitHealAttr); } @@ -666,16 +987,26 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr { * Determines if a damage and draining move was used to check if this ability should stop the healing. * Examples include: Absorb, Draining Kiss, Bitter Blade, etc. * Also displays a message to show this ability was activated. - * @param pokemon {@linkcode Pokemon} with this ability + * @param _pokemon {@linkcode Pokemon} with this ability * @param _passive N/A * @param attacker {@linkcode Pokemon} that is attacking this Pokemon - * @param move {@linkcode PokemonMove} that is being used + * @param _move {@linkcode PokemonMove} that is being used * @param _hitResult N/A * @param _args N/A */ - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + _pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { - globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) })); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) }), + ); } } } @@ -687,7 +1018,13 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr { private selfTarget: boolean; private allOthers: boolean; - constructor(condition: PokemonDefendCondition, stat: BattleStat, stages: number, selfTarget = true, allOthers = false) { + constructor( + condition: PokemonDefendCondition, + stat: BattleStat, + stages: number, + selfTarget = true, + allOthers = false, + ) { super(true); this.condition = condition; @@ -697,23 +1034,48 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr { this.allOthers = allOthers; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { return this.condition(pokemon, attacker, move); } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (simulated) { return; } if (this.allOthers) { const ally = pokemon.getAlly(); - const otherPokemon = !isNullOrUndefined(ally) ? pokemon.getOpponents().concat([ ally ]) : pokemon.getOpponents(); + const otherPokemon = !isNullOrUndefined(ally) ? pokemon.getOpponents().concat([ally]) : pokemon.getOpponents(); for (const other of otherPokemon) { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase((other).getBattlerIndex(), false, [ this.stat ], this.stages)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(other.getBattlerIndex(), false, [this.stat], this.stages), + ); } } else { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase((this.selfTarget ? pokemon : attacker).getBattlerIndex(), this.selfTarget, [ this.stat ], this.stages)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase( + (this.selfTarget ? pokemon : attacker).getBattlerIndex(), + this.selfTarget, + [this.stat], + this.stages, + ), + ); } } } @@ -725,7 +1087,13 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { private stages: number; private selfTarget: boolean; - constructor(condition: PokemonDefendCondition, hpGate: number, stats: BattleStat[], stages: number, selfTarget = true) { + constructor( + condition: PokemonDefendCondition, + hpGate: number, + stats: BattleStat[], + stages: number, + selfTarget = true, + ) { super(true); this.condition = condition; @@ -735,16 +1103,41 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { this.selfTarget = selfTarget; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { const hpGateFlat: number = Math.ceil(pokemon.getMaxHp() * this.hpGate); const lastAttackReceived = pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1]; const damageReceived = lastAttackReceived?.damage || 0; - return this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + damageReceived) > hpGateFlat); + return ( + this.condition(pokemon, attacker, move) && pokemon.hp <= hpGateFlat && pokemon.hp + damageReceived > hpGateFlat + ); } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase((this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.stages)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase( + (this.selfTarget ? pokemon : attacker).getBattlerIndex(), + true, + this.stats, + this.stages, + ), + ); } } } @@ -760,15 +1153,38 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { const tag = globalScene.arena.getTag(this.tagType) as ArenaTrapTag; - return (this.condition(pokemon, attacker, move)) - && (!globalScene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers); + return ( + this.condition(pokemon, attacker, move) && (!globalScene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) + ); } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { - globalScene.arena.addTag(this.tagType, 0, undefined, pokemon.id, pokemon.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER); + globalScene.arena.addTag( + this.tagType, + 0, + undefined, + pokemon.id, + pokemon.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER, + ); } } } @@ -783,14 +1199,35 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { return this.condition(pokemon, attacker, move); } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _attacker: Pokemon, + move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!pokemon.getTag(this.tagType) && !simulated) { pokemon.addTag(this.tagType, undefined, undefined, pokemon.id); - globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name })); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:windPowerCharged", { + pokemonName: getPokemonNameWithAffix(pokemon), + moveName: move.name, + }), + ); } } } @@ -798,22 +1235,38 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr { private type: PokemonType; - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + hitResult: HitResult, + _args: any[], + ): boolean { this.type = attacker.getMoveType(move); const pokemonTypes = pokemon.getTypes(true); return hitResult < HitResult.NO_EFFECT && (simulated || pokemonTypes.length !== 1 || pokemonTypes[0] !== this.type); } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult, + _args: any[], + ): void { const type = attacker.getMoveType(move); - pokemon.summonData.types = [ type ]; + pokemon.summonData.types = [type]; } override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:postDefendTypeChange", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, - typeName: i18next.t(`pokemonInfo:Type.${PokemonType[this.type]}`) + typeName: i18next.t(`pokemonInfo:Type.${PokemonType[this.type]}`), }); } } @@ -827,11 +1280,27 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { this.terrainType = terrainType; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + override canApplyPostDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move, + hitResult: HitResult, + _args: any[], + ): boolean { return hitResult < HitResult.NO_EFFECT && globalScene.arena.canSetTerrain(this.terrainType); } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { globalScene.arena.trySetTerrain(this.terrainType, false, pokemon); } @@ -849,15 +1318,36 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { this.effects = effects; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; - return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && !attacker.status - && (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance) - && attacker.canSetStatus(effect, true, false, pokemon); + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { + const effect = + this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; + return ( + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && + !attacker.status && + (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance) && + attacker.canSetStatus(effect, true, false, pokemon) + ); } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { - const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; + override applyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { + const effect = + this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; attacker.trySetStatus(effect, true, pokemon); } } @@ -867,12 +1357,30 @@ export class EffectSporeAbAttr extends PostDefendContactApplyStatusEffectAbAttr super(10, StatusEffect.POISON, StatusEffect.PARALYSIS, StatusEffect.SLEEP); } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return !(attacker.hasAbility(AbilityId.OVERCOAT) || attacker.isOfType(PokemonType.GRASS)) - && super.canApplyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args); + override canApplyPostDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + hitResult: HitResult | null, + args: any[], + ): boolean { + return ( + !(attacker.hasAbility(AbilityId.OVERCOAT) || attacker.isOfType(PokemonType.GRASS)) && + super.canApplyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args) + ); } - override applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { + override applyPostDefend( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + hitResult: HitResult, + args: any[], + ): void { super.applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args); } } @@ -890,12 +1398,31 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { this.turnCount = turnCount; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && pokemon.randBattleSeedInt(100) < this.chance - && attacker.canAddTag(this.tagType); + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { + return ( + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && + pokemon.randBattleSeedInt(100) < this.chance && + attacker.canAddTag(this.tagType) + ); } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + _pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { attacker.addTag(this.tagType, this.turnCount, move.id, attacker.id); } @@ -913,14 +1440,26 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { this.stages = stages; } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), true, [this.stat], this.stages), + ); } } override getCondition(): AbAttrCondition { - return (pokemon: Pokemon) => pokemon.turnData.attacksReceived.length !== 0 && pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1].critical; + return (pokemon: Pokemon) => + pokemon.turnData.attacksReceived.length !== 0 && + pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1].critical; } } @@ -933,12 +1472,31 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { this.damageRatio = damageRatio; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return !simulated && move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) - && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr); + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { + return ( + !simulated && + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && + !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) + ); } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), { result: HitResult.INDIRECT }); attacker.turnData.damageTaken += toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); } @@ -946,7 +1504,7 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:postDefendContactDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - abilityName + abilityName, }); } } @@ -966,11 +1524,30 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { this.turns = turns; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && !attacker.getTag(BattlerTagType.PERISH_SONG); + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { + return ( + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && + !attacker.getTag(BattlerTagType.PERISH_SONG) + ); } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { attacker.addTag(BattlerTagType.PERISH_SONG, this.turns); pokemon.addTag(BattlerTagType.PERISH_SONG, this.turns); @@ -978,7 +1555,10 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { } override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { - return i18next.t("abilityTriggers:perishBody", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName }); + return i18next.t("abilityTriggers:perishBody", { + pokemonName: getPokemonNameWithAffix(pokemon), + abilityName: abilityName, + }); } } @@ -993,12 +1573,31 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { this.condition = condition; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return (!(this.condition && !this.condition(pokemon, attacker, move)) - && !globalScene.arena.weather?.isImmutable() && globalScene.arena.canSetWeather(this.weatherType)); + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { + return ( + !(this.condition && !this.condition(pokemon, attacker, move)) && + !globalScene.arena.weather?.isImmutable() && + globalScene.arena.canSetWeather(this.weatherType) + ); } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { globalScene.arena.trySetWeather(this.weatherType, pokemon); } @@ -1006,16 +1605,30 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { } export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { - constructor() { - super(); + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { + return ( + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && + attacker.getAbility().isSwappable + ); } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) - && attacker.getAbility().isSwappable; - } - - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, args: any[]): void { + override applyPostDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { const tempAbility = attacker.getAbility(); attacker.setTempAbility(pokemon.getAbility()); @@ -1024,7 +1637,9 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { } override getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { - return i18next.t("abilityTriggers:postDefendAbilitySwap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }); + return i18next.t("abilityTriggers:postDefendAbilitySwap", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + }); } } @@ -1036,12 +1651,31 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { this.ability = ability; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && attacker.getAbility().isSuppressable - && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr); + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { + return ( + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && + attacker.getAbility().isSuppressable && + !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr) + ); } - override applyPostDefend(_pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + _pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { attacker.setTempAbility(allAbilities[this.ability]); } @@ -1050,7 +1684,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:postDefendAbilityGive", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - abilityName + abilityName, }); } } @@ -1066,12 +1700,31 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { this.chance = chance; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return attacker.getTag(BattlerTagType.DISABLED) === null - && move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance); + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult | null, + _args: any[], + ): boolean { + return ( + attacker.getTag(BattlerTagType.DISABLED) === null && + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }) && + (this.chance === -1 || pokemon.randBattleSeedInt(100) < this.chance) + ); } - override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { + override applyPostDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { this.attacker = attacker; this.move = move; @@ -1093,35 +1746,52 @@ export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChang this.stages = stages; } - override canApplyPostStatStageChange(pokemon: Pokemon, simulated: boolean, statStagesChanged: BattleStat[], stagesChanged: integer, selfTarget: boolean, args: any[]): boolean { + override canApplyPostStatStageChange( + pokemon: Pokemon, + _simulated: boolean, + statStagesChanged: BattleStat[], + stagesChanged: number, + selfTarget: boolean, + _args: any[], + ): boolean { return this.condition(pokemon, statStagesChanged, stagesChanged) && !selfTarget; } - override applyPostStatStageChange(pokemon: Pokemon, simulated: boolean, statStagesChanged: BattleStat[], stagesChanged: number, selfTarget: boolean, args: any[]): void { + override applyPostStatStageChange( + pokemon: Pokemon, + simulated: boolean, + _statStagesChanged: BattleStat[], + _stagesChanged: number, + _selfTarget: boolean, + _args: any[], + ): void { if (!simulated) { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase((pokemon).getBattlerIndex(), true, this.statsToChange, this.stages)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.statsToChange, this.stages), + ); } } } export class PreAttackAbAttr extends AbAttr { canApplyPreAttack( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - defender: Pokemon | null, - move: Move, - args: any[]): boolean { + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _defender: Pokemon | null, + _move: Move, + _args: any[], + ): boolean { return true; } applyPreAttack( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - defender: Pokemon | null, - move: Move, - args: any[], + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _defender: Pokemon | null, + _move: Move, + _args: any[], ): void {} } @@ -1138,8 +1808,8 @@ export class MoveEffectChanceMultiplierAbAttr extends AbAttr { this.chanceMultiplier = chanceMultiplier; } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const exceptMoves = [ MoveId.ORDER_UP, MoveId.ELECTRO_SHOT ]; + override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { + const exceptMoves = [MoveId.ORDER_UP, MoveId.ELECTRO_SHOT]; return !((args[0] as NumberHolder).value <= 0 || exceptMoves.includes((args[1] as Move).id)); } @@ -1147,7 +1817,13 @@ export class MoveEffectChanceMultiplierAbAttr extends AbAttr { * @param args [0]: {@linkcode NumberHolder} Move additional effect chance. Has to be higher than or equal to 0. * [1]: {@linkcode MoveId } Move used by the ability user. */ - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[0] as NumberHolder).value *= this.chanceMultiplier; (args[0] as NumberHolder).value = Math.min((args[0] as NumberHolder).value, 100); } @@ -1159,35 +1835,58 @@ export class MoveEffectChanceMultiplierAbAttr extends AbAttr { * @see {@linkcode applyPreDefend} */ export class IgnoreMoveEffectsAbAttr extends PreDefendAbAttr { - constructor(showAbility: boolean = false) { + constructor(showAbility = false) { super(showAbility); } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move | null, cancelled: BooleanHolder | null, args: any[]): boolean { + override canApplyPreDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move | null, + _cancelled: BooleanHolder | null, + args: any[], + ): boolean { return (args[0] as NumberHolder).value > 0; } /** * @param args [0]: {@linkcode NumberHolder} Move additional effect chance. */ - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[0] as NumberHolder).value = 0; } } export class VariableMovePowerAbAttr extends PreAttackAbAttr { - override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + override canApplyPreAttack( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _defender: Pokemon, + _move: Move, + _args: any[], + ): boolean { return true; } } export class FieldPreventExplosiveMovesAbAttr extends AbAttr { override apply( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, cancelled: BooleanHolder, - args: any[], + _args: any[], ): void { cancelled.value = true; } @@ -1213,33 +1912,56 @@ export class FieldMultiplyStatAbAttr extends AbAttr { this.canStack = canStack; } - canApplyFieldStat(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: Stat, statValue: NumberHolder, checkedPokemon: Pokemon, hasApplied: BooleanHolder, args: any[]): boolean { - return this.canStack || !hasApplied.value - && this.stat === stat && checkedPokemon.getAbilityAttrs(FieldMultiplyStatAbAttr).every(attr => (attr as FieldMultiplyStatAbAttr).stat !== stat); + canApplyFieldStat( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + stat: Stat, + _statValue: NumberHolder, + checkedPokemon: Pokemon, + hasApplied: BooleanHolder, + _args: any[], + ): boolean { + return ( + this.canStack || + (!hasApplied.value && + this.stat === stat && + checkedPokemon + .getAbilityAttrs(FieldMultiplyStatAbAttr) + .every(attr => (attr as FieldMultiplyStatAbAttr).stat !== stat)) + ); } /** * applyFieldStat: Tries to multiply a Pokemon's Stat - * @param pokemon {@linkcode Pokemon} the Pokemon using this ability - * @param passive {@linkcode boolean} unused - * @param stat {@linkcode Stat} the type of the checked stat + * @param _pokemon {@linkcode Pokemon} the Pokemon using this ability + * @param _passive {@linkcode boolean} unused + * @param _stat {@linkcode Stat} the type of the checked stat * @param statValue {@linkcode NumberHolder} the value of the checked stat - * @param checkedPokemon {@linkcode Pokemon} the Pokemon this ability is targeting + * @param _checkedPokemon {@linkcode Pokemon} the Pokemon this ability is targeting * @param hasApplied {@linkcode BooleanHolder} whether or not another multiplier has been applied to this stat - * @param args {any[]} unused + * @param _args {any[]} unused */ - applyFieldStat(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: Stat, statValue: NumberHolder, checkedPokemon: Pokemon, hasApplied: BooleanHolder, args: any[]): void { + applyFieldStat( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _stat: Stat, + statValue: NumberHolder, + _checkedPokemon: Pokemon, + hasApplied: BooleanHolder, + _args: any[], + ): void { statValue.value *= this.multiplier; hasApplied.value = true; } - } export class MoveTypeChangeAbAttr extends PreAttackAbAttr { constructor( private newType: PokemonType, private powerMultiplier: number, - private condition?: PokemonAttackCondition + private condition?: PokemonAttackCondition, ) { super(false); } @@ -1260,23 +1982,41 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr { * @param _args - args[0] holds the type that the move is changed to, args[1] holds the multiplier * @returns whether the move type change attribute can be applied */ - override canApplyPreAttack(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _defender: Pokemon | null, move: Move, _args: [NumberHolder?, NumberHolder?, ...any]): boolean { - return (!this.condition || this.condition(pokemon, _defender, move)) && - !noAbilityTypeOverrideMoves.has(move.id) && - (!pokemon.isTerastallized || - (move.id !== MoveId.TERA_BLAST && - (move.id !== MoveId.TERA_STARSTORM || pokemon.getTeraType() !== PokemonType.STELLAR || !pokemon.hasSpecies(SpeciesId.TERAPAGOS)))); + override canApplyPreAttack( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _defender: Pokemon | null, + move: Move, + _args: [NumberHolder?, NumberHolder?, ...any], + ): boolean { + return ( + (!this.condition || this.condition(pokemon, _defender, move)) && + !noAbilityTypeOverrideMoves.has(move.id) && + (!pokemon.isTerastallized || + (move.id !== MoveId.TERA_BLAST && + (move.id !== MoveId.TERA_STARSTORM || + pokemon.getTeraType() !== PokemonType.STELLAR || + !pokemon.hasSpecies(SpeciesId.TERAPAGOS)))) + ); } /** - * @param pokemon - The pokemon that has the move type changing ability and is using the attacking move - * @param passive - Unused - * @param simulated - Unused - * @param defender - The pokemon being attacked (unused) - * @param move - The move being used + * @param _pokemon - The pokemon that has the move type changing ability and is using the attacking move + * @param _passive - Unused + * @param _simulated - Unused + * @param _defender - The pokemon being attacked (unused) + * @param _move - The move being used * @param args - args[0] holds the type that the move is changed to, args[1] holds the multiplier */ - override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: [NumberHolder?, NumberHolder?, ...any]): void { + override applyPreAttack( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _defender: Pokemon, + _move: Move, + args: [NumberHolder?, NumberHolder?, ...any], + ): void { if (args[0] && args[0] instanceof NumberHolder) { args[0].value = this.newType; } @@ -1294,20 +2034,31 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { super(true); } - override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { - if (!pokemon.isTerastallized && - move.id !== MoveId.STRUGGLE && - /** - * Skip moves that call other moves because these moves generate a following move that will trigger this ability attribute - * @see {@link https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_call_other_moves} - */ - !move.findAttr((attr) => - attr instanceof RandomMovesetMoveAttr || - attr instanceof RandomMoveAttr || - attr instanceof NaturePowerAttr || - attr instanceof CopyMoveAttr)) { + override canApplyPreAttack( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _defender: Pokemon | null, + move: Move, + _args: any[], + ): boolean { + if ( + !pokemon.isTerastallized && + move.id !== MoveId.STRUGGLE && + /** + * Skip moves that call other moves because these moves generate a following move that will trigger this ability attribute + * @see {@link https://bulbapedia.bulbagarden.net/wiki/Category:Moves_that_call_other_moves} + */ + !move.findAttr( + attr => + attr instanceof RandomMovesetMoveAttr || + attr instanceof RandomMoveAttr || + attr instanceof NaturePowerAttr || + attr instanceof CopyMoveAttr, + ) + ) { const moveType = pokemon.getMoveType(move); - if (pokemon.getTypes().some((t) => t !== moveType)) { + if (pokemon.getTypes().some(t => t !== moveType)) { this.moveType = moveType; return true; } @@ -1315,17 +2066,24 @@ export class PokemonTypeChangeAbAttr extends PreAttackAbAttr { return false; } - override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { + override applyPreAttack( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _defender: Pokemon, + move: Move, + _args: any[], + ): void { const moveType = pokemon.getMoveType(move); if (!simulated) { this.moveType = moveType; - pokemon.summonData.types = [ moveType ]; + pokemon.summonData.types = [moveType]; pokemon.updateInfo(); } } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:pokemonTypeChange", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveType: i18next.t(`pokemonInfo:Type.${PokemonType[this.moveType]}`), @@ -1346,7 +2104,14 @@ export class AddSecondStrikeAbAttr extends PreAttackAbAttr { this.damageMultiplier = damageMultiplier; } - override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { + override canApplyPreAttack( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _defender: Pokemon | null, + move: Move, + _args: any[], + ): boolean { return move.canBeMultiStrikeEnhanced(pokemon, true); } @@ -1354,14 +2119,21 @@ export class AddSecondStrikeAbAttr extends PreAttackAbAttr { * If conditions are met, this doubles the move's hit count (via args[1]) * or multiplies the damage of secondary strikes (via args[2]) * @param pokemon the {@linkcode Pokemon} using the move - * @param passive n/a - * @param defender n/a - * @param move the {@linkcode Move} used by the ability source + * @param _passive n/a + * @param _defender n/a + * @param _move the {@linkcode Move} used by the ability source * @param args Additional arguments: * - `[0]` the number of strikes this move currently has ({@linkcode NumberHolder}) * - `[1]` the damage multiplier for the current strike ({@linkcode NumberHolder}) */ - override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { + override applyPreAttack( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _defender: Pokemon, + _move: Move, + args: any[], + ): void { const hitCount = args[0] as NumberHolder; const multiplier = args[1] as NumberHolder; if (hitCount?.value) { @@ -1390,19 +2162,33 @@ export class DamageBoostAbAttr extends PreAttackAbAttr { this.condition = condition; } - override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { + override canApplyPreAttack( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + defender: Pokemon | null, + move: Move, + _args: any[], + ): boolean { return this.condition(pokemon, defender, move); } /** * - * @param pokemon the attacker pokemon - * @param passive N/A - * @param defender the target pokemon - * @param move the move used by the attacker pokemon + * @param _pokemon the attacker pokemon + * @param _passive N/A + * @param _defender the target pokemon + * @param _move the move used by the attacker pokemon * @param args Utils.NumberHolder as damage */ - override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { + override applyPreAttack( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _defender: Pokemon, + _move: Move, + args: any[], + ): void { const power = args[0] as NumberHolder; power.value = toDmgValue(power.value * this.damageMultiplier); } @@ -1412,34 +2198,49 @@ export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr { private condition: PokemonAttackCondition; private powerMultiplier: number; - constructor(condition: PokemonAttackCondition, powerMultiplier: number, showAbility: boolean = false) { + constructor(condition: PokemonAttackCondition, powerMultiplier: number, showAbility = false) { super(showAbility); this.condition = condition; this.powerMultiplier = powerMultiplier; } - override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { + override canApplyPreAttack( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + defender: Pokemon | null, + move: Move, + _args: any[], + ): boolean { return this.condition(pokemon, defender, move); } - override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { + override applyPreAttack( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _defender: Pokemon, + _move: Move, + args: any[], + ): void { (args[0] as NumberHolder).value *= this.powerMultiplier; } } export class MoveTypePowerBoostAbAttr extends MovePowerBoostAbAttr { constructor(boostedType: PokemonType, powerMultiplier?: number) { - super((pokemon, defender, move) => pokemon?.getMoveType(move) === boostedType, powerMultiplier || 1.5, false); + super((pokemon, _defender, move) => pokemon?.getMoveType(move) === boostedType, powerMultiplier || 1.5, false); } } export class LowHpMoveTypePowerBoostAbAttr extends MoveTypePowerBoostAbAttr { + // biome-ignore lint/complexity/noUselessConstructor: Changes the constructor params constructor(boostedType: PokemonType) { super(boostedType); } getCondition(): AbAttrCondition { - return (pokemon) => pokemon.getHpRatio() <= 0.33; + return pokemon => pokemon.getHpRatio() <= 0.33; } } @@ -1460,11 +2261,25 @@ export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr { this.mult = mult; } - override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + override canApplyPreAttack( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + defender: Pokemon, + move: Move, + _args: any[], + ): boolean { return this.mult(pokemon, defender, move) !== 1; } - override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { + override applyPreAttack( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + defender: Pokemon, + move: Move, + args: any[], + ): void { const multiplier = this.mult(pokemon, defender, move); (args[0] as NumberHolder).value *= multiplier; } @@ -1489,11 +2304,25 @@ export class FieldMovePowerBoostAbAttr extends AbAttr { this.powerMultiplier = powerMultiplier; } - canApplyPreAttack(pokemon: Pokemon | null, passive: boolean | null, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean { + canApplyPreAttack( + _pokemon: Pokemon | null, + _passive: boolean | null, + _simulated: boolean, + _defender: Pokemon | null, + _move: Move, + _args: any[], + ): boolean { return true; // logic for this attr is handled in move.ts instead of normally } - applyPreAttack(pokemon: Pokemon | null, passive: boolean | null, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): void { + applyPreAttack( + pokemon: Pokemon | null, + _passive: boolean | null, + _simulated: boolean, + defender: Pokemon | null, + move: Move, + args: any[], + ): void { if (this.condition(pokemon, defender, move)) { (args[0] as NumberHolder).value *= this.powerMultiplier; } @@ -1510,7 +2339,7 @@ export class PreAttackFieldMoveTypePowerBoostAbAttr extends FieldMovePowerBoostA * @param powerMultiplier - The multiplier to apply to the move's power, defaults to 1.5 if not provided. */ constructor(boostedType: PokemonType, powerMultiplier?: number) { - super((pokemon, defender, move) => pokemon?.getMoveType(move) === boostedType, powerMultiplier || 1.5); + super((pokemon, _defender, move) => pokemon?.getMoveType(move) === boostedType, powerMultiplier || 1.5); } } @@ -1518,13 +2347,13 @@ export class PreAttackFieldMoveTypePowerBoostAbAttr extends FieldMovePowerBoostA * Boosts the power of a specific type of move for all Pokemon in the field. * @extends PreAttackFieldMoveTypePowerBoostAbAttr */ -export class FieldMoveTypePowerBoostAbAttr extends PreAttackFieldMoveTypePowerBoostAbAttr { } +export class FieldMoveTypePowerBoostAbAttr extends PreAttackFieldMoveTypePowerBoostAbAttr {} /** * Boosts the power of a specific type of move for the user and its allies. * @extends PreAttackFieldMoveTypePowerBoostAbAttr */ -export class UserFieldMoveTypePowerBoostAbAttr extends PreAttackFieldMoveTypePowerBoostAbAttr { } +export class UserFieldMoveTypePowerBoostAbAttr extends PreAttackFieldMoveTypePowerBoostAbAttr {} /** * Boosts the power of moves in specified categories. @@ -1536,7 +2365,7 @@ export class AllyMoveCategoryPowerBoostAbAttr extends FieldMovePowerBoostAbAttr * @param powerMultiplier - The multiplier to apply to the move's power. */ constructor(boostedCategories: MoveCategory[], powerMultiplier: number) { - super((pokemon, defender, move) => boostedCategories.includes(move.category), powerMultiplier); + super((_pokemon, _defender, move) => boostedCategories.includes(move.category), powerMultiplier); } } @@ -1556,21 +2385,23 @@ export class StatMultiplierAbAttr extends AbAttr { canApplyStatStage( pokemon: Pokemon, _passive: boolean, - simulated: boolean, + _simulated: boolean, stat: BattleStat, - statValue: NumberHolder, - args: any[]): boolean { - const move = (args[0] as Move); + _statValue: NumberHolder, + args: any[], + ): boolean { + const move = args[0] as Move; return stat === this.stat && (!this.condition || this.condition(pokemon, null, move)); } applyStatStage( - pokemon: Pokemon, + _pokemon: Pokemon, _passive: boolean, - simulated: boolean, - stat: BattleStat, + _simulated: boolean, + _stat: BattleStat, statValue: NumberHolder, - args: any[]): void { + _args: any[], + ): void { statValue.value *= this.multiplier; } } @@ -1579,7 +2410,10 @@ export class PostAttackAbAttr extends AbAttr { private attackCondition: PokemonAttackCondition; /** The default attackCondition requires that the selected move is a damaging move */ - constructor(attackCondition: PokemonAttackCondition = (user, target, move) => (move.category !== MoveCategory.STATUS), showAbility = true) { + constructor( + attackCondition: PokemonAttackCondition = (_user, _target, move) => move.category !== MoveCategory.STATUS, + showAbility = true, + ) { super(showAbility); this.attackCondition = attackCondition; @@ -1592,25 +2426,27 @@ export class PostAttackAbAttr extends AbAttr { */ canApplyPostAttack( pokemon: Pokemon, - passive: boolean, - simulated: boolean, + _passive: boolean, + _simulated: boolean, defender: Pokemon, move: Move, - hitResult: HitResult | null, - args: any[]): boolean { + _hitResult: HitResult | null, + _args: any[], + ): boolean { // When attackRequired is true, we require the move to be an attack move and to deal damage before checking secondary requirements. // If attackRequired is false, we always defer to the secondary requirements. return this.attackCondition(pokemon, defender, move); } applyPostAttack( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - defender: Pokemon, - move: Move, - hitResult: HitResult | null, - args: any[]): void {} + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _defender: Pokemon, + _move: Move, + _hitResult: HitResult | null, + _args: any[], + ): void {} } /** @@ -1628,7 +2464,7 @@ export class AllyStatMultiplierAbAttr extends AbAttr { * @param multipler - The multiplier to apply to the stat * @param ignorable - Whether the multiplier can be ignored by mold breaker-like moves and abilities */ - constructor(stat: BattleStat, multiplier: number, ignorable: boolean = true) { + constructor(stat: BattleStat, multiplier: number, ignorable = true) { super(false); this.stat = stat; @@ -1648,23 +2484,41 @@ export class AllyStatMultiplierAbAttr extends AbAttr { * @param _args - unused * @returns `true` if this changed the checked stat, `false` otherwise. */ - applyAllyStat(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _stat: BattleStat, statValue: NumberHolder, _checkedPokemon: Pokemon, _ignoreAbility: boolean, _args: any[]) { + applyAllyStat( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _stat: BattleStat, + statValue: NumberHolder, + _checkedPokemon: Pokemon, + _ignoreAbility: boolean, + _args: any[], + ) { statValue.value *= this.multiplier; } /** * Check if this ability can apply to the checked stat. - * @param pokemon - The ally {@linkcode Pokemon} with the ability (unused) + * @param _pokemon - The ally {@linkcode Pokemon} with the ability (unused) * @param passive - unused - * @param simulated - Whether the ability is being simulated (unused) + * @param _simulated - Whether the ability is being simulated (unused) * @param stat - The type of the checked {@linkcode Stat} - * @param statValue - {@linkcode NumberHolder} containing the value of the checked stat - * @param checkedPokemon - The {@linkcode Pokemon} this ability is targeting (unused) + * @param _statValue - {@linkcode NumberHolder} containing the value of the checked stat + * @param _checkedPokemon - The {@linkcode Pokemon} this ability is targeting (unused) * @param ignoreAbility - Whether the ability should be ignored if possible - * @param args - unused + * @param _args - unused * @returns `true` if this can apply to the checked stat, `false` otherwise. */ - canApplyAllyStat(pokemon: Pokemon, _passive: boolean, simulated: boolean, stat: BattleStat, statValue: NumberHolder, checkedPokemon: Pokemon, ignoreAbility: boolean, args: any[]): boolean { + canApplyAllyStat( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + stat: BattleStat, + _statValue: NumberHolder, + _checkedPokemon: Pokemon, + ignoreAbility: boolean, + _args: any[], + ): boolean { return stat === this.stat && !(ignoreAbility && this.ignorable); } } @@ -1675,7 +2529,7 @@ export class AllyStatMultiplierAbAttr extends AbAttr { */ export class GorillaTacticsAbAttr extends PostAttackAbAttr { constructor() { - super((user, target, move) => true, false); + super((_user, _target, _move) => true, false); } override canApplyPostAttack( @@ -1685,29 +2539,33 @@ export class GorillaTacticsAbAttr extends PostAttackAbAttr { defender: Pokemon, move: Move, hitResult: HitResult | null, - args: any[]): boolean { - return super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) - && simulated || !pokemon.getTag(BattlerTagType.GORILLA_TACTICS); + args: any[], + ): boolean { + return ( + (super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) && simulated) || + !pokemon.getTag(BattlerTagType.GORILLA_TACTICS) + ); } /** * * @param {Pokemon} pokemon the {@linkcode Pokemon} with this ability - * @param passive n/a + * @param _passive n/a * @param simulated whether the ability is being simulated - * @param defender n/a - * @param move n/a - * @param hitResult n/a - * @param args n/a + * @param _defender n/a + * @param _move n/a + * @param _hitResult n/a + * @param _args n/a */ override applyPostAttack( pokemon: Pokemon, - passive: boolean, + _passive: boolean, simulated: boolean, - defender: Pokemon, - move: Move, - hitResult: HitResult | null, - args: any[]): void { + _defender: Pokemon, + _move: Move, + _hitResult: HitResult | null, + _args: any[], + ): void { if (!simulated) { pokemon.addTag(BattlerTagType.GORILLA_TACTICS); } @@ -1731,14 +2589,15 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { defender: Pokemon, move: Move, hitResult: HitResult, - args: any[]): boolean { + args: any[], + ): boolean { if ( super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) && !simulated && hitResult < HitResult.NO_EFFECT && (!this.stealCondition || this.stealCondition(pokemon, defender, move)) ) { - const heldItems = this.getTargetHeldItems(defender).filter((i) => i.isTransferable); + const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferable); if (heldItems.length) { // Ensure that the stolen item in testing is the same as when the effect is applied this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; @@ -1753,14 +2612,14 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { override applyPostAttack( pokemon: Pokemon, - passive: boolean, - simulated: boolean, + _passive: boolean, + _simulated: boolean, defender: Pokemon, - move: Move, - hitResult: HitResult, - args: any[], + _move: Move, + _hitResult: HitResult, + _args: any[], ): void { - const heldItems = this.getTargetHeldItems(defender).filter((i) => i.isTransferable); + const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferable); if (!this.stolenItem) { this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; } @@ -1777,8 +2636,10 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { } getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] { - return globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier - && m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier[]; + return globalScene.findModifiers( + m => m instanceof PokemonHeldItemModifier && m.pokemonId === target.id, + target.isPlayer(), + ) as PokemonHeldItemModifier[]; } } @@ -1795,21 +2656,44 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { this.effects = effects; } - override canApplyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + override canApplyPostAttack( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + hitResult: HitResult | null, + args: any[], + ): boolean { if ( - super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) - && (simulated || !attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker - && (!this.contactRequired || move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon})) && pokemon.randBattleSeedInt(100) < this.chance && !pokemon.status) + super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) && + (simulated || + (!attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && + pokemon !== attacker && + (!this.contactRequired || + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })) && + pokemon.randBattleSeedInt(100) < this.chance && + !pokemon.status)) ) { - const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; + const effect = + this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; return simulated || attacker.canSetStatus(effect, true, false, pokemon); } return false; } - applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { - const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; + applyPostAttack( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { + const effect = + this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; attacker.trySetStatus(effect, true, pokemon); } } @@ -1825,8 +2709,11 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { private chance: (user: Pokemon, target: Pokemon, move: Move) => number; private effects: BattlerTagType[]; - - constructor(contactRequired: boolean, chance: (user: Pokemon, target: Pokemon, move: Move) => number, ...effects: BattlerTagType[]) { + constructor( + contactRequired: boolean, + chance: (user: Pokemon, target: Pokemon, move: Move) => number, + ...effects: BattlerTagType[] + ) { super(undefined, false); this.contactRequired = contactRequired; @@ -1834,17 +2721,39 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { this.effects = effects; } - override canApplyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { + override canApplyPostAttack( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + hitResult: HitResult | null, + args: any[], + ): boolean { /**Battler tags inflicted by abilities post attacking are also considered additional effects.*/ - return super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) && - !attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && - (!this.contactRequired || move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon})) && - pokemon.randBattleSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status; + return ( + super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) && + !attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && + pokemon !== attacker && + (!this.contactRequired || + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon })) && + pokemon.randBattleSeedInt(100) < this.chance(attacker, pokemon, move) && + !pokemon.status + ); } - override applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { + override applyPostAttack( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { - const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; + const effect = + this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randBattleSeedInt(this.effects.length)]; attacker.addTag(effect); } } @@ -1860,13 +2769,17 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { this.condition = condition; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if ( - !simulated && - hitResult < HitResult.NO_EFFECT && - (!this.condition || this.condition(pokemon, attacker, move)) - ) { - const heldItems = this.getTargetHeldItems(attacker).filter((i) => i.isTransferable); + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker: Pokemon, + move: Move, + hitResult: HitResult, + _args: any[], + ): boolean { + if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) { + const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferable); if (heldItems.length) { this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) { @@ -1880,14 +2793,13 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { override applyPostDefend( pokemon: Pokemon, _passive: boolean, - simulated: boolean, + _simulated: boolean, attacker: Pokemon, - move: Move, - hitResult: HitResult, + _move: Move, + _hitResult: HitResult, _args: any[], ): void { - - const heldItems = this.getTargetHeldItems(attacker).filter((i) => i.isTransferable); + const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferable); if (!this.stolenItem) { this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)]; } @@ -1904,8 +2816,10 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { } getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] { - return globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier - && m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier[]; + return globalScene.findModifiers( + m => m instanceof PokemonHeldItemModifier && m.pokemonId === target.id, + target.isPlayer(), + ) as PokemonHeldItemModifier[]; } } @@ -1915,30 +2829,31 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { */ export class PostSetStatusAbAttr extends AbAttr { canApplyPostSetStatus( - pokemon: Pokemon, - sourcePokemon: Pokemon | null = null, - passive: boolean, - effect: StatusEffect, - simulated: boolean, - rgs: any[]): boolean { + _pokemon: Pokemon, + _sourcePokemon: Pokemon | null = null, + _passive: boolean, + _effect: StatusEffect, + _simulated: boolean, + _rgs: any[], + ): boolean { return true; } /** * Does nothing after a status condition is set. - * @param pokemon {@linkcode Pokemon} that status condition was set on. - * @param sourcePokemon {@linkcode Pokemon} that that set the status condition. Is `null` if status was not set by a Pokemon. - * @param passive Whether this ability is a passive. - * @param effect {@linkcode StatusEffect} that was set. - * @param args Set of unique arguments needed by this attribute. + * @param _pokemon {@linkcode Pokemon} that status condition was set on. + * @param _sourcePokemon {@linkcode Pokemon} that that set the status condition. Is `null` if status was not set by a Pokemon. + * @param _passive Whether this ability is a passive. + * @param _effect {@linkcode StatusEffect} that was set. + * @param _args Set of unique arguments needed by this attribute. */ applyPostSetStatus( - pokemon: Pokemon, - sourcePokemon: Pokemon | null = null, - passive: boolean, - effect: StatusEffect, - simulated: boolean, - args: any[], + _pokemon: Pokemon, + _sourcePokemon: Pokemon | null = null, + _passive: boolean, + _effect: StatusEffect, + _simulated: boolean, + _args: any[], ): void {} } @@ -1948,17 +2863,24 @@ export class PostSetStatusAbAttr extends AbAttr { * ability attribute. For Synchronize ability. */ export class SynchronizeStatusAbAttr extends PostSetStatusAbAttr { - override canApplyPostSetStatus(pokemon: Pokemon, sourcePokemon: (Pokemon | null) | undefined, passive: boolean, effect: StatusEffect, simulated: boolean, args: any[]): boolean { + override canApplyPostSetStatus( + _pokemon: Pokemon, + sourcePokemon: (Pokemon | null) | undefined, + _passive: boolean, + effect: StatusEffect, + _simulated: boolean, + _args: any[], + ): boolean { /** Synchronizable statuses */ const syncStatuses = new Set([ StatusEffect.BURN, StatusEffect.PARALYSIS, StatusEffect.POISON, - StatusEffect.TOXIC + StatusEffect.TOXIC, ]); // synchronize does not need to check canSetStatus because the ability shows even if it fails to set the status - return ((sourcePokemon ?? false) && syncStatuses.has(effect)); + return (sourcePokemon ?? false) && syncStatuses.has(effect); } /** @@ -1966,11 +2888,18 @@ export class SynchronizeStatusAbAttr extends PostSetStatusAbAttr { * was set by a source Pokemon, set the source Pokemon's status to the same `StatusEffect`. * @param pokemon {@linkcode Pokemon} that status condition was set on. * @param sourcePokemon {@linkcode Pokemon} that that set the status condition. Is null if status was not set by a Pokemon. - * @param passive Whether this ability is a passive. + * @param _passive Whether this ability is a passive. * @param effect {@linkcode StatusEffect} that was set. - * @param args Set of unique arguments needed by this attribute. + * @param _args Set of unique arguments needed by this attribute. */ - override applyPostSetStatus(pokemon: Pokemon, sourcePokemon: Pokemon | null = null, passive: boolean, effect: StatusEffect, simulated: boolean, args: any[]): void { + override applyPostSetStatus( + pokemon: Pokemon, + sourcePokemon: Pokemon | null = null, + _passive: boolean, + effect: StatusEffect, + simulated: boolean, + _args: any[], + ): void { if (!simulated && sourcePokemon) { sourcePokemon.trySetStatus(effect, true, pokemon); } @@ -1978,11 +2907,11 @@ export class SynchronizeStatusAbAttr extends PostSetStatusAbAttr { } export class PostVictoryAbAttr extends AbAttr { - canApplyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + canApplyPostVictory(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return true; } - applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} + applyPostVictory(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} } class PostVictoryStatStageChangeAbAttr extends PostVictoryAbAttr { @@ -1996,10 +2925,12 @@ class PostVictoryStatStageChangeAbAttr extends PostVictoryAbAttr { this.stages = stages; } - override applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostVictory(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; if (!simulated) { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ stat ], this.stages)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), true, [stat], this.stages), + ); } } } @@ -2007,18 +2938,18 @@ class PostVictoryStatStageChangeAbAttr extends PostVictoryAbAttr { export class PostVictoryFormChangeAbAttr extends PostVictoryAbAttr { private formFunc: (p: Pokemon) => number; - constructor(formFunc: ((p: Pokemon) => number)) { + constructor(formFunc: (p: Pokemon) => number) { super(true); this.formFunc = formFunc; } - override canApplyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostVictory(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { const formIndex = this.formFunc(pokemon); return formIndex !== pokemon.formIndex; } - override applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostVictory(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } @@ -2026,11 +2957,23 @@ export class PostVictoryFormChangeAbAttr extends PostVictoryAbAttr { } export class PostKnockOutAbAttr extends AbAttr { - canApplyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean { + canApplyPostKnockOut( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _knockedOut: Pokemon, + _args: any[], + ): boolean { return true; } - applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): void {} + applyPostKnockOut( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _knockedOut: Pokemon, + _args: any[], + ): void {} } export class PostKnockOutStatStageChangeAbAttr extends PostKnockOutAbAttr { @@ -2044,27 +2987,48 @@ export class PostKnockOutStatStageChangeAbAttr extends PostKnockOutAbAttr { this.stages = stages; } - override applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): void { + override applyPostKnockOut( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _knockedOut: Pokemon, + _args: any[], + ): void { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; if (!simulated) { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ stat ], this.stages)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), true, [stat], this.stages), + ); } } } export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr { - constructor() { - super(); - } - - override canApplyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean { + override canApplyPostKnockOut( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + knockedOut: Pokemon, + _args: any[], + ): boolean { return pokemon.isPlayer() === knockedOut.isPlayer() && knockedOut.getAbility().isCopiable; } - override applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): void { + override applyPostKnockOut( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + knockedOut: Pokemon, + _args: any[], + ): void { if (!simulated) { pokemon.setTempAbility(knockedOut.getAbility()); - globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:copyFaintedAllyAbility", { pokemonNameWithAffix: getPokemonNameWithAffix(knockedOut), abilityName: allAbilities[knockedOut.getAbility().id].name })); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:copyFaintedAllyAbility", { + pokemonNameWithAffix: getPokemonNameWithAffix(knockedOut), + abilityName: allAbilities[knockedOut.getAbility().id].name, + }), + ); } } } @@ -2082,7 +3046,7 @@ export class IgnoreOpponentStatStagesAbAttr extends AbAttr { this.stats = stats ?? BATTLE_STATS; } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { return this.stats.includes(args[0]); } @@ -2090,11 +3054,17 @@ export class IgnoreOpponentStatStagesAbAttr extends AbAttr { * Modifies a BooleanHolder and returns the result to see if a stat is ignored or not * @param _pokemon n/a * @param _passive n/a - * @param simulated n/a + * @param _simulated n/a * @param _cancelled n/a * @param args A BooleanHolder that represents whether or not to ignore a stat's stat changes */ - override apply(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[1] as BooleanHolder).value = true; } } @@ -2104,14 +3074,20 @@ export class IntimidateImmunityAbAttr extends AbAttr { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:intimidateImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - abilityName + abilityName, }); } } @@ -2128,9 +3104,17 @@ export class PostIntimidateStatStageChangeAbAttr extends AbAttr { this.overwrites = !!overwrites; } - override apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + cancelled: BooleanHolder, + _args: any[], + ): void { if (!simulated) { - globalScene.phaseManager.pushPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), false, this.stats, this.stages)); + globalScene.phaseManager.pushPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), false, this.stats, this.stages), + ); } cancelled.value = this.overwrites; } @@ -2156,17 +3140,17 @@ export class PostSummonAbAttr extends AbAttr { return this.activateOnGain; } - canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return true; } /** * Applies ability post summon (after switching in) - * @param pokemon {@linkcode Pokemon} with this ability - * @param passive Whether this ability is a passive - * @param args Set of unique arguments needed by this attribute + * @param _pokemon {@linkcode Pokemon} with this ability + * @param _passive Whether this ability is a passive + * @param _args Set of unique arguments needed by this attribute */ - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} + applyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} } /** @@ -2189,11 +3173,11 @@ export class PostSummonRemoveArenaTagAbAttr extends PostSummonAbAttr { this.arenaTags = arenaTags; } - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return globalScene.arena.tags.some(tag => this.arenaTags.includes(tag.tagType)); } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { for (const arenaTag of this.arenaTags) { globalScene.arena.removeTag(arenaTag); @@ -2212,7 +3196,6 @@ export class PostSummonAddArenaTagAbAttr extends PostSummonAbAttr { private readonly quiet?: boolean; private sourceId: number; - constructor(showAbility: boolean, tagType: ArenaTagType, turnCount: number, side?: ArenaTagSide, quiet?: boolean) { super(showAbility); this.tagType = tagType; @@ -2221,7 +3204,7 @@ export class PostSummonAddArenaTagAbAttr extends PostSummonAbAttr { this.quiet = quiet; } - public override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + public override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { this.sourceId = pokemon.id; if (!simulated) { globalScene.arena.addTag(this.tagType, this.turnCount, undefined, this.sourceId, this.side, this.quiet); @@ -2238,7 +3221,7 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr { this.messageFunc = messageFunc; } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { globalScene.phaseManager.queueMessage(this.messageFunc(pokemon)); } @@ -2255,7 +3238,7 @@ export class PostSummonUnnamedMessageAbAttr extends PostSummonAbAttr { this.message = message; } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { globalScene.phaseManager.queueMessage(this.message); } @@ -2273,11 +3256,11 @@ export class PostSummonAddBattlerTagAbAttr extends PostSummonAbAttr { this.turnCount = turnCount; } - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return pokemon.canAddTag(this.tagType); } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { pokemon.addTag(this.tagType, this.turnCount); } @@ -2300,11 +3283,11 @@ export class PostSummonRemoveBattlerTagAbAttr extends PostSummonRemoveEffectAbAt this.immuneTags = immuneTags; } - public override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + public override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return this.immuneTags.some(tagType => !!pokemon.getTag(tagType)); } - public override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + public override applyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { this.immuneTags.forEach(tagType => pokemon.removeTag(tagType)); } } @@ -2324,7 +3307,7 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { this.intimidate = !!intimidate; } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (simulated) { return; } @@ -2332,7 +3315,9 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { if (this.selfTarget) { // we unshift the StatStageChangePhase to put it right after the showAbility and not at the end of the // phase list (which could be after CommandPhase for example) - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages), + ); } else { for (const opponent of pokemon.getOpponents()) { const cancelled = new BooleanHolder(false); @@ -2345,7 +3330,9 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { } } if (!cancelled.value) { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(opponent.getBattlerIndex(), false, this.stats, this.stages)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(opponent.getBattlerIndex(), false, this.stats, this.stages), + ); } } } @@ -2363,15 +3350,25 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { this.showAnim = showAnim; } - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return pokemon.getAlly()?.isActive(true) ?? false; } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { const target = pokemon.getAlly(); if (!simulated && !isNullOrUndefined(target)) { - globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / this.healRatio), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim)); + globalScene.phaseManager.unshiftPhase( + new PokemonHealPhase( + target.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / this.healRatio), + i18next.t("abilityTriggers:postSummonAllyHeal", { + pokemonNameWithAffix: getPokemonNameWithAffix(target), + pokemonName: pokemon.name, + }), + true, + !this.showAnim, + ), + ); } } } @@ -2385,22 +3382,22 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { * @returns if the move was successful */ export class PostSummonClearAllyStatStagesAbAttr extends PostSummonAbAttr { - constructor() { - super(); - } - - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return pokemon.getAlly()?.isActive(true) ?? false; } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { const target = pokemon.getAlly(); if (!simulated && !isNullOrUndefined(target)) { for (const s of BATTLE_STATS) { target.setStatStage(s, 0); } - globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:postSummonClearAllyStats", { pokemonNameWithAffix: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:postSummonClearAllyStats", { + pokemonNameWithAffix: getPokemonNameWithAffix(target), + }), + ); } } } @@ -2418,7 +3415,7 @@ export class DownloadAbAttr extends PostSummonAbAttr { private enemyCountTally: number; private stats: BattleStat[]; - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { this.enemyDef = 0; this.enemySpDef = 0; this.enemyCountTally = 0; @@ -2437,14 +3434,14 @@ export class DownloadAbAttr extends PostSummonAbAttr { * Checks to see if it is the opening turn (starting a new game), if so, Download won't work. This is because Download takes into account * vitamins and items, so it needs to use the Stat and the stat alone. * @param {Pokemon} pokemon Pokemon that is using the move, as well as seeing the opposing pokemon. - * @param {boolean} passive N/A - * @param {any[]} args N/A + * @param {boolean} _passive N/A + * @param {any[]} _args N/A */ - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (this.enemyDef < this.enemySpDef) { - this.stats = [ Stat.ATK ]; + this.stats = [Stat.ATK]; } else { - this.stats = [ Stat.SPATK ]; + this.stats = [Stat.SPATK]; } if (!simulated) { @@ -2462,14 +3459,16 @@ export class PostSummonWeatherChangeAbAttr extends PostSummonAbAttr { this.weatherType = weatherType; } - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const weatherReplaceable = (this.weatherType === WeatherType.HEAVY_RAIN || + override canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + const weatherReplaceable = + this.weatherType === WeatherType.HEAVY_RAIN || this.weatherType === WeatherType.HARSH_SUN || - this.weatherType === WeatherType.STRONG_WINDS) || !globalScene.arena.weather?.isImmutable(); + this.weatherType === WeatherType.STRONG_WINDS || + !globalScene.arena.weather?.isImmutable(); return weatherReplaceable && globalScene.arena.canSetWeather(this.weatherType); } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { globalScene.arena.trySetWeather(this.weatherType, pokemon); } @@ -2485,11 +3484,11 @@ export class PostSummonTerrainChangeAbAttr extends PostSummonAbAttr { this.terrainType = terrainType; } - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return globalScene.arena.canSetTerrain(this.terrainType); } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { globalScene.arena.trySetTerrain(this.terrainType, false, pokemon); } @@ -2511,12 +3510,12 @@ export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr { this.immuneEffects = immuneEffects; } - public override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + public override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { const status = pokemon.status?.effect; - return !isNullOrUndefined(status) && (this.immuneEffects.length < 1 || this.immuneEffects.includes(status)) + return !isNullOrUndefined(status) && (this.immuneEffects.length < 1 || this.immuneEffects.includes(status)); } - public override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + public override applyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { const status = pokemon.status?.effect; if (!isNullOrUndefined(status)) { this.statusHealed = status; @@ -2536,17 +3535,17 @@ export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr { export class PostSummonFormChangeAbAttr extends PostSummonAbAttr { private formFunc: (p: Pokemon) => number; - constructor(formFunc: ((p: Pokemon) => number)) { + constructor(formFunc: (p: Pokemon) => number) { super(true); this.formFunc = formFunc; } - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return this.formFunc(pokemon) !== pokemon.formIndex; } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } @@ -2558,7 +3557,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { private target: Pokemon; private targetAbilityName: string; - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { const targets = pokemon.getOpponents(); if (!targets.length) { return false; @@ -2566,7 +3565,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { let target: Pokemon; if (targets.length > 1) { - globalScene.executeWithSeedOffset(() => target = randSeedItem(targets), globalScene.currentBattle.waveIndex); + globalScene.executeWithSeedOffset(() => (target = randSeedItem(targets)), globalScene.currentBattle.waveIndex); } else { target = targets[0]; } @@ -2584,7 +3583,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { return true; } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { pokemon.setTempAbility(this.target!.getAbility()); setAbilityRevealed(this.target!); @@ -2592,7 +3591,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { } } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:trace", { pokemonName: getPokemonNameWithAffix(pokemon), targetName: getPokemonNameWithAffix(this.target), @@ -2616,7 +3615,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt this.statusEffect = statusEffect; } - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { const party = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); return party.filter(p => p.isAllowedInBattle()).length > 0; } @@ -2625,17 +3624,19 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt * Removes supplied status effect from the user's field when user of the ability is summoned. * * @param pokemon - The Pokémon that triggered the ability. - * @param passive - n/a - * @param args - n/a + * @param _passive - n/a + * @param _args - n/a */ - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { const party = pokemon.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField(); const allowedParty = party.filter(p => p.isAllowedInBattle()); if (!simulated) { for (const pokemon of allowedParty) { if (pokemon.status && this.statusEffect.includes(pokemon.status.effect)) { - globalScene.phaseManager.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); + globalScene.phaseManager.queueMessage( + getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)), + ); pokemon.resetStatus(false); pokemon.updateInfo(); } @@ -2644,10 +3645,9 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt } } - /** Attempt to copy the stat changes on an ally pokemon */ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { if (!globalScene.currentBattle.double) { return false; } @@ -2656,7 +3656,7 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { return !(isNullOrUndefined(ally) || ally.getStatStages().every(s => s === 0)); } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { const ally = pokemon.getAlly(); if (!simulated && !isNullOrUndefined(ally)) { for (const s of BATTLE_STATS) { @@ -2666,7 +3666,7 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { } } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:costar", { pokemonName: getPokemonNameWithAffix(pokemon), allyName: getPokemonNameWithAffix(pokemon.getAlly()), @@ -2691,7 +3691,8 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { if (targets[0].fusionSpecies) { target = targets[1]; return; - } else if (targets[1].fusionSpecies) { + } + if (targets[1].fusionSpecies) { target = targets[0]; return; } @@ -2706,7 +3707,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { return target; } - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { const targets = pokemon.getOpponents(); const target = this.getTarget(targets); @@ -2722,11 +3723,12 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { return !(this.getTarget(targets).fusionSpecies || pokemon.fusionSpecies); } - override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { const target = this.getTarget(pokemon.getOpponents()); - globalScene.phaseManager.unshiftPhase(new PokemonTransformPhase(pokemon.getBattlerIndex(), target.getBattlerIndex(), true)); - + globalScene.phaseManager.unshiftPhase( + new PokemonTransformPhase(pokemon.getBattlerIndex(), target.getBattlerIndex(), true), + ); } } @@ -2736,17 +3738,17 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { * @extends PostSummonAbAttr */ export class PostSummonWeatherSuppressedFormChangeAbAttr extends PostSummonAbAttr { - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostSummon(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return getPokemonWithWeatherBasedForms().length > 0; } /** * Triggers {@linkcode Arena.triggerWeatherBasedFormChangesToNormal | triggerWeatherBasedFormChangesToNormal} - * @param {Pokemon} pokemon the Pokemon with this ability - * @param passive n/a - * @param args n/a + * @param {Pokemon} _pokemon the Pokemon with this ability + * @param _passive n/a + * @param _args n/a */ - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { globalScene.arena.triggerWeatherBasedFormChangesToNormal(); } @@ -2767,9 +3769,11 @@ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr { this.ability = ability; } - override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const isCastformWithForecast = (pokemon.species.speciesId === SpeciesId.CASTFORM && this.ability === AbilityId.FORECAST); - const isCherrimWithFlowerGift = (pokemon.species.speciesId === SpeciesId.CHERRIM && this.ability === AbilityId.FLOWER_GIFT); + override canApplyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + const isCastformWithForecast = + pokemon.species.speciesId === SpeciesId.CASTFORM && this.ability === AbilityId.FORECAST; + const isCherrimWithFlowerGift = + pokemon.species.speciesId === SpeciesId.CHERRIM && this.ability === AbilityId.FLOWER_GIFT; return isCastformWithForecast || isCherrimWithFlowerGift; } @@ -2779,10 +3783,10 @@ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr { * {@linkcode SpeciesFormChange.SpeciesFormChangeWeatherTrigger | SpeciesFormChangeRevertWeatherFormTrigger} if it * is the specific Pokemon and ability * @param {Pokemon} pokemon the Pokemon with this ability - * @param passive n/a - * @param args n/a + * @param _passive n/a + * @param _args n/a */ - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeWeatherTrigger); globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeRevertWeatherFormTrigger); @@ -2801,17 +3805,21 @@ export class CommanderAbAttr extends AbAttr { super(true); } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { // If the ally Dondozo is fainted or was previously "commanded" by // another Pokemon, this effect cannot apply. // TODO: Should this work with X + Dondozo fusions? const ally = pokemon.getAlly(); - return globalScene.currentBattle?.double && !isNullOrUndefined(ally) && ally.species.speciesId === SpeciesId.DONDOZO - && !(ally.isFainted() || ally.getTag(BattlerTagType.COMMANDED)); + return ( + globalScene.currentBattle?.double && + !isNullOrUndefined(ally) && + ally.species.speciesId === SpeciesId.DONDOZO && + !(ally.isFainted() || ally.getTag(BattlerTagType.COMMANDED)) + ); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: null, args: any[]): void { + override apply(pokemon: Pokemon, _passive: boolean, simulated: boolean, _cancelled: null, _args: any[]): void { if (!simulated) { // Lapse the source's semi-invulnerable tags (to avoid visual inconsistencies) pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); @@ -2820,21 +3828,21 @@ export class CommanderAbAttr extends AbAttr { // Apply boosts from this effect to the ally Dondozo pokemon.getAlly()?.addTag(BattlerTagType.COMMANDED, 0, MoveId.NONE, pokemon.id); // Cancel the source Pokemon's next move (if a move is queued) - globalScene.phaseManager.tryRemovePhase((phase) => phase.is("MovePhase") && phase.pokemon === pokemon); + globalScene.phaseManager.tryRemovePhase(phase => phase.is("MovePhase") && phase.pokemon === pokemon); } } } export class PreSwitchOutAbAttr extends AbAttr { - constructor(showAbility: boolean = true) { + constructor(showAbility = true) { super(showAbility); } - canApplyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + canApplyPreSwitchOut(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return true; } - applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} + applyPreSwitchOut(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} } export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr { @@ -2842,11 +3850,11 @@ export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr { super(false); } - override canApplyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPreSwitchOut(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return !isNullOrUndefined(pokemon.status); } - override applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPreSwitchOut(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { pokemon.resetStatus(); pokemon.updateInfo(); @@ -2860,11 +3868,11 @@ export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr { export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { /** * @param pokemon The {@linkcode Pokemon} with the ability - * @param passive N/A - * @param args N/A + * @param _passive N/A + * @param _args N/A * @returns {boolean} Returns true if the weather clears, otherwise false. */ - override applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override applyPreSwitchOut(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { const weatherType = globalScene.arena.weather?.weatherType; let turnOffWeather = false; @@ -2875,8 +3883,8 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { pokemon.hasAbility(AbilityId.DESOLATE_LAND) && globalScene .getField(true) - .filter((p) => p !== pokemon) - .filter((p) => p.hasAbility(AbilityId.DESOLATE_LAND)).length === 0 + .filter(p => p !== pokemon) + .filter(p => p.hasAbility(AbilityId.DESOLATE_LAND)).length === 0 ) { turnOffWeather = true; } @@ -2886,8 +3894,8 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA) && globalScene .getField(true) - .filter((p) => p !== pokemon) - .filter((p) => p.hasAbility(AbilityId.PRIMORDIAL_SEA)).length === 0 + .filter(p => p !== pokemon) + .filter(p => p.hasAbility(AbilityId.PRIMORDIAL_SEA)).length === 0 ) { turnOffWeather = true; } @@ -2897,8 +3905,8 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { pokemon.hasAbility(AbilityId.DELTA_STREAM) && globalScene .getField(true) - .filter((p) => p !== pokemon) - .filter((p) => p.hasAbility(AbilityId.DELTA_STREAM)).length === 0 + .filter(p => p !== pokemon) + .filter(p => p.hasAbility(AbilityId.DELTA_STREAM)).length === 0 ) { turnOffWeather = true; } @@ -2919,11 +3927,11 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { } export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr { - override canApplyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPreSwitchOut(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return !pokemon.isFullHp(); } - override applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPreSwitchOut(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { const healAmount = toDmgValue(pokemon.getMaxHp() * 0.33); pokemon.heal(healAmount); @@ -2940,62 +3948,75 @@ export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr { export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr { private formFunc: (p: Pokemon) => number; - constructor(formFunc: ((p: Pokemon) => number)) { + constructor(formFunc: (p: Pokemon) => number) { super(); this.formFunc = formFunc; } - override canApplyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPreSwitchOut(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return this.formFunc(pokemon) !== pokemon.formIndex; } /** * On switch out, trigger the form change to the one defined in the ability * @param pokemon The pokemon switching out and changing form {@linkcode Pokemon} - * @param passive N/A - * @param args N/A + * @param _passive N/A + * @param _args N/A */ - override applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPreSwitchOut(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } } - } export class PreLeaveFieldAbAttr extends AbAttr { - canApplyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + canApplyPreLeaveField(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return true; } - applyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} + applyPreLeaveField(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} } /** * Clears Desolate Land/Primordial Sea/Delta Stream upon the Pokemon switching out. */ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr { - - override canApplyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPreLeaveField(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { const weatherType = globalScene.arena.weather?.weatherType; // Clear weather only if user's ability matches the weather and no other pokemon has the ability. switch (weatherType) { - case (WeatherType.HARSH_SUN): - if (pokemon.hasAbility(AbilityId.DESOLATE_LAND) - && globalScene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(AbilityId.DESOLATE_LAND)).length === 0) { + case WeatherType.HARSH_SUN: + if ( + pokemon.hasAbility(AbilityId.DESOLATE_LAND) && + globalScene + .getField(true) + .filter(p => p !== pokemon) + .filter(p => p.hasAbility(AbilityId.DESOLATE_LAND)).length === 0 + ) { return true; } break; - case (WeatherType.HEAVY_RAIN): - if (pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA) - && globalScene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(AbilityId.PRIMORDIAL_SEA)).length === 0) { + case WeatherType.HEAVY_RAIN: + if ( + pokemon.hasAbility(AbilityId.PRIMORDIAL_SEA) && + globalScene + .getField(true) + .filter(p => p !== pokemon) + .filter(p => p.hasAbility(AbilityId.PRIMORDIAL_SEA)).length === 0 + ) { return true; } break; - case (WeatherType.STRONG_WINDS): - if (pokemon.hasAbility(AbilityId.DELTA_STREAM) - && globalScene.getField(true).filter(p => p !== pokemon).filter(p => p.hasAbility(AbilityId.DELTA_STREAM)).length === 0) { + case WeatherType.STRONG_WINDS: + if ( + pokemon.hasAbility(AbilityId.DELTA_STREAM) && + globalScene + .getField(true) + .filter(p => p !== pokemon) + .filter(p => p.hasAbility(AbilityId.DELTA_STREAM)).length === 0 + ) { return true; } break; @@ -3004,11 +4025,11 @@ export class PreLeaveFieldClearWeatherAbAttr extends PreLeaveFieldAbAttr { } /** - * @param pokemon The {@linkcode Pokemon} with the ability - * @param passive N/A - * @param args N/A + * @param _pokemon The {@linkcode Pokemon} with the ability + * @param _passive N/A + * @param _args N/A */ - override applyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPreLeaveField(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { globalScene.arena.trySetWeather(WeatherType.NONE); } @@ -3023,11 +4044,16 @@ export class PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr extends PreLeaveFi super(false); } - public override canApplyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + public override canApplyPreLeaveField( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _args: any[], + ): boolean { return !!globalScene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS); } - public override applyPreLeaveField(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + public override applyPreLeaveField(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { const suppressTag = globalScene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS) as SuppressAbilitiesTag; suppressTag.onSourceLeave(globalScene.arena); } @@ -3035,22 +4061,23 @@ export class PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr extends PreLeaveFi export class PreStatStageChangeAbAttr extends AbAttr { canApplyPreStatStageChange( - pokemon: Pokemon | null, - passive: boolean, - simulated: boolean, - stat: BattleStat, - cancelled: BooleanHolder, - args: any[]): boolean { + _pokemon: Pokemon | null, + _passive: boolean, + _simulated: boolean, + _stat: BattleStat, + _cancelled: BooleanHolder, + _args: any[], + ): boolean { return true; } applyPreStatStageChange( - pokemon: Pokemon | null, - passive: boolean, - simulated: boolean, - stat: BattleStat, - cancelled: BooleanHolder, - args: any[], + _pokemon: Pokemon | null, + _passive: boolean, + _simulated: boolean, + _stat: BattleStat, + _cancelled: BooleanHolder, + _args: any[], ): void {} } @@ -3060,7 +4087,7 @@ export class PreStatStageChangeAbAttr extends AbAttr { */ export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { /** {@linkcode BattleStat} to reflect */ - private reflectedStat? : BattleStat; + private reflectedStat?: BattleStat; /** * Apply the {@linkcode ReflectStatStageChangeAbAttr} to an interaction @@ -3071,12 +4098,21 @@ export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { * @param cancelled The {@linkcode BooleanHolder} that will be set to true due to reflection * @param args */ - override applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, simulated: boolean, stat: BattleStat, cancelled: BooleanHolder, args: any[]): void { + override applyPreStatStageChange( + _pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + stat: BattleStat, + cancelled: BooleanHolder, + args: any[], + ): void { const attacker: Pokemon = args[0]; const stages = args[1]; this.reflectedStat = stat; if (!simulated) { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(attacker.getBattlerIndex(), false, [ stat ], stages, true, false, true, null, true)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(attacker.getBattlerIndex(), false, [stat], stages, true, false, true, null, true), + ); } cancelled.value = true; } @@ -3085,7 +4121,7 @@ export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { return i18next.t("abilityTriggers:protectStat", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, - statName: this.reflectedStat ? i18next.t(getStatKey(this.reflectedStat)) : i18next.t("battle:stats") + statName: this.reflectedStat ? i18next.t(getStatKey(this.reflectedStat)) : i18next.t("battle:stats"), }); } } @@ -3103,7 +4139,14 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { this.protectedStat = protectedStat; } - override canApplyPreStatStageChange(pokemon: Pokemon | null, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: BooleanHolder, args: any[]): boolean { + override canApplyPreStatStageChange( + _pokemon: Pokemon | null, + _passive: boolean, + _simulated: boolean, + stat: BattleStat, + _cancelled: BooleanHolder, + _args: any[], + ): boolean { return isNullOrUndefined(this.protectedStat) || stat === this.protectedStat; } @@ -3112,11 +4155,18 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { * @param _pokemon * @param _passive * @param simulated - * @param stat the {@linkcode BattleStat} being affected + * @param _stat the {@linkcode BattleStat} being affected * @param cancelled The {@linkcode BooleanHolder} that will be set to true if the stat is protected * @param _args */ - override applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, stat: BattleStat, cancelled: BooleanHolder, _args: any[]): void { + override applyPreStatStageChange( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _stat: BattleStat, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } @@ -3124,7 +4174,7 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { return i18next.t("abilityTriggers:protectStat", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, - statName: this.protectedStat ? i18next.t(getStatKey(this.protectedStat)) : i18next.t("battle:stats") + statName: this.protectedStat ? i18next.t(getStatKey(this.protectedStat)) : i18next.t("battle:stats"), }); } } @@ -3142,26 +4192,45 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr { constructor(...effects: StatusEffect[]) { /** This effect does not require a damaging move */ - super((user, target, move) => true); + super((_user, _target, _move) => true); this.effects = effects; } - override canApplyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) - && this.effects.indexOf(args[0]) > -1 && !defender.isFainted() && defender.canAddTag(BattlerTagType.CONFUSED); + override canApplyPostAttack( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + defender: Pokemon, + move: Move, + hitResult: HitResult | null, + args: any[], + ): boolean { + return ( + super.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args) && + this.effects.indexOf(args[0]) > -1 && + !defender.isFainted() && + defender.canAddTag(BattlerTagType.CONFUSED) + ); } - /** * Applies confusion to the target pokemon. * @param pokemon {@link Pokemon} attacking - * @param passive N/A + * @param _passive N/A * @param defender {@link Pokemon} defending * @param move {@link Move} used to apply status effect and confusion - * @param hitResult N/A - * @param args [0] {@linkcode StatusEffect} applied by move + * @param _hitResult N/A + * @param _args [0] {@linkcode StatusEffect} applied by move */ - override applyPostAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { + override applyPostAttack( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + defender: Pokemon, + move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { defender.addTag(BattlerTagType.CONFUSED, pokemon.randBattleSeedIntRange(2, 5), move.id, defender.id); } @@ -3171,22 +4240,23 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr { export class PreSetStatusAbAttr extends AbAttr { /** Return whether the ability attribute can be applied */ canApplyPreSetStatus( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - effect: StatusEffect | undefined, - cancelled: BooleanHolder, - args: any[]): boolean { + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _effect: StatusEffect | undefined, + _cancelled: BooleanHolder, + _args: any[], + ): boolean { return true; } applyPreSetStatus( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - effect: StatusEffect | undefined, - cancelled: BooleanHolder, - args: any[], + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _effect: StatusEffect | undefined, + _cancelled: BooleanHolder, + _args: any[], ): void {} } @@ -3206,35 +4276,49 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { this.immuneEffects = immuneEffects; } - override canApplyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect, cancelled: BooleanHolder, args: any[]): boolean { - return effect !== StatusEffect.FAINT && this.immuneEffects.length < 1 || this.immuneEffects.includes(effect); + override canApplyPreSetStatus( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + effect: StatusEffect, + _cancelled: BooleanHolder, + _args: any[], + ): boolean { + return (effect !== StatusEffect.FAINT && this.immuneEffects.length < 1) || this.immuneEffects.includes(effect); } /** * Applies immunity to supplied status effects. * - * @param pokemon - The Pokémon to which the status is being applied. - * @param passive - n/a + * @param _pokemon - The Pokémon to which the status is being applied. + * @param _passive - n/a * @param effect - The status effect being applied. * @param cancelled - A holder for a boolean value indicating if the status application was cancelled. - * @param args - n/a + * @param _args - n/a */ - override applyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect, cancelled: BooleanHolder, args: any[]): void { + override applyPreSetStatus( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + effect: StatusEffect, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; this.lastEffect = effect; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { - return this.immuneEffects.length ? - i18next.t("abilityTriggers:statusEffectImmunityWithName", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - abilityName, - statusEffectName: getStatusEffectDescriptor(this.lastEffect) - }) : - i18next.t("abilityTriggers:statusEffectImmunity", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - abilityName - }); + getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + return this.immuneEffects.length + ? i18next.t("abilityTriggers:statusEffectImmunityWithName", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + statusEffectName: getStatusEffectDescriptor(this.lastEffect), + }) + : i18next.t("abilityTriggers:statusEffectImmunity", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + }); } } @@ -3242,13 +4326,13 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { * Provides immunity to status effects to the user. * @extends PreSetStatusEffectImmunityAbAttr */ -export class StatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr { } +export class StatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr {} /** * Provides immunity to status effects to the user's field. * @extends PreSetStatusEffectImmunityAbAttr */ -export class UserFieldStatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr { } +export class UserFieldStatusEffectImmunityAbAttr extends PreSetStatusEffectImmunityAbAttr {} /** * Conditionally provides immunity to status effects to the user's field. @@ -3267,16 +4351,27 @@ export class ConditionalUserFieldStatusEffectImmunityAbAttr extends UserFieldSta /** * Evaluate the condition to determine if the {@linkcode ConditionalUserFieldStatusEffectImmunityAbAttr} can be applied. - * @param pokemon The pokemon with the ability - * @param passive unused - * @param simulated Whether the ability is being simulated + * @param _pokemon The pokemon with the ability + * @param _passive unused + * @param _simulated Whether the ability is being simulated * @param effect The status effect being applied * @param cancelled Holds whether the status effect was cancelled by a prior effect * @param args `Args[0]` is the target of the status effect, `Args[1]` is the source. * @returns Whether the ability can be applied to cancel the status effect. */ - override canApplyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect, cancelled: BooleanHolder, args: [Pokemon, Pokemon | null, ...any]): boolean { - return (!cancelled.value && effect !== StatusEffect.FAINT && this.immuneEffects.length < 1 || this.immuneEffects.includes(effect)) && this.condition(args[0], args[1]); + override canApplyPreSetStatus( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + effect: StatusEffect, + cancelled: BooleanHolder, + args: [Pokemon, Pokemon | null, ...any], + ): boolean { + return ( + ((!cancelled.value && effect !== StatusEffect.FAINT && this.immuneEffects.length < 1) || + this.immuneEffects.includes(effect)) && + this.condition(args[0], args[1]) + ); } constructor(condition: (target: Pokemon, source: Pokemon | null) => boolean, ...immuneEffects: StatusEffect[]) { @@ -3298,27 +4393,38 @@ export class ConditionalUserFieldProtectStatAbAttr extends PreStatStageChangeAbA /** If the method evaluates to true, the stat will be protected. */ protected condition: (target: Pokemon) => boolean; - constructor(condition: (target: Pokemon) => boolean, protectedStat?: BattleStat) { + constructor(condition: (target: Pokemon) => boolean, _protectedStat?: BattleStat) { super(); this.condition = condition; } /** * Determine whether the {@linkcode ConditionalUserFieldProtectStatAbAttr} can be applied. - * @param pokemon The pokemon with the ability - * @param passive unused - * @param simulated Unused + * @param _pokemon The pokemon with the ability + * @param _passive unused + * @param _simulated Unused * @param stat The stat being affected * @param cancelled Holds whether the stat change was already prevented. * @param args Args[0] is the target pokemon of the stat change. * @returns */ - override canApplyPreStatStageChange(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: BooleanHolder, args: [Pokemon, ...any]): boolean { + override canApplyPreStatStageChange( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + stat: BattleStat, + cancelled: BooleanHolder, + args: [Pokemon, ...any], + ): boolean { const target = args[0]; if (!target) { return false; } - return !cancelled.value && (isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) && this.condition(target); + return ( + !cancelled.value && + (isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) && + this.condition(target) + ); } /** @@ -3330,31 +4436,37 @@ export class ConditionalUserFieldProtectStatAbAttr extends PreStatStageChangeAbA * @param cancelled Will be set to true if the stat change is prevented * @param _args unused */ - override applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _stat: BattleStat, cancelled: BooleanHolder, _args: any[]): void { + override applyPreStatStageChange( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _stat: BattleStat, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } } - export class PreApplyBattlerTagAbAttr extends AbAttr { canApplyPreApplyBattlerTag( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - tag: BattlerTag, - cancelled: BooleanHolder, - args: any[], + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _tag: BattlerTag, + _cancelled: BooleanHolder, + _args: any[], ): boolean { return true; } applyPreApplyBattlerTag( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - tag: BattlerTag, - cancelled: BooleanHolder, - args: any[], + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _tag: BattlerTag, + _cancelled: BooleanHolder, + _args: any[], ): void {} } @@ -3368,24 +4480,38 @@ export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr { constructor(immuneTagTypes: BattlerTagType | BattlerTagType[]) { super(true); - this.immuneTagTypes = Array.isArray(immuneTagTypes) ? immuneTagTypes : [ immuneTagTypes ]; + this.immuneTagTypes = Array.isArray(immuneTagTypes) ? immuneTagTypes : [immuneTagTypes]; } - override canApplyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, simulated: boolean, tag: BattlerTag, cancelled: BooleanHolder, args: any[]): boolean { + override canApplyPreApplyBattlerTag( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + tag: BattlerTag, + cancelled: BooleanHolder, + _args: any[], + ): boolean { this.battlerTag = tag; return !cancelled.value && this.immuneTagTypes.includes(tag.tagType); } - override applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, simulated: boolean, tag: BattlerTag, cancelled: BooleanHolder, args: any[]): void { + override applyPreApplyBattlerTag( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _tag: BattlerTag, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:battlerTagImmunity", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, - battlerTagName: this.battlerTag.getDescriptor() + battlerTagName: this.battlerTag.getDescriptor(), }); } } @@ -3394,13 +4520,13 @@ export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr { * Provides immunity to BattlerTags {@linkcode BattlerTag} to the user. * @extends PreApplyBattlerTagImmunityAbAttr */ -export class BattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr { } +export class BattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr {} /** * Provides immunity to BattlerTags {@linkcode BattlerTag} to the user's field. * @extends PreApplyBattlerTagImmunityAbAttr */ -export class UserFieldBattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr { } +export class UserFieldBattlerTagImmunityAbAttr extends PreApplyBattlerTagImmunityAbAttr {} export class ConditionalUserFieldBattlerTagImmunityAbAttr extends UserFieldBattlerTagImmunityAbAttr { private condition: (target: Pokemon) => boolean; @@ -3415,8 +4541,17 @@ export class ConditionalUserFieldBattlerTagImmunityAbAttr extends UserFieldBattl * @param args Args[0] is the target that the tag is attempting to be applied to * @returns Whether the ability can be used to cancel the battler tag */ - override canApplyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, simulated: boolean, tag: BattlerTag, cancelled: BooleanHolder, args: [Pokemon, ...any]): boolean { - return super.canApplyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args) && this.condition(args[0]); + override canApplyPreApplyBattlerTag( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + tag: BattlerTag, + cancelled: BooleanHolder, + args: [Pokemon, ...any], + ): boolean { + return ( + super.canApplyPreApplyBattlerTag(pokemon, passive, simulated, tag, cancelled, args) && this.condition(args[0]) + ); } constructor(condition: (target: Pokemon) => boolean, immuneTagTypes: BattlerTagType | BattlerTagType[]) { @@ -3435,8 +4570,14 @@ export class BlockCritAbAttr extends AbAttr { * Apply the block crit ability by setting the value in the provided boolean holder to false * @param args - [0] is a boolean holder representing whether the attack can crit */ - override apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: BooleanHolder, args: [BooleanHolder, ...any]): void { - (args[0]).value = false; + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: [BooleanHolder, ...any], + ): void { + args[0].value = false; } } @@ -3448,13 +4589,19 @@ export class BonusCritAbAttr extends AbAttr { /** * Apply the bonus crit ability by increasing the value in the provided number holder by 1 * - * @param pokemon The pokemon with the BonusCrit ability (unused) - * @param passive Unused - * @param simulated Unused - * @param cancelled Unused + * @param _pokemon The pokemon with the BonusCrit ability (unused) + * @param _passive Unused + * @param _simulated Unused + * @param _cancelled Unused * @param args Args[0] is a number holder containing the crit stage. */ - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: [NumberHolder, ...any]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: [NumberHolder, ...any], + ): void { (args[0] as NumberHolder).value += 1; } } @@ -3468,12 +4615,18 @@ export class MultCritAbAttr extends AbAttr { this.multAmount = multAmount; } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { const critMult = args[0] as NumberHolder; return critMult.value > 1; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { const critMult = args[0] as NumberHolder; critMult.value *= this.multAmount; } @@ -3487,25 +4640,31 @@ export class MultCritAbAttr extends AbAttr { export class ConditionalCritAbAttr extends AbAttr { private condition: PokemonAttackCondition; - constructor(condition: PokemonAttackCondition, checkUser?: boolean) { + constructor(condition: PokemonAttackCondition, _checkUser?: boolean) { super(false); this.condition = condition; } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const target = (args[1] as Pokemon); - const move = (args[2] as Move); + override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { + const target = args[1] as Pokemon; + const move = args[2] as Move; return this.condition(pokemon, target, move); } /** - * @param pokemon {@linkcode Pokemon} user. + * @param _pokemon {@linkcode Pokemon} user. * @param args [0] {@linkcode BooleanHolder} If true critical hit is guaranteed. * [1] {@linkcode Pokemon} Target. * [2] {@linkcode Move} used by ability user. */ - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[0] as BooleanHolder).value = true; } } @@ -3515,7 +4674,13 @@ export class BlockNonDirectDamageAbAttr extends AbAttr { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } } @@ -3535,23 +4700,35 @@ export class BlockStatusDamageAbAttr extends AbAttr { this.effects = effects; } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return !!pokemon.status?.effect && this.effects.includes(pokemon.status.effect); } /** - * @param {Pokemon} pokemon The pokemon with the ability - * @param {boolean} passive N/A + * @param {Pokemon} _pokemon The pokemon with the ability + * @param {boolean} _passive N/A * @param {BooleanHolder} cancelled Whether to cancel the status damage - * @param {any[]} args N/A + * @param {any[]} _args N/A */ - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } } export class BlockOneHitKOAbAttr extends AbAttr { - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } } @@ -3576,39 +4753,46 @@ export class ChangeMovePriorityAbAttr extends AbAttr { this.changeAmount = changeAmount; } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { return this.moveFunc(pokemon, args[0] as Move); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[1] as NumberHolder).value += this.changeAmount; } } -export class IgnoreContactAbAttr extends AbAttr { } +export class IgnoreContactAbAttr extends AbAttr {} export class PreWeatherEffectAbAttr extends AbAttr { canApplyPreWeatherEffect( - pokemon: Pokemon, - passive: Boolean, - simulated: boolean, - weather: Weather | null, - cancelled: BooleanHolder, - args: any[]): boolean { + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _weather: Weather | null, + _cancelled: BooleanHolder, + _args: any[], + ): boolean { return true; } applyPreWeatherEffect( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - weather: Weather | null, - cancelled: BooleanHolder, - args: any[], + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _weather: Weather | null, + _cancelled: BooleanHolder, + _args: any[], ): void {} } -export class PreWeatherDamageAbAttr extends PreWeatherEffectAbAttr { } +export class PreWeatherDamageAbAttr extends PreWeatherEffectAbAttr {} export class BlockWeatherDamageAttr extends PreWeatherDamageAbAttr { private weatherTypes: WeatherType[]; @@ -3619,11 +4803,25 @@ export class BlockWeatherDamageAttr extends PreWeatherDamageAbAttr { this.weatherTypes = weatherTypes; } - override canApplyPreWeatherEffect(pokemon: Pokemon, passive: Boolean, simulated: boolean, weather: Weather, cancelled: BooleanHolder, args: any[]): boolean { + override canApplyPreWeatherEffect( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + weather: Weather, + _cancelled: BooleanHolder, + _args: any[], + ): boolean { return !this.weatherTypes.length || this.weatherTypes.indexOf(weather?.weatherType) > -1; } - override applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, cancelled: BooleanHolder, args: any[]): void { + override applyPreWeatherEffect( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _weather: Weather, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } } @@ -3637,11 +4835,25 @@ export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr { this.affectsImmutable = !!affectsImmutable; } - override canApplyPreWeatherEffect(pokemon: Pokemon, passive: Boolean, simulated: boolean, weather: Weather, cancelled: BooleanHolder, args: any[]): boolean { + override canApplyPreWeatherEffect( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + weather: Weather, + _cancelled: BooleanHolder, + _args: any[], + ): boolean { return this.affectsImmutable || weather.isImmutable(); } - override applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, cancelled: BooleanHolder, args: any[]): void { + override applyPreWeatherEffect( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _weather: Weather, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } } @@ -3665,7 +4877,8 @@ function getSheerForceHitDisableAbCondition(): AbAttrCondition { } /** `true` if the last move's chance is above 0 and the last attacker's ability is sheer force */ - const SheerForceAffected = allMoves[lastReceivedAttack.move].chance >= 0 && lastAttacker.hasAbility(AbilityId.SHEER_FORCE); + const SheerForceAffected = + allMoves[lastReceivedAttack.move].chance >= 0 && lastAttacker.hasAbility(AbilityId.SHEER_FORCE); return !SheerForceAffected; }; @@ -3693,7 +4906,10 @@ function getAnticipationCondition(): AbAttrCondition { continue; } // the move's base type (not accounting for variable type changes) is super effective - if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true, undefined, move.getMove()) >= 2) { + if ( + move.getMove() instanceof AttackMove && + pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true, undefined, move.getMove()) >= 2 + ) { return true; } // move is a OHKO @@ -3702,18 +4918,35 @@ function getAnticipationCondition(): AbAttrCondition { } // edge case for hidden power, type is computed if (move.getMove().id === MoveId.HIDDEN_POWER) { - const iv_val = Math.floor(((opponent.ivs[Stat.HP] & 1) - + (opponent.ivs[Stat.ATK] & 1) * 2 - + (opponent.ivs[Stat.DEF] & 1) * 4 - + (opponent.ivs[Stat.SPD] & 1) * 8 - + (opponent.ivs[Stat.SPATK] & 1) * 16 - + (opponent.ivs[Stat.SPDEF] & 1) * 32) * 15 / 63); + const iv_val = Math.floor( + (((opponent.ivs[Stat.HP] & 1) + + (opponent.ivs[Stat.ATK] & 1) * 2 + + (opponent.ivs[Stat.DEF] & 1) * 4 + + (opponent.ivs[Stat.SPD] & 1) * 8 + + (opponent.ivs[Stat.SPATK] & 1) * 16 + + (opponent.ivs[Stat.SPDEF] & 1) * 32) * + 15) / + 63, + ); const type = [ - PokemonType.FIGHTING, PokemonType.FLYING, PokemonType.POISON, PokemonType.GROUND, - PokemonType.ROCK, PokemonType.BUG, PokemonType.GHOST, PokemonType.STEEL, - PokemonType.FIRE, PokemonType.WATER, PokemonType.GRASS, PokemonType.ELECTRIC, - PokemonType.PSYCHIC, PokemonType.ICE, PokemonType.DRAGON, PokemonType.DARK ][iv_val]; + PokemonType.FIGHTING, + PokemonType.FLYING, + PokemonType.POISON, + PokemonType.GROUND, + PokemonType.ROCK, + PokemonType.BUG, + PokemonType.GHOST, + PokemonType.STEEL, + PokemonType.FIRE, + PokemonType.WATER, + PokemonType.GRASS, + PokemonType.ELECTRIC, + PokemonType.PSYCHIC, + PokemonType.ICE, + PokemonType.DRAGON, + PokemonType.DARK, + ][iv_val]; if (pokemon.getAttackTypeEffectiveness(type, opponent) >= 2) { return true; @@ -3743,7 +4976,7 @@ export class ForewarnAbAttr extends PostSummonAbAttr { super(true); } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { let maxPowerSeen = 0; let maxMove = ""; let movePower = 0; @@ -3753,7 +4986,11 @@ export class ForewarnAbAttr extends PostSummonAbAttr { movePower = 1; } else if (move?.getMove().hasAttr(OneHitKOAttr)) { movePower = 150; - } else if (move?.getMove().id === MoveId.COUNTER || move?.getMove().id === MoveId.MIRROR_COAT || move?.getMove().id === MoveId.METAL_BURST) { + } else if ( + move?.getMove().id === MoveId.COUNTER || + move?.getMove().id === MoveId.MIRROR_COAT || + move?.getMove().id === MoveId.METAL_BURST + ) { movePower = 120; } else if (move?.getMove().power === -1) { movePower = 80; @@ -3768,7 +5005,12 @@ export class ForewarnAbAttr extends PostSummonAbAttr { } } if (!simulated) { - globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:forewarn", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: maxMove })); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:forewarn", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + moveName: maxMove, + }), + ); } } } @@ -3778,10 +5020,16 @@ export class FriskAbAttr extends PostSummonAbAttr { super(true); } - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { for (const opponent of pokemon.getOpponents()) { - globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:frisk", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), opponentName: opponent.name, opponentAbilityName: opponent.getAbility().name })); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:frisk", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + opponentName: opponent.name, + opponentAbilityName: opponent.getAbility().name, + }), + ); setAbilityRevealed(opponent); } } @@ -3789,11 +5037,23 @@ export class FriskAbAttr extends PostSummonAbAttr { } export class PostWeatherChangeAbAttr extends AbAttr { - canApplyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { + canApplyPostWeatherChange( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _weather: WeatherType, + _args: any[], + ): boolean { return true; } - applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): void {} + applyPostWeatherChange( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _weather: WeatherType, + _args: any[], + ): void {} } /** @@ -3812,9 +5072,17 @@ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { this.formRevertingWeathers = formRevertingWeathers; } - override canApplyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { - const isCastformWithForecast = (pokemon.species.speciesId === SpeciesId.CASTFORM && this.ability === AbilityId.FORECAST); - const isCherrimWithFlowerGift = (pokemon.species.speciesId === SpeciesId.CHERRIM && this.ability === AbilityId.FLOWER_GIFT); + override canApplyPostWeatherChange( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _weather: WeatherType, + _args: any[], + ): boolean { + const isCastformWithForecast = + pokemon.species.speciesId === SpeciesId.CASTFORM && this.ability === AbilityId.FORECAST; + const isCherrimWithFlowerGift = + pokemon.species.speciesId === SpeciesId.CHERRIM && this.ability === AbilityId.FLOWER_GIFT; return isCastformWithForecast || isCherrimWithFlowerGift; } @@ -3822,12 +5090,18 @@ export class PostWeatherChangeFormChangeAbAttr extends PostWeatherChangeAbAttr { /** * Calls {@linkcode Arena.triggerWeatherBasedFormChangesToNormal | triggerWeatherBasedFormChangesToNormal} when the * weather changed to form-reverting weather, otherwise calls {@linkcode Arena.triggerWeatherBasedFormChanges | triggerWeatherBasedFormChanges} - * @param {Pokemon} pokemon the Pokemon with this ability - * @param passive n/a - * @param weather n/a - * @param args n/a + * @param {Pokemon} _pokemon the Pokemon with this ability + * @param _passive n/a + * @param _weather n/a + * @param _args n/a */ - override applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): void { + override applyPostWeatherChange( + _pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _weather: WeatherType, + _args: any[], + ): void { if (simulated) { return; } @@ -3855,11 +5129,23 @@ export class PostWeatherChangeAddBattlerTagAttr extends PostWeatherChangeAbAttr this.weatherTypes = weatherTypes; } - override canApplyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): boolean { + override canApplyPostWeatherChange( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + weather: WeatherType, + _args: any[], + ): boolean { return !!this.weatherTypes.find(w => weather === w) && pokemon.canAddTag(this.tagType); } - override applyPostWeatherChange(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: WeatherType, args: any[]): void { + override applyPostWeatherChange( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _weather: WeatherType, + _args: any[], + ): void { if (!simulated) { pokemon.addTag(this.tagType, this.turnCount); } @@ -3876,20 +5162,21 @@ export class PostWeatherLapseAbAttr extends AbAttr { } canApplyPostWeatherLapse( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - weather: Weather | null, - args: any[]): boolean { + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _weather: Weather | null, + _args: any[], + ): boolean { return true; } applyPostWeatherLapse( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - weather: Weather | null, - args: any[], + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _weather: Weather | null, + _args: any[], ): void {} getCondition(): AbAttrCondition { @@ -3906,15 +5193,36 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr { this.healFactor = healFactor; } - override canApplyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather | null, args: any[]): boolean { + override canApplyPostWeatherLapse( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _weather: Weather | null, + _args: any[], + ): boolean { return !pokemon.isFullHp(); } - override applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, args: any[]): void { + override applyPostWeatherLapse( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + _weather: Weather, + _args: any[], + ): void { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; if (!simulated) { - globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / (16 / this.healFactor)), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + globalScene.phaseManager.unshiftPhase( + new PokemonHealPhase( + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / (16 / this.healFactor)), + i18next.t("abilityTriggers:postWeatherLapseHeal", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + }), + true, + ), + ); } } } @@ -3928,25 +5236,56 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr { this.damageFactor = damageFactor; } - override canApplyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather | null, args: any[]): boolean { + override canApplyPostWeatherLapse( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _weather: Weather | null, + _args: any[], + ): boolean { return !pokemon.hasAbilityWithAttr(BlockNonDirectDamageAbAttr); } - override applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, args: any[]): void { + override applyPostWeatherLapse( + pokemon: Pokemon, + passive: boolean, + simulated: boolean, + _weather: Weather, + _args: any[], + ): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:postWeatherLapseDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName })); - pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / (16 / this.damageFactor)), { result: HitResult.INDIRECT }); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:postWeatherLapseDamage", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + }), + ); + pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / (16 / this.damageFactor)), { + result: HitResult.INDIRECT, + }); } } } export class PostTerrainChangeAbAttr extends AbAttr { - canApplyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): boolean { + canApplyPostTerrainChange( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _terrain: TerrainType, + _args: any[], + ): boolean { return true; } - applyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): void {} + applyPostTerrainChange( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _terrain: TerrainType, + _args: any[], + ): void {} } export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr { @@ -3962,11 +5301,23 @@ export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr this.terrainTypes = terrainTypes; } - override canApplyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): boolean { + override canApplyPostTerrainChange( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + terrain: TerrainType, + _args: any[], + ): boolean { return !!this.terrainTypes.find(t => t === terrain) && pokemon.canAddTag(this.tagType); } - override applyPostTerrainChange(pokemon: Pokemon, passive: boolean, simulated: boolean, terrain: TerrainType, args: any[]): void { + override applyPostTerrainChange( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _terrain: TerrainType, + _args: any[], + ): void { if (!simulated) { pokemon.addTag(this.tagType, this.turnCount); } @@ -3974,18 +5325,18 @@ export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr } function getTerrainCondition(...terrainTypes: TerrainType[]): AbAttrCondition { - return (pokemon: Pokemon) => { + return (_pokemon: Pokemon) => { const terrainType = globalScene.arena.terrain?.terrainType; return !!terrainType && terrainTypes.indexOf(terrainType) > -1; }; } export class PostTurnAbAttr extends AbAttr { - canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + canApplyPostTurn(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return true; } - applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} + applyPostTurn(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} } /** @@ -4003,20 +5354,26 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { this.effects = effects; } - override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return !isNullOrUndefined(pokemon.status) && this.effects.includes(pokemon.status.effect) && !pokemon.isFullHp(); } /** * @param {Pokemon} pokemon The pokemon with the ability that will receive the healing * @param {Boolean} passive N/A - * @param {any[]} args N/A + * @param {any[]} _args N/A */ - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / 8), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true)); + globalScene.phaseManager.unshiftPhase( + new PokemonHealPhase( + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / 8), + i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), + true, + ), + ); } } } @@ -4034,7 +5391,7 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { this.allyTarget = allyTarget; } - override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { if (this.allyTarget) { this.target = pokemon.getAlly(); } else { @@ -4045,9 +5402,11 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { return !!effect && effect !== StatusEffect.FAINT; } - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostTurn(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated && this.target?.status) { - globalScene.phaseManager.queueMessage(getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target))); + globalScene.phaseManager.queueMessage( + getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target)), + ); this.target.resetStatus(false); this.target.updateInfo(); } @@ -4063,29 +5422,26 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr { * Array containing all {@linkcode BerryType | BerryTypes} that are under cap and able to be restored. * Stored inside the class for a minor performance boost */ - private berriesUnderCap: BerryType[] + private berriesUnderCap: BerryType[]; /** * @param procChance - function providing chance to restore an item * @see {@linkcode createEatenBerry()} */ - constructor( - private procChance: (pokemon: Pokemon) => number - ) { + constructor(private procChance: (pokemon: Pokemon) => number) { super(); } override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { // Ensure we have at least 1 recoverable berry (at least 1 berry in berriesEaten is not capped) const cappedBerries = new Set( - globalScene.getModifiers(BerryModifier, pokemon.isPlayer()).filter( - bm => bm.pokemonId === pokemon.id && bm.getCountUnderMax() < 1 - ).map(bm => bm.berryType) + globalScene + .getModifiers(BerryModifier, pokemon.isPlayer()) + .filter(bm => bm.pokemonId === pokemon.id && bm.getCountUnderMax() < 1) + .map(bm => bm.berryType), ); - this.berriesUnderCap = pokemon.battleData.berriesEaten.filter( - bt => !cappedBerries.has(bt) - ); + this.berriesUnderCap = pokemon.battleData.berriesEaten.filter(bt => !cappedBerries.has(bt)); if (!this.berriesUnderCap.length) { return false; @@ -4096,7 +5452,7 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr { return this.procChance(pokemon) >= pass; } - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { this.createEatenBerry(pokemon); } @@ -4116,12 +5472,12 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr { // Add the randomly chosen berry or update the existing one const berryModifier = globalScene.findModifier( - (m) => m instanceof BerryModifier && m.berryType === chosenBerryType && m.pokemonId == pokemon.id, - pokemon.isPlayer() + m => m instanceof BerryModifier && m.berryType === chosenBerryType && m.pokemonId === pokemon.id, + pokemon.isPlayer(), ) as BerryModifier | undefined; if (berryModifier) { - berryModifier.stackCount++ + berryModifier.stackCount++; } else { const newBerry = new BerryModifier(chosenBerry, pokemon.id, chosenBerryType, 1); if (pokemon.isPlayer()) { @@ -4132,15 +5488,20 @@ export class PostTurnRestoreBerryAbAttr extends PostTurnAbAttr { } globalScene.updateModifiers(pokemon.isPlayer()); - globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:postTurnLootCreateEatenBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: chosenBerry.name })); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:postTurnLootCreateEatenBerry", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + berryName: chosenBerry.name, + }), + ); return true; } } /** - * Attribute to track and re-trigger last turn's berries at the end of the `BerryPhase`. - * Used by {@linkcode AbilityId.CUD_CHEW}. -*/ + * Attribute to track and re-trigger last turn's berries at the end of the `BerryPhase`. + * Used by {@linkcode AbilityId.CUD_CHEW}. + */ export class RepeatBerryNextTurnAbAttr extends PostTurnAbAttr { /** * @returns `true` if the pokemon ate anything last turn @@ -4161,7 +5522,13 @@ export class RepeatBerryNextTurnAbAttr extends PostTurnAbAttr { * @param _cancelled - N/A * @param _args - N/A */ - override apply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: BooleanHolder | null, _args: any[]): void { + override apply( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder | null, + _args: any[], + ): void { globalScene.phaseManager.unshiftPhase( new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM), ); @@ -4181,7 +5548,7 @@ export class RepeatBerryNextTurnAbAttr extends PostTurnAbAttr { /** * @returns always `true` as we always want to move berries into summon data */ - override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + override canApplyPostTurn(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { this.showAbility = false; // don't show popup for turn end berry moving (should ideally be hidden) return true; } @@ -4210,15 +5577,15 @@ export class MoodyAbAttr extends PostTurnAbAttr { /** * Randomly increases one stat stage by 2 and decreases a different stat stage by 1 * @param {Pokemon} pokemon Pokemon that has this ability - * @param passive N/A + * @param _passive N/A * @param simulated true if applying in a simulated call. - * @param args N/A + * @param _args N/A * * Any stat stages at +6 or -6 are excluded from being increased or decreased, respectively * If the pokemon already has all stat stages raised to 6, it will only decrease one stat stage by 1 * If the pokemon already has all stat stages lowered to -6, it will only increase one stat stage by 2 */ - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { const canRaise = EFFECTIVE_STATS.filter(s => pokemon.getStatStage(s) < 6); let canLower = EFFECTIVE_STATS.filter(s => pokemon.getStatStage(s) > -6); @@ -4226,41 +5593,53 @@ export class MoodyAbAttr extends PostTurnAbAttr { if (canRaise.length > 0) { const raisedStat = canRaise[pokemon.randBattleSeedInt(canRaise.length)]; canLower = canRaise.filter(s => s !== raisedStat); - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ raisedStat ], 2)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), true, [raisedStat], 2), + ); } if (canLower.length > 0) { const loweredStat = canLower[pokemon.randBattleSeedInt(canLower.length)]; - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ loweredStat ], -1)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), true, [loweredStat], -1), + ); } } } } export class SpeedBoostAbAttr extends PostTurnAbAttr { - constructor() { super(true); } - override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { return simulated || (!pokemon.turnData.switchedInThisTurn && !pokemon.turnData.failedRunAway); } - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ Stat.SPD ], 1)); + override applyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPD], 1)); } } export class PostTurnHealAbAttr extends PostTurnAbAttr { - override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return !pokemon.isFullHp(); } - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / 16), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + globalScene.phaseManager.unshiftPhase( + new PokemonHealPhase( + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / 16), + i18next.t("abilityTriggers:postTurnHeal", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + }), + true, + ), + ); } } } @@ -4268,79 +5647,91 @@ export class PostTurnHealAbAttr extends PostTurnAbAttr { export class PostTurnFormChangeAbAttr extends PostTurnAbAttr { private formFunc: (p: Pokemon) => number; - constructor(formFunc: ((p: Pokemon) => number)) { + constructor(formFunc: (p: Pokemon) => number) { super(true); this.formFunc = formFunc; } - override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return this.formFunc(pokemon) !== pokemon.formIndex; } - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeAbilityTrigger, false); } } } - /** * Attribute used for abilities (Bad Dreams) that damages the opponents for being asleep */ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { - override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return pokemon.getOpponents().some(opp => (opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(AbilityId.COMATOSE)) && !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !opp.switchOutStatus); + override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + return pokemon + .getOpponents() + .some( + opp => + (opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(AbilityId.COMATOSE)) && + !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && + !opp.switchOutStatus, + ); } /** * Deals damage to all sleeping opponents equal to 1/8 of their max hp (min 1) * @param pokemon {@linkcode Pokemon} with this ability - * @param passive N/A + * @param _passive N/A * @param simulated `true` if applying in a simulated call. - * @param args N/A + * @param _args N/A */ - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { for (const opp of pokemon.getOpponents()) { - if ((opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(AbilityId.COMATOSE)) && !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !opp.switchOutStatus) { + if ( + (opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(AbilityId.COMATOSE)) && + !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && + !opp.switchOutStatus + ) { if (!simulated) { opp.damageAndUpdate(toDmgValue(opp.getMaxHp() / 8), { result: HitResult.INDIRECT }); - globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:badDreams", { pokemonName: getPokemonNameWithAffix(opp) })); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:badDreams", { pokemonName: getPokemonNameWithAffix(opp) }), + ); } } } } } - /** * Grabs the last failed Pokeball used * @extends PostTurnAbAttr * @see {@linkcode applyPostTurn} */ export class FetchBallAbAttr extends PostTurnAbAttr { - constructor() { - super(); - } - - override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostTurn(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): boolean { return !simulated && !isNullOrUndefined(globalScene.currentBattle.lastUsedPokeball) && !!pokemon.isPlayer; } /** * Adds the last used Pokeball back into the player's inventory * @param pokemon {@linkcode Pokemon} with this ability - * @param passive N/A - * @param args N/A + * @param _passive N/A + * @param _args N/A */ - override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { const lastUsed = globalScene.currentBattle.lastUsedPokeball; globalScene.pokeballCounts[lastUsed!]++; globalScene.currentBattle.lastUsedPokeball = null; - globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:fetchBall", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokeballName: getPokeballName(lastUsed!) })); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:fetchBall", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + pokeballName: getPokeballName(lastUsed!), + }), + ); } } -export class PostBiomeChangeAbAttr extends AbAttr { } +export class PostBiomeChangeAbAttr extends AbAttr {} export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr { private weatherType: WeatherType; @@ -4351,11 +5742,17 @@ export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr { this.weatherType = weatherType; } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return ((globalScene.arena.weather?.isImmutable() ?? false) && globalScene.arena.canSetWeather(this.weatherType)); + override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + return (globalScene.arena.weather?.isImmutable() ?? false) && globalScene.arena.canSetWeather(this.weatherType); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _cancelled: BooleanHolder, + _args: any[], + ): void { if (!simulated) { globalScene.arena.trySetWeather(this.weatherType, pokemon); } @@ -4371,11 +5768,17 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { this.terrainType = terrainType; } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return globalScene.arena.canSetTerrain(this.terrainType); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _cancelled: BooleanHolder, + _args: any[], + ): void { if (!simulated) { globalScene.arena.trySetTerrain(this.terrainType, false, pokemon); } @@ -4388,22 +5791,23 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { */ export class PostMoveUsedAbAttr extends AbAttr { canApplyPostMoveUsed( - pokemon: Pokemon, - move: PokemonMove, - source: Pokemon, - targets: BattlerIndex[], - simulated: boolean, - args: any[]): boolean { + _pokemon: Pokemon, + _move: PokemonMove, + _source: Pokemon, + _targets: BattlerIndex[], + _simulated: boolean, + _args: any[], + ): boolean { return true; } applyPostMoveUsed( - pokemon: Pokemon, - move: PokemonMove, - source: Pokemon, - targets: BattlerIndex[], - simulated: boolean, - args: any[], + _pokemon: Pokemon, + _move: PokemonMove, + _source: Pokemon, + _targets: BattlerIndex[], + _simulated: boolean, + _args: any[], ): void {} } @@ -4412,13 +5816,26 @@ export class PostMoveUsedAbAttr extends AbAttr { * @extends PostMoveUsedAbAttr */ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { - override canApplyPostMoveUsed(dancer: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], simulated: boolean, args: any[]): boolean { + override canApplyPostMoveUsed( + dancer: Pokemon, + _move: PokemonMove, + source: Pokemon, + _targets: BattlerIndex[], + _simulated: boolean, + _args: any[], + ): boolean { // List of tags that prevent the Dancer from replicating the move - const forbiddenTags = [ BattlerTagType.FLYING, BattlerTagType.UNDERWATER, - BattlerTagType.UNDERGROUND, BattlerTagType.HIDDEN ]; + const forbiddenTags = [ + BattlerTagType.FLYING, + BattlerTagType.UNDERWATER, + BattlerTagType.UNDERGROUND, + BattlerTagType.HIDDEN, + ]; // The move to replicate cannot come from the Dancer - return source.getBattlerIndex() !== dancer.getBattlerIndex() - && !dancer.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType)); + return ( + source.getBattlerIndex() !== dancer.getBattlerIndex() && + !dancer.summonData.tags.some(tag => forbiddenTags.includes(tag.tagType)) + ); } /** @@ -4428,7 +5845,7 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { * @param move {@linkcode PokemonMove} Dancing move used by the source * @param source {@linkcode Pokemon} that used the dancing move * @param targets {@linkcode BattlerIndex}Targets of the dancing move - * @param args N/A + * @param _args N/A */ override applyPostMoveUsed( dancer: Pokemon, @@ -4436,7 +5853,8 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { source: Pokemon, targets: BattlerIndex[], simulated: boolean, - args: any[]): void { + _args: any[], + ): void { if (!simulated) { dancer.turnData.extraTurns++; // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance @@ -4445,7 +5863,7 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { globalScene.phaseManager.unshiftPhase(new MovePhase(dancer, target, move, true, true)); } else if (move.getMove() instanceof SelfStatusMove) { // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself - globalScene.phaseManager.unshiftPhase(new MovePhase(dancer, [ dancer.getBattlerIndex() ], move, true, true)); + globalScene.phaseManager.unshiftPhase(new MovePhase(dancer, [dancer.getBattlerIndex()], move, true, true)); } } } @@ -4457,11 +5875,11 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { * @param source {@linkcode Pokemon} Source of the dancing move * @param targets {@linkcode BattlerIndex} Targets of the dancing move */ - getTarget(dancer: Pokemon, source: Pokemon, targets: BattlerIndex[]) : BattlerIndex[] { + getTarget(dancer: Pokemon, source: Pokemon, targets: BattlerIndex[]): BattlerIndex[] { if (dancer.isPlayer()) { - return source.isPlayer() ? targets : [ source.getBattlerIndex() ]; + return source.isPlayer() ? targets : [source.getBattlerIndex()]; } - return source.isPlayer() ? [ source.getBattlerIndex() ] : targets; + return source.isPlayer() ? [source.getBattlerIndex()] : targets; } } @@ -4470,11 +5888,11 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { * @extends AbAttr */ export class PostItemLostAbAttr extends AbAttr { - canApplyPostItemLost(pokemon: Pokemon, simulated: boolean, args: any[]): boolean { + canApplyPostItemLost(_pokemon: Pokemon, _simulated: boolean, _args: any[]): boolean { return true; } - applyPostItemLost(pokemon: Pokemon, simulated: boolean, args: any[]): void {} + applyPostItemLost(_pokemon: Pokemon, _simulated: boolean, _args: any[]): void {} } /** @@ -4488,16 +5906,16 @@ export class PostItemLostApplyBattlerTagAbAttr extends PostItemLostAbAttr { this.tagType = tagType; } - override canApplyPostItemLost(pokemon: Pokemon, simulated: boolean, args: any[]): boolean { + override canApplyPostItemLost(pokemon: Pokemon, simulated: boolean, _args: any[]): boolean { return !pokemon.getTag(this.tagType) && !simulated; } /** * Adds the last used Pokeball back into the player's inventory * @param pokemon {@linkcode Pokemon} with this ability - * @param args N/A + * @param _args N/A */ - override applyPostItemLost(pokemon: Pokemon, simulated: boolean, args: any[]): void { + override applyPostItemLost(pokemon: Pokemon, _simulated: boolean, _args: any[]): void { pokemon.addTag(this.tagType); } } @@ -4511,7 +5929,13 @@ export class StatStageChangeMultiplierAbAttr extends AbAttr { this.multiplier = multiplier; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[0] as NumberHolder).value *= this.multiplier; } } @@ -4519,13 +5943,23 @@ export class StatStageChangeMultiplierAbAttr extends AbAttr { export class StatStageChangeCopyAbAttr extends AbAttr { override apply( pokemon: Pokemon, - passive: boolean, + _passive: boolean, simulated: boolean, - cancelled: BooleanHolder, + _cancelled: BooleanHolder, args: any[], ): void { if (!simulated) { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, (args[0] as BattleStat[]), (args[1] as number), true, false, false)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase( + pokemon.getBattlerIndex(), + true, + args[0] as BattleStat[], + args[1] as number, + true, + false, + false, + ), + ); } } } @@ -4535,7 +5969,13 @@ export class BypassBurnDamageReductionAbAttr extends AbAttr { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } } @@ -4543,7 +5983,7 @@ export class BypassBurnDamageReductionAbAttr extends AbAttr { /** * Causes Pokemon to take reduced damage from the {@linkcode StatusEffect.BURN | Burn} status * @param multiplier Multiplied with the damage taken -*/ + */ export class ReduceBurnDamageAbAttr extends AbAttr { constructor(protected multiplier: number) { super(false); @@ -4551,18 +5991,30 @@ export class ReduceBurnDamageAbAttr extends AbAttr { /** * Applies the damage reduction - * @param pokemon N/A - * @param passive N/A - * @param cancelled N/A + * @param _pokemon N/A + * @param _passive N/A + * @param _cancelled N/A * @param args `[0]` {@linkcode NumberHolder} The damage value being modified */ - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[0] as NumberHolder).value = toDmgValue((args[0] as NumberHolder).value * this.multiplier); } } export class DoubleBerryEffectAbAttr extends AbAttr { - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[0] as NumberHolder).value *= 2; } } @@ -4600,7 +6052,7 @@ export class HealFromBerryUseAbAttr extends AbAttr { this.healPercent = Math.max(Math.min(healPercent, 1), 0); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, ...args: [BooleanHolder, any[]]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, ..._args: [BooleanHolder, any[]]): void { if (simulated) { return; } @@ -4610,15 +6062,24 @@ export class HealFromBerryUseAbAttr extends AbAttr { new PokemonHealPhase( pokemon.getBattlerIndex(), toDmgValue(pokemon.getMaxHp() * this.healPercent), - i18next.t("abilityTriggers:healFromBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), - true - ) - ); + i18next.t("abilityTriggers:healFromBerryUse", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + }), + true, + ), + ); } } export class RunSuccessAbAttr extends AbAttr { - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[0] as NumberHolder).value = 256; } } @@ -4640,23 +6101,23 @@ export class CheckTrappedAbAttr extends AbAttr { } canApplyCheckTrapped( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - trapped: BooleanHolder, - otherPokemon: Pokemon, - args: any[], + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _trapped: BooleanHolder, + _otherPokemon: Pokemon, + _args: any[], ): boolean { return true; } applyCheckTrapped( - pokemon: Pokemon, - passive: boolean, - simulated: boolean, - trapped: BooleanHolder, - otherPokemon: Pokemon, - args: any[], + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _trapped: BooleanHolder, + _otherPokemon: Pokemon, + _args: any[], ): void {} } @@ -4667,10 +6128,23 @@ export class CheckTrappedAbAttr extends AbAttr { * @see {@linkcode applyCheckTrapped} */ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { - override canApplyCheckTrapped(pokemon: Pokemon, passive: boolean, simulated: boolean, trapped: BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean { - return this.arenaTrapCondition(pokemon, otherPokemon) - && !(otherPokemon.getTypes(true).includes(PokemonType.GHOST) || (otherPokemon.getTypes(true).includes(PokemonType.STELLAR) && otherPokemon.getTypes().includes(PokemonType.GHOST))) - && !otherPokemon.hasAbility(AbilityId.RUN_AWAY); + override canApplyCheckTrapped( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _trapped: BooleanHolder, + otherPokemon: Pokemon, + _args: any[], + ): boolean { + return ( + this.arenaTrapCondition(pokemon, otherPokemon) && + !( + otherPokemon.getTypes(true).includes(PokemonType.GHOST) || + (otherPokemon.getTypes(true).includes(PokemonType.STELLAR) && + otherPokemon.getTypes().includes(PokemonType.GHOST)) + ) && + !otherPokemon.hasAbility(AbilityId.RUN_AWAY) + ); } /** @@ -4679,18 +6153,28 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { * If the enemy has the ability Run Away, it is not trapped. * If the user has Magnet Pull and the enemy is not a Steel type, it is not trapped. * If the user has Arena Trap and the enemy is not grounded, it is not trapped. - * @param pokemon The {@link Pokemon} with this {@link AbAttr} - * @param passive N/A + * @param _pokemon The {@link Pokemon} with this {@link AbAttr} + * @param _passive N/A * @param trapped {@link BooleanHolder} indicating whether the other Pokemon is trapped or not - * @param otherPokemon The {@link Pokemon} that is affected by an Arena Trap ability - * @param args N/A + * @param _otherPokemon The {@link Pokemon} that is affected by an Arena Trap ability + * @param _args N/A */ - override applyCheckTrapped(pokemon: Pokemon, passive: boolean, simulated: boolean, trapped: BooleanHolder, otherPokemon: Pokemon, args: any[]): void { + override applyCheckTrapped( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + trapped: BooleanHolder, + _otherPokemon: Pokemon, + _args: any[], + ): void { trapped.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { - return i18next.t("abilityTriggers:arenaTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }); + getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + return i18next.t("abilityTriggers:arenaTrap", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + }); } } @@ -4699,27 +6183,33 @@ export class MaxMultiHitAbAttr extends AbAttr { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[0] as NumberHolder).value = 0; } } export class PostBattleAbAttr extends AbAttr { - constructor(showAbility: boolean = true) { + constructor(showAbility = true) { super(showAbility); } - canApplyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + canApplyPostBattle(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return true; } - applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} + applyPostBattle(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void {} } export class PostBattleLootAbAttr extends PostBattleAbAttr { private randItem?: PokemonHeldItemModifier; - override canApplyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostBattle(pokemon: Pokemon, _passive: boolean, simulated: boolean, args: any[]): boolean { const postBattleLoot = globalScene.currentBattle.postBattleLoot; if (!simulated && postBattleLoot.length && args[0]) { this.randItem = randSeedItem(postBattleLoot); @@ -4729,9 +6219,9 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr { } /** - * @param args - `[0]`: boolean for if the battle ended in a victory + * @param _args - `[0]`: boolean for if the battle ended in a victory */ - override applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostBattle(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { const postBattleLoot = globalScene.currentBattle.postBattleLoot; if (!this.randItem) { this.randItem = randSeedItem(postBattleLoot); @@ -4739,18 +6229,39 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr { if (globalScene.tryTransferHeldItemModifier(this.randItem, pokemon, true, 1, true, undefined, false)) { postBattleLoot.splice(postBattleLoot.indexOf(this.randItem), 1); - globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:postBattleLoot", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), itemName: this.randItem.type.name })); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:postBattleLoot", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + itemName: this.randItem.type.name, + }), + ); } this.randItem = undefined; } } export class PostFaintAbAttr extends AbAttr { - canApplyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { + canApplyPostFaint( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker?: Pokemon, + _move?: Move, + _hitResult?: HitResult, + ..._args: any[] + ): boolean { return true; } - applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): void {} + applyPostFaint( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker?: Pokemon, + _move?: Move, + _hitResult?: HitResult, + ..._args: any[] + ): void {} } /** @@ -4759,21 +6270,37 @@ export class PostFaintAbAttr extends AbAttr { * @extends PostFaintAbAttr */ export class PostFaintUnsuppressedWeatherFormChangeAbAttr extends PostFaintAbAttr { - override canApplyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { + override canApplyPostFaint( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker?: Pokemon, + _move?: Move, + _hitResult?: HitResult, + ..._args: any[] + ): boolean { return getPokemonWithWeatherBasedForms().length > 0; } /** * Triggers {@linkcode Arena.triggerWeatherBasedFormChanges | triggerWeatherBasedFormChanges} * when the user of the ability faints - * @param {Pokemon} pokemon the fainted Pokemon - * @param passive n/a - * @param attacker n/a - * @param move n/a - * @param hitResult n/a - * @param args n/a + * @param {Pokemon} _pokemon the fainted Pokemon + * @param _passive n/a + * @param _attacker n/a + * @param _move n/a + * @param _hitResult n/a + * @param _args n/a */ - override applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { + override applyPostFaint( + _pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { if (!simulated) { globalScene.arena.triggerWeatherBasedFormChanges(); } @@ -4789,22 +6316,46 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { this.damageRatio = damageRatio; } - override canApplyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { - const diedToDirectDamage = move !== undefined && attacker !== undefined && move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}); + override canApplyPostFaint( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker?: Pokemon, + move?: Move, + _hitResult?: HitResult, + ..._args: any[] + ): boolean { + const diedToDirectDamage = + move !== undefined && + attacker !== undefined && + move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon }); const cancelled = new BooleanHolder(false); globalScene.getField(true).map(p => applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled, simulated)); return !(!diedToDirectDamage || cancelled.value || attacker!.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)); } - override applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): void { + override applyPostFaint( + _pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker?: Pokemon, + _move?: Move, + _hitResult?: HitResult, + ..._args: any[] + ): void { if (!simulated) { - attacker!.damageAndUpdate(toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)), { result: HitResult.INDIRECT }); + attacker!.damageAndUpdate(toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)), { + result: HitResult.INDIRECT, + }); attacker!.turnData.damageTaken += toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)); } } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { - return i18next.t("abilityTriggers:postFaintContactDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }); + getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + return i18next.t("abilityTriggers:postFaintContactDamage", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + }); } } @@ -4812,20 +6363,28 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { * Attribute used for abilities (Innards Out) that damage the opponent based on how much HP the last attack used to knock out the owner of the ability. */ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr { - constructor() { - super (); - } - - override applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): void { - if (move !== undefined && attacker !== undefined && !simulated) { //If the mon didn't die to indirect damage + override applyPostFaint( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + attacker?: Pokemon, + move?: Move, + _hitResult?: HitResult, + ..._args: any[] + ): void { + //If the mon didn't die to indirect damage + if (move !== undefined && attacker !== undefined && !simulated) { const damage = pokemon.turnData.attacksReceived[0].damage; - attacker.damageAndUpdate((damage), { result: HitResult.INDIRECT }); + attacker.damageAndUpdate(damage, { result: HitResult.INDIRECT }); attacker.turnData.damageTaken += damage; } } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { - return i18next.t("abilityTriggers:postFaintHpDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }); + getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { + return i18next.t("abilityTriggers:postFaintHpDamage", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + }); } } @@ -4841,7 +6400,7 @@ export class RedirectMoveAbAttr extends AbAttr { * - `[2]` - The Pokemon that used the move being redirected */ - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { if (!this.canRedirect(args[0] as MoveId, args[2] as Pokemon)) { return false; } @@ -4850,15 +6409,21 @@ export class RedirectMoveAbAttr extends AbAttr { return target.value !== newTarget; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { const target = args[1] as NumberHolder; const newTarget = pokemon.getBattlerIndex(); target.value = newTarget; } - canRedirect(moveId: MoveId, user: Pokemon): boolean { + canRedirect(moveId: MoveId, _user: Pokemon): boolean { const move = allMoves[moveId]; - return !![ MoveTarget.NEAR_OTHER, MoveTarget.OTHER ].find(t => move.moveTarget === t); + return !![MoveTarget.NEAR_OTHER, MoveTarget.OTHER].find(t => move.moveTarget === t); } } @@ -4875,7 +6440,7 @@ export class RedirectTypeMoveAbAttr extends RedirectMoveAbAttr { } } -export class BlockRedirectAbAttr extends AbAttr { } +export class BlockRedirectAbAttr extends AbAttr {} /** * Used by Early Bird, makes the pokemon wake up faster @@ -4891,7 +6456,7 @@ export class ReduceStatusEffectDurationAbAttr extends AbAttr { this.statusEffect = statusEffect; } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { return args[1] instanceof NumberHolder && args[0] === this.statusEffect; } @@ -4901,7 +6466,13 @@ export class ReduceStatusEffectDurationAbAttr extends AbAttr { * - `[0]` - The {@linkcode StatusEffect} of the Pokemon * - `[1]` - The number of turns remaining until the status is healed */ - override apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { args[1].value -= 1; } } @@ -4919,23 +6490,35 @@ export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr { constructor(stats: BattleStat[], stages: number) { super(); - this.stats = Array.isArray(stats) - ? stats - : [ stats ]; + this.stats = Array.isArray(stats) ? stats : [stats]; this.stages = stages; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _cancelled: BooleanHolder, + _args: any[], + ): void { if (!simulated) { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages)); + globalScene.phaseManager.unshiftPhase( + new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages), + ); } } } -export class IncreasePpAbAttr extends AbAttr { } +export class IncreasePpAbAttr extends AbAttr {} export class ForceSwitchOutImmunityAbAttr extends AbAttr { - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } } @@ -4945,12 +6528,18 @@ export class ReduceBerryUseThresholdAbAttr extends AbAttr { super(false); } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { const hpRatio = pokemon.getHpRatio(); return args[0].value < hpRatio; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { args[0].value *= 2; } } @@ -4968,7 +6557,13 @@ export class WeightMultiplierAbAttr extends AbAttr { this.multiplier = multiplier; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[0] as NumberHolder).value *= this.multiplier; } } @@ -4978,7 +6573,13 @@ export class SyncEncounterNatureAbAttr extends AbAttr { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { (args[0] as Pokemon).setNature(pokemon.getNature()); } } @@ -4989,22 +6590,28 @@ export class MoveAbilityBypassAbAttr extends AbAttr { constructor(moveIgnoreFunc?: (pokemon: Pokemon, move: Move) => boolean) { super(false); - this.moveIgnoreFunc = moveIgnoreFunc || ((pokemon, move) => true); + this.moveIgnoreFunc = moveIgnoreFunc || ((_pokemon, _move) => true); } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return this.moveIgnoreFunc(pokemon, (args[0] as Move)); + override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { + return this.moveIgnoreFunc(pokemon, args[0] as Move); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } } -export class AlwaysHitAbAttr extends AbAttr { } +export class AlwaysHitAbAttr extends AbAttr {} /** Attribute for abilities that allow moves that make contact to ignore protection (i.e. Unseen Fist) */ -export class IgnoreProtectOnContactAbAttr extends AbAttr { } +export class IgnoreProtectOnContactAbAttr extends AbAttr {} /** * Attribute implementing the effects of {@link https://bulbapedia.bulbagarden.net/wiki/Infiltrator_(Ability) | Infiltrator}. @@ -5015,19 +6622,19 @@ export class InfiltratorAbAttr extends AbAttr { super(false); } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { return args[0] instanceof BooleanHolder; } /** * Sets a flag to bypass screens, Substitute, Safeguard, and Mist - * @param pokemon n/a - * @param passive n/a - * @param simulated n/a - * @param cancelled n/a + * @param _pokemon n/a + * @param _passive n/a + * @param _simulated n/a + * @param _cancelled n/a * @param args `[0]` a {@linkcode BooleanHolder | BooleanHolder} containing the flag */ - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: null, args: any[]): void { + override apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: null, args: any[]): void { const bypassed = args[0]; bypassed.value = true; } @@ -5038,7 +6645,7 @@ export class InfiltratorAbAttr extends AbAttr { * Allows the source to bounce back {@linkcode MoveFlags.REFLECTABLE | Reflectable} * moves as if the user had used {@linkcode MoveId.MAGIC_COAT | Magic Coat}. */ -export class ReflectStatusMoveAbAttr extends AbAttr { } +export class ReflectStatusMoveAbAttr extends AbAttr {} export class NoTransformAbilityAbAttr extends AbAttr { constructor() { @@ -5062,11 +6669,17 @@ export class IgnoreTypeImmunityAbAttr extends AbAttr { this.allowedMoveTypes = allowedMoveTypes; } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { return this.defenderType === (args[1] as PokemonType) && this.allowedMoveTypes.includes(args[0] as PokemonType); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } } @@ -5085,11 +6698,17 @@ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { this.defenderType = defenderType; } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, args: any[]): boolean { return this.statusEffect.includes(args[0] as StatusEffect) && this.defenderType.includes(args[1] as PokemonType); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + cancelled: BooleanHolder, + _args: any[], + ): void { cancelled.value = true; } } @@ -5101,20 +6720,16 @@ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { * @see {@linkcode applyPostBattle} */ export class MoneyAbAttr extends PostBattleAbAttr { - constructor() { - super(); - } - - override canApplyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApplyPostBattle(_pokemon: Pokemon, _passive: boolean, simulated: boolean, args: any[]): boolean { return !simulated && args[0]; } /** - * @param pokemon {@linkcode Pokemon} that is the user of this ability. - * @param passive N/A - * @param args - `[0]`: boolean for if the battle ended in a victory + * @param _pokemon {@linkcode Pokemon} that is the user of this ability. + * @param _passive N/A + * @param _args - `[0]`: boolean for if the battle ended in a victory */ - override applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostBattle(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { globalScene.currentBattle.moneyScattered += globalScene.getWaveMoneyAmount(0.2); } } @@ -5139,14 +6754,16 @@ export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageC * @param {ArenaTagType} tagType - The type of arena tag to check for. */ constructor(tagType: ArenaTagType) { - super([ Stat.ATK ], 1, true, false); + super([Stat.ATK], 1, true, false); this.tagType = tagType; } override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const side = pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; - return (globalScene.arena.getTagOnSide(this.tagType, side) ?? false) - && super.canApplyPostSummon(pokemon, passive, simulated, args); + return ( + (globalScene.arena.getTagOnSide(this.tagType, side) ?? false) && + super.canApplyPostSummon(pokemon, passive, simulated, args) + ); } /** @@ -5172,10 +6789,16 @@ export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageC export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { private multiplier: number; private tagType: BattlerTagType; - private recoilDamageFunc?: ((pokemon: Pokemon) => number); + private recoilDamageFunc?: (pokemon: Pokemon) => number; private triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string; - constructor(condition: PokemonDefendCondition, multiplier: number, tagType: BattlerTagType, triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string, recoilDamageFunc?: (pokemon: Pokemon) => number) { + constructor( + condition: PokemonDefendCondition, + multiplier: number, + tagType: BattlerTagType, + triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string, + recoilDamageFunc?: (pokemon: Pokemon) => number, + ) { super(condition, multiplier); this.multiplier = multiplier; @@ -5184,7 +6807,15 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { this.triggerMessageFunc = triggerMessageFunc; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder | null, args: any[]): boolean { + override canApplyPreDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + attacker: Pokemon, + move: Move, + _cancelled: BooleanHolder | null, + _args: any[], + ): boolean { return this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon); } @@ -5194,17 +6825,29 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { * * @param pokemon The Pokémon with the ability. * @param _passive n/a - * @param attacker The attacking Pokémon. - * @param move The move being used. + * @param _attacker The attacking Pokémon. + * @param _move The move being used. * @param _cancelled n/a * @param args Additional arguments. */ - override applyPreDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _cancelled: BooleanHolder, args: any[]): void { + override applyPreDefend( + pokemon: Pokemon, + _passive: boolean, + simulated: boolean, + _attacker: Pokemon, + _move: Move, + _cancelled: BooleanHolder, + args: any[], + ): void { if (!simulated) { (args[0] as NumberHolder).value = this.multiplier; pokemon.removeTag(this.tagType); if (this.recoilDamageFunc) { - pokemon.damageAndUpdate(this.recoilDamageFunc(pokemon), { result: HitResult.INDIRECT, ignoreSegments: true, ignoreFaintPhase: true }); + pokemon.damageAndUpdate(this.recoilDamageFunc(pokemon), { + result: HitResult.INDIRECT, + ignoreSegments: true, + ignoreFaintPhase: true, + }); } } } @@ -5227,9 +6870,9 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { * @see {@linkcode applyPreSummon()} */ export class PreSummonAbAttr extends AbAttr { - applyPreSummon(pokemon: Pokemon, passive: boolean, args: any[]): void {} + applyPreSummon(_pokemon: Pokemon, _passive: boolean, _args: any[]): void {} - canApplyPreSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + canApplyPreSummon(_pokemon: Pokemon, _passive: boolean, _args: any[]): boolean { return true; } } @@ -5239,27 +6882,32 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { * Apply a new illusion when summoning Zoroark if the illusion is available * * @param pokemon - The Pokémon with the Illusion ability. - * @param passive - N/A - * @param args - N/A + * @param _passive - N/A + * @param _args - N/A * @returns Whether the illusion was applied. */ - override applyPreSummon(pokemon: Pokemon, passive: boolean, args: any[]): void { - const party: Pokemon[] = (pokemon.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).filter(p => p.isAllowedInBattle()); - const lastPokemon: Pokemon = party.filter(p => p !==pokemon).at(-1) || pokemon; + override applyPreSummon(pokemon: Pokemon, _passive: boolean, _args: any[]): void { + const party: Pokemon[] = (pokemon.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).filter( + p => p.isAllowedInBattle(), + ); + const lastPokemon: Pokemon = party.filter(p => p !== pokemon).at(-1) || pokemon; pokemon.setIllusion(lastPokemon); } - override canApplyPreSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + override canApplyPreSummon(pokemon: Pokemon, _passive: boolean, _args: any[]): boolean { if (pokemon.hasTrainer()) { - const party: Pokemon[] = (pokemon.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).filter(p => p.isAllowedInBattle()); - const lastPokemon: Pokemon = party.filter(p => p !==pokemon).at(-1) || pokemon; + const party: Pokemon[] = (pokemon.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).filter( + p => p.isAllowedInBattle(), + ); + const lastPokemon: Pokemon = party.filter(p => p !== pokemon).at(-1) || pokemon; const speciesId = lastPokemon.species.speciesId; // If the last conscious Pokémon in the party is a Terastallized Ogerpon or Terapagos, Illusion will not activate. // Illusion will also not activate if the Pokémon with Illusion is Terastallized and the last Pokémon in the party is Ogerpon or Terapagos. if ( lastPokemon === pokemon || - ((speciesId === SpeciesId.OGERPON || speciesId === SpeciesId.TERAPAGOS) && (lastPokemon.isTerastallized || pokemon.isTerastallized)) + ((speciesId === SpeciesId.OGERPON || speciesId === SpeciesId.TERAPAGOS) && + (lastPokemon.isTerastallized || pokemon.isTerastallized)) ) { return false; } @@ -5269,7 +6917,13 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { } export class IllusionBreakAbAttr extends AbAttr { - override apply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: BooleanHolder | null, _args: any[]): void { + override apply( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder | null, + _args: any[], + ): void { pokemon.breakIllusion(); pokemon.summonData.illusionBroken = true; } @@ -5280,21 +6934,42 @@ export class PostDefendIllusionBreakAbAttr extends PostDefendAbAttr { * Destroy the illusion upon taking damage * * @param pokemon - The Pokémon with the Illusion ability. - * @param passive - unused - * @param attacker - The attacking Pokémon. - * @param move - The move being used. - * @param hitResult - The type of hitResult the pokemon got - * @param args - unused + * @param _passive - unused + * @param _attacker - The attacking Pokémon. + * @param _move - The move being used. + * @param _hitResult - The type of hitResult the pokemon got + * @param _args - unused * @returns - Whether the illusion was destroyed. */ - override applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): void { + override applyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move, + _hitResult: HitResult, + _args: any[], + ): void { pokemon.breakIllusion(); pokemon.summonData.illusionBroken = true; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - const breakIllusion: HitResult[] = [ HitResult.EFFECTIVE, HitResult.SUPER_EFFECTIVE, HitResult.NOT_VERY_EFFECTIVE, HitResult.ONE_HIT_KO ]; - return breakIllusion.includes(hitResult) && !!pokemon.summonData.illusion + override canApplyPostDefend( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _attacker: Pokemon, + _move: Move, + hitResult: HitResult, + _args: any[], + ): boolean { + const breakIllusion: HitResult[] = [ + HitResult.EFFECTIVE, + HitResult.SUPER_EFFECTIVE, + HitResult.NOT_VERY_EFFECTIVE, + HitResult.ONE_HIT_KO, + ]; + return breakIllusion.includes(hitResult) && !!pokemon.summonData.illusion; } } @@ -5303,16 +6978,15 @@ export class IllusionPostBattleAbAttr extends PostBattleAbAttr { * Break the illusion once the battle ends * * @param pokemon - The Pokémon with the Illusion ability. - * @param passive - Unused - * @param args - Unused + * @param _passive - Unused + * @param _args - Unused * @returns - Whether the illusion was applied. */ - override applyPostBattle(pokemon: Pokemon, passive: boolean, simulated:boolean, args: any[]): void { - pokemon.breakIllusion() + override applyPostBattle(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { + pokemon.breakIllusion(); } } - /** * If a Pokémon with this Ability selects a damaging move, it has a 30% chance of going first in its priority bracket. If the Ability activates, this is announced at the start of the turn (after move selection). * @@ -5329,28 +7003,36 @@ export class BypassSpeedChanceAbAttr extends AbAttr { this.chance = chance; } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(pokemon: Pokemon, _passive: boolean, simulated: boolean, args: any[]): boolean { const bypassSpeed = args[0] as BooleanHolder; const turnCommand = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; const isCommandFight = turnCommand?.command === Command.FIGHT; const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL; - return !simulated && !bypassSpeed.value && pokemon.randBattleSeedInt(100) < this.chance && isCommandFight && isDamageMove; + return ( + !simulated && !bypassSpeed.value && pokemon.randBattleSeedInt(100) < this.chance && isCommandFight && isDamageMove + ); } /** * bypass move order in their priority bracket when pokemon choose damaging move - * @param {Pokemon} pokemon {@linkcode Pokemon} the Pokemon applying this ability - * @param {boolean} passive N/A - * @param {BooleanHolder} cancelled N/A + * @param {Pokemon} _pokemon {@linkcode Pokemon} the Pokemon applying this ability + * @param {boolean} _passive N/A + * @param {BooleanHolder} _cancelled N/A * @param {any[]} args [0] {@linkcode BooleanHolder} set to true when the ability activated */ - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { const bypassSpeed = args[0] as BooleanHolder; bypassSpeed.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:quickDraw", { pokemonName: getPokemonNameWithAffix(pokemon) }); } } @@ -5358,9 +7040,9 @@ export class BypassSpeedChanceAbAttr extends AbAttr { /** * This attribute checks if a Pokemon's move meets a provided condition to determine if the Pokemon can use Quick Claw * It was created because Pokemon with the ability Mycelium Might cannot access Quick Claw's benefits when using status moves. -*/ + */ export class PreventBypassSpeedChanceAbAttr extends AbAttr { - private condition: ((pokemon: Pokemon, move: Move) => boolean); + private condition: (pokemon: Pokemon, move: Move) => boolean; /** * @param {function} condition - checks if a move meets certain conditions @@ -5370,7 +7052,7 @@ export class PreventBypassSpeedChanceAbAttr extends AbAttr { this.condition = condition; } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { const turnCommand = globalScene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; const isCommandFight = turnCommand?.command === Command.FIGHT; const move = turnCommand?.move?.move ? allMoves[turnCommand.move.move] : null; @@ -5381,7 +7063,13 @@ export class PreventBypassSpeedChanceAbAttr extends AbAttr { * @argument {boolean} bypassSpeed - determines if a Pokemon is able to bypass speed at the moment * @argument {boolean} canCheckHeldItems - determines if a Pokemon has access to Quick Claw's effects or not */ - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + override apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + args: any[], + ): void { const bypassSpeed = args[0] as BooleanHolder; const canCheckHeldItems = args[1] as BooleanHolder; bypassSpeed.value = false; @@ -5398,11 +7086,17 @@ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr { super(true); } - override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + override canApply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { return !pokemon.isTerastallized; } - override apply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: BooleanHolder, _args: any[]): void { + override apply( + pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: BooleanHolder, + _args: any[], + ): void { const currentTerrain = globalScene.arena.getTerrainType(); const typeChange: PokemonType[] = this.determineTypeChange(pokemon, currentTerrain); if (typeChange.length !== 0) { @@ -5445,26 +7139,24 @@ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr { } override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return globalScene.arena.getTerrainType() !== TerrainType.NONE && - this.canApply(pokemon, passive, simulated, args); + return globalScene.arena.getTerrainType() !== TerrainType.NONE && this.canApply(pokemon, passive, simulated, args); } /** * Checks if the Pokemon should change types if summoned into an active terrain */ - override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, _args: any[]): void { this.apply(pokemon, passive, simulated, new BooleanHolder(false), []); } - override getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]) { + override getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]) { const currentTerrain = globalScene.arena.getTerrainType(); const pokemonNameWithAffix = getPokemonNameWithAffix(pokemon); if (currentTerrain === TerrainType.NONE) { return i18next.t("abilityTriggers:pokemonTypeChangeRevert", { pokemonNameWithAffix }); - } else { - const moveType = i18next.t(`pokemonInfo:Type.${PokemonType[this.determineTypeChange(pokemon, currentTerrain)[0]]}`); - return i18next.t("abilityTriggers:pokemonTypeChange", { pokemonNameWithAffix, moveType }); } + const moveType = i18next.t(`pokemonInfo:Type.${PokemonType[this.determineTypeChange(pokemon, currentTerrain)[0]]}`); + return i18next.t("abilityTriggers:pokemonTypeChange", { pokemonNameWithAffix, moveType }); } } @@ -5475,23 +7167,26 @@ function applySingleAbAttrs( applyFunc: AbAttrApplyFunc, successFunc: AbAttrSuccessFunc, args: any[], - gainedMidTurn: boolean = false, - simulated: boolean = false, - messages: string[] = [] + gainedMidTurn = false, + simulated = false, + messages: string[] = [], ) { - if (!pokemon?.canApplyAbility(passive) || (passive && (pokemon.getPassiveAbility().id === pokemon.getAbility().id))) { + if (!pokemon?.canApplyAbility(passive) || (passive && pokemon.getPassiveAbility().id === pokemon.getAbility().id)) { return; } const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); - if (gainedMidTurn && ability.getAttrs(attrType).some(attr => attr instanceof PostSummonAbAttr && !attr.shouldActivateOnGain())) { + if ( + gainedMidTurn && + ability.getAttrs(attrType).some(attr => attr instanceof PostSummonAbAttr && !attr.shouldActivateOnGain()) + ) { return; } for (const attr of ability.getAttrs(attrType)) { const condition = attr.getCondition(); let abShown = false; - if (condition && !condition(pokemon) || !successFunc(attr, passive)) { + if ((condition && !condition(pokemon)) || !successFunc(attr, passive)) { continue; } @@ -5540,33 +7235,41 @@ class ForceSwitchOutHelper { * - If the Pokémon is still alive (hp > 0), and if so, it leaves the field and a new SwitchPhase is initiated. */ if (switchOutTarget.isPlayer()) { - if (globalScene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) { + if (globalScene.getPlayerParty().filter(p => p.isAllowedInBattle() && !p.isOnField()).length < 1) { return false; } if (switchOutTarget.hp > 0) { switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); - globalScene.phaseManager.prependToPhase(new SwitchPhase(this.switchType, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase); + globalScene.phaseManager.prependToPhase( + new SwitchPhase(this.switchType, switchOutTarget.getFieldIndex(), true, true), + MoveEndPhase, + ); return true; } - /** - * For non-wild battles, it checks if the opposing party has any available Pokémon to switch in. - * If yes, the Pokémon leaves the field and a new SwitchSummonPhase is initiated. - */ + /** + * For non-wild battles, it checks if the opposing party has any available Pokémon to switch in. + * If yes, the Pokémon leaves the field and a new SwitchSummonPhase is initiated. + */ } else if (globalScene.currentBattle.battleType !== BattleType.WILD) { - if (globalScene.getEnemyParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) { + if (globalScene.getEnemyParty().filter(p => p.isAllowedInBattle() && !p.isOnField()).length < 1) { return false; } if (switchOutTarget.hp > 0) { switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); - const summonIndex = (globalScene.currentBattle.trainer ? globalScene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0); - globalScene.phaseManager.prependToPhase(new SwitchSummonPhase(this.switchType, switchOutTarget.getFieldIndex(), summonIndex, false, false), MoveEndPhase); + const summonIndex = globalScene.currentBattle.trainer + ? globalScene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) + : 0; + globalScene.phaseManager.prependToPhase( + new SwitchSummonPhase(this.switchType, switchOutTarget.getFieldIndex(), summonIndex, false, false), + MoveEndPhase, + ); return true; } - /** - * For wild Pokémon battles, the Pokémon will flee if the conditions are met (waveIndex and double battles). - * It will not flee if it is a Mystery Encounter with fleeing disabled (checked in `getSwitchOutCondition()`) or if it is a wave 10x wild boss - */ + /** + * For wild Pokémon battles, the Pokémon will flee if the conditions are met (waveIndex and double battles). + * It will not flee if it is a Mystery Encounter with fleeing disabled (checked in `getSwitchOutCondition()`) or if it is a wave 10x wild boss + */ } else { const allyPokemon = switchOutTarget.getAlly(); @@ -5576,7 +7279,12 @@ class ForceSwitchOutHelper { if (switchOutTarget.hp > 0) { switchOutTarget.leaveField(false); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), + null, + true, + 500, + ); if (globalScene.currentBattle.double && !isNullOrUndefined(allyPokemon)) { globalScene.redirectPokemonMoves(switchOutTarget, allyPokemon); } @@ -5622,14 +7330,24 @@ class ForceSwitchOutHelper { } } - if (!player && globalScene.currentBattle.isBattleMysteryEncounter() && !globalScene.currentBattle.mysteryEncounter?.fleeAllowed) { + if ( + !player && + globalScene.currentBattle.isBattleMysteryEncounter() && + !globalScene.currentBattle.mysteryEncounter?.fleeAllowed + ) { return false; } const party = player ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); - return (!player && globalScene.currentBattle.battleType === BattleType.WILD) - || party.filter(p => p.isAllowedInBattle() && !p.isOnField() - && (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > 0; + return ( + (!player && globalScene.currentBattle.battleType === BattleType.WILD) || + party.filter( + p => + p.isAllowedInBattle() && + !p.isOnField() && + (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot), + ).length > 0 + ); } /** @@ -5641,7 +7359,9 @@ class ForceSwitchOutHelper { public getFailedText(target: Pokemon): string | null { const blockedByAbility = new BooleanHolder(false); applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility); - return blockedByAbility.value ? i18next.t("moveTriggers:cannotBeSwitchedOut", { pokemonName: getPokemonNameWithAffix(target) }) : null; + return blockedByAbility.value + ? i18next.t("moveTriggers:cannotBeSwitchedOut", { pokemonName: getPokemonNameWithAffix(target) }) + : null; } } @@ -5669,22 +7389,23 @@ function calculateShellBellRecovery(pokemon: Pokemon): number { */ export class PostDamageAbAttr extends AbAttr { public canApplyPostDamage( - pokemon: Pokemon, - damage: number, - passive: boolean, - simulated: boolean, - args: any[], - source?: Pokemon): boolean { + _pokemon: Pokemon, + _damage: number, + _passive: boolean, + _simulated: boolean, + _args: any[], + _source?: Pokemon, + ): boolean { return true; } public applyPostDamage( - pokemon: Pokemon, - damage: number, - passive: boolean, - simulated: boolean, - args: any[], - source?: Pokemon, + _pokemon: Pokemon, + _damage: number, + _passive: boolean, + _simulated: boolean, + _args: any[], + _source?: Pokemon, ): void {} } @@ -5711,13 +7432,14 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr { public override canApplyPostDamage( pokemon: Pokemon, damage: number, - passive: boolean, - simulated: boolean, - args: any[], - source?: Pokemon): boolean { + _passive: boolean, + _simulated: boolean, + _args: any[], + source?: Pokemon, + ): boolean { const moveHistory = pokemon.getMoveHistory(); // Will not activate when the Pokémon's HP is lowered by cutting its own HP - const fordbiddenAttackingMoves = [ MoveId.BELLY_DRUM, MoveId.SUBSTITUTE, MoveId.CURSE, MoveId.PAIN_SPLIT ]; + const fordbiddenAttackingMoves = [MoveId.BELLY_DRUM, MoveId.SUBSTITUTE, MoveId.CURSE, MoveId.PAIN_SPLIT]; if (moveHistory.length > 0) { const lastMoveUsed = moveHistory[moveHistory.length - 1]; if (fordbiddenAttackingMoves.includes(lastMoveUsed.move)) { @@ -5726,20 +7448,25 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr { } // Dragon Tail and Circle Throw switch out Pokémon before the Ability activates. - const fordbiddenDefendingMoves = [ MoveId.DRAGON_TAIL, MoveId.CIRCLE_THROW ]; + const fordbiddenDefendingMoves = [MoveId.DRAGON_TAIL, MoveId.CIRCLE_THROW]; if (source) { const enemyMoveHistory = source.getMoveHistory(); if (enemyMoveHistory.length > 0) { const enemyLastMoveUsed = enemyMoveHistory[enemyMoveHistory.length - 1]; // Will not activate if the Pokémon's HP falls below half while it is in the air during Sky Drop. - if (fordbiddenDefendingMoves.includes(enemyLastMoveUsed.move) || enemyLastMoveUsed.move === MoveId.SKY_DROP && enemyLastMoveUsed.result === MoveResult.OTHER) { + if ( + fordbiddenDefendingMoves.includes(enemyLastMoveUsed.move) || + (enemyLastMoveUsed.move === MoveId.SKY_DROP && enemyLastMoveUsed.result === MoveResult.OTHER) + ) { return false; - // Will not activate if the Pokémon's HP falls below half by a move affected by Sheer Force. - // TODO: Make this use the sheer force disable condition - } else if (allMoves[enemyLastMoveUsed.move].chance >= 0 && source.hasAbility(AbilityId.SHEER_FORCE)) { + // Will not activate if the Pokémon's HP falls below half by a move affected by Sheer Force. + // TODO: Make this use the sheer force disable condition + } + if (allMoves[enemyLastMoveUsed.move].chance >= 0 && source.hasAbility(AbilityId.SHEER_FORCE)) { return false; + } // Activate only after the last hit of multistrike moves - } else if (source.turnData.hitsLeft > 1) { + if (source.turnData.hitsLeft > 1) { return false; } if (source.turnData.hitCount > 1) { @@ -5769,13 +7496,20 @@ export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr { * the Pokémon's health after damage to determine whether the switch-out should occur. * * @param pokemon The Pokémon that took damage. - * @param damage N/A - * @param passive N/A - * @param simulated Whether the ability is being simulated. - * @param args N/A - * @param source N/A + * @param _damage N/A + * @param _passive N/A + * @param _simulated Whether the ability is being simulated. + * @param _args N/A + * @param _source N/A */ - public override applyPostDamage(pokemon: Pokemon, damage: number, passive: boolean, simulated: boolean, args: any[], source?: Pokemon): void { + public override applyPostDamage( + pokemon: Pokemon, + _damage: number, + _passive: boolean, + _simulated: boolean, + _args: any[], + _source?: Pokemon, + ): void { this.helper.switchOutLogic(pokemon); } } @@ -5785,11 +7519,11 @@ function applyAbAttrsInternal( applyFunc: AbAttrApplyFunc, successFunc: AbAttrSuccessFunc, args: any[], - simulated: boolean = false, + simulated = false, messages: string[] = [], - gainedMidTurn = false + gainedMidTurn = false, ) { - for (const passive of [ false, true ]) { + for (const passive of [false, true]) { if (pokemon) { applySingleAbAttrs(pokemon, passive, attrType, applyFunc, successFunc, args, gainedMidTurn, simulated, messages); globalScene.phaseManager.clearPhaseQueueSplice(); @@ -5862,7 +7596,8 @@ export function applyPostDefendAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args), - (attr, passive) => attr.canApplyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args), args, + (attr, passive) => attr.canApplyPostDefend(pokemon, passive, simulated, attacker, move, hitResult, args), + args, simulated, ); } @@ -5879,8 +7614,8 @@ export function applyPostMoveUsedAbAttrs( applyAbAttrsInternal( attrType, pokemon, - (attr, passive) => attr.applyPostMoveUsed(pokemon, move, source, targets, simulated, args), - (attr, passive) => attr.canApplyPostMoveUsed(pokemon, move, source, targets, simulated, args), + (attr, _passive) => attr.applyPostMoveUsed(pokemon, move, source, targets, simulated, args), + (attr, _passive) => attr.canApplyPostMoveUsed(pokemon, move, source, targets, simulated, args), args, simulated, ); @@ -5913,14 +7648,23 @@ export function applyStatMultiplierAbAttrs( * @param ignoreAbility - Whether or not the ability should be ignored by the pokemon or its move. * @param args - unused */ -export function applyAllyStatMultiplierAbAttrs(attrType: Constructor, - pokemon: Pokemon, stat: BattleStat, statValue: NumberHolder, simulated: boolean = false, checkedPokemon: Pokemon, ignoreAbility: boolean, ...args: any[] +export function applyAllyStatMultiplierAbAttrs( + attrType: Constructor, + pokemon: Pokemon, + stat: BattleStat, + statValue: NumberHolder, + simulated = false, + checkedPokemon: Pokemon, + ignoreAbility: boolean, + ...args: any[] ): void { - return applyAbAttrsInternal( + applyAbAttrsInternal( attrType, pokemon, - (attr, passive) => attr.applyAllyStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, ignoreAbility, args), - (attr, passive) => attr.canApplyAllyStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, ignoreAbility, args), + (attr, passive) => + attr.applyAllyStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, ignoreAbility, args), + (attr, passive) => + attr.canApplyAllyStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, ignoreAbility, args), args, simulated, ); @@ -5948,7 +7692,7 @@ export function applyPostDamageAbAttrs( attrType: Constructor, pokemon: Pokemon, damage: number, - passive: boolean, + _passive: boolean, simulated = false, args: any[], source?: Pokemon, @@ -5985,8 +7729,11 @@ export function applyFieldStatMultiplierAbAttrs( applyAbAttrsInternal( attrType, pokemon, - (attr, passive) => attr.applyFieldStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, hasApplied, args), - (attr, passive) => attr.canApplyFieldStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, hasApplied, args), args, + (attr, passive) => + attr.applyFieldStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, hasApplied, args), + (attr, passive) => + attr.canApplyFieldStat(pokemon, passive, simulated, stat, statValue, checkedPokemon, hasApplied, args), + args, ); } @@ -6021,7 +7768,8 @@ export function applyPostAttackAbAttrs( attrType, pokemon, (attr, passive) => attr.applyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args), - (attr, passive) => attr.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args), args, + (attr, passive) => attr.canApplyPostAttack(pokemon, passive, simulated, defender, move, hitResult, args), + args, simulated, ); } @@ -6075,17 +7823,13 @@ export function applyPostSummonAbAttrs( ); } -export function applyPreSummonAbAttrs( - attrType: Constructor, - pokemon: Pokemon, - ...args: any[] -): void { +export function applyPreSummonAbAttrs(attrType: Constructor, pokemon: Pokemon, ...args: any[]): void { applyAbAttrsInternal( attrType, pokemon, (attr, passive) => attr.applyPreSummon(pokemon, passive, args), (attr, passive) => attr.canApplyPreSummon(pokemon, passive, args), - args + args, ); } @@ -6111,18 +7855,17 @@ export function applyPreLeaveFieldAbAttrs( simulated = false, ...args: any[] ): void { - return applyAbAttrsInternal( + applyAbAttrsInternal( attrType, pokemon, - (attr, passive) => - attr.applyPreLeaveField(pokemon, passive, simulated, args), + (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, args), (attr, passive) => attr.canApplyPreLeaveField(pokemon, passive, simulated, args), args, - simulated + simulated, ); } -export function applyPreStatStageChangeAbAttrs ( +export function applyPreStatStageChangeAbAttrs( attrType: Constructor, pokemon: Pokemon | null, stat: BattleStat, @@ -6144,7 +7887,7 @@ export function applyPostStatStageChangeAbAttrs( attrType: Constructor, pokemon: Pokemon, stats: BattleStat[], - stages: integer, + stages: number, selfTarget: boolean, simulated = false, ...args: any[] @@ -6153,7 +7896,8 @@ export function applyPostStatStageChangeAbAttrs( attrType, pokemon, (attr, _passive) => attr.applyPostStatStageChange(pokemon, simulated, stats, stages, selfTarget, args), - (attr, _passive) => attr.canApplyPostStatStageChange(pokemon, simulated, stats, stages, selfTarget, args), args, + (attr, _passive) => attr.canApplyPostStatStageChange(pokemon, simulated, stats, stages, selfTarget, args), + args, simulated, ); } @@ -6292,7 +8036,8 @@ export function applyCheckTrappedAbAttrs( attrType, pokemon, (attr, passive) => attr.applyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args), - (attr, passive) => attr.canApplyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args), args, + (attr, passive) => attr.canApplyCheckTrapped(pokemon, passive, simulated, trapped, otherPokemon, args), + args, simulated, messages, ); @@ -6342,8 +8087,8 @@ export function applyPostItemLostAbAttrs( applyAbAttrsInternal( attrType, pokemon, - (attr, passive) => attr.applyPostItemLost(pokemon, simulated, args), - (attr, passive) => attr.canApplyPostItemLost(pokemon, simulated, args), + (attr, _passive) => attr.applyPostItemLost(pokemon, simulated, args), + (attr, _passive) => attr.canApplyPostItemLost(pokemon, simulated, args), args, ); } @@ -6353,11 +8098,7 @@ export function applyPostItemLostAbAttrs( * * Ignores passives as they don't change and shouldn't be reapplied when main abilities change */ -export function applyOnGainAbAttrs( - pokemon: Pokemon, - passive: boolean = false, - simulated: boolean = false, - ...args: any[]): void { +export function applyOnGainAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void { applySingleAbAttrs( pokemon, passive, @@ -6378,11 +8119,12 @@ export function applyOnLoseAbAttrs(pokemon: Pokemon, passive = false, simulated pokemon, passive, PreLeaveFieldAbAttr, - (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [ ...args, true ]), - (attr, passive) => attr.canApplyPreLeaveField(pokemon, passive, simulated, [ ...args, true ]), + (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [...args, true]), + (attr, passive) => attr.canApplyPreLeaveField(pokemon, passive, simulated, [...args, true]), args, true, - simulated); + simulated, + ); applySingleAbAttrs( pokemon, @@ -6392,8 +8134,8 @@ export function applyOnLoseAbAttrs(pokemon: Pokemon, passive = false, simulated (attr, passive) => attr.canApply(pokemon, passive, simulated, args), args, true, - simulated - ) + simulated, + ); } /** @@ -6408,12 +8150,16 @@ function setAbilityRevealed(pokemon: Pokemon): void { * Returns all Pokemon on field with weather-based forms */ function getPokemonWithWeatherBasedForms() { - return globalScene.getField(true).filter(p => - (p.hasAbility(AbilityId.FORECAST) && p.species.speciesId === SpeciesId.CASTFORM) - || (p.hasAbility(AbilityId.FLOWER_GIFT) && p.species.speciesId === SpeciesId.CHERRIM) - ); + return globalScene + .getField(true) + .filter( + p => + (p.hasAbility(AbilityId.FORECAST) && p.species.speciesId === SpeciesId.CASTFORM) || + (p.hasAbility(AbilityId.FLOWER_GIFT) && p.species.speciesId === SpeciesId.CHERRIM), + ); } +// biome-ignore format: prevent biome from removing the newlines (e.g. prevent `new Ability(...).attr(...)`) export function initAbilities() { allAbilities.push( new Ability(AbilityId.NONE, 3), @@ -6553,7 +8299,7 @@ export function initAbilities() { .attr(PostSummonHealStatusAbAttr, StatusEffect.BURN) .ignorable(), new Ability(AbilityId.MAGNET_PULL, 3) - .attr(ArenaTrapAbAttr, (user, target) => { + .attr(ArenaTrapAbAttr, (_user, target) => { return target.getTypes(true).includes(PokemonType.STEEL) || (target.getTypes(true).includes(PokemonType.STELLAR) && target.getTypes().includes(PokemonType.STEEL)); }), new Ability(AbilityId.SOUNDPROOF, 3) @@ -6609,7 +8355,7 @@ export function initAbilities() { .bypassFaint() .ignorable(), new Ability(AbilityId.SHED_SKIN, 3) - .conditionalAttr(pokemon => !randSeedInt(3), PostTurnResetStatusAbAttr), + .conditionalAttr(_pokemon => !randSeedInt(3), PostTurnResetStatusAbAttr), new Ability(AbilityId.GUTS, 3) .attr(BypassBurnDamageReductionAbAttr) .conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(AbilityId.COMATOSE), StatMultiplierAbAttr, Stat.ATK, 1.5), @@ -6632,7 +8378,7 @@ export function initAbilities() { .attr(PostSummonWeatherChangeAbAttr, WeatherType.SUNNY) .attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.SUNNY), new Ability(AbilityId.ARENA_TRAP, 3) - .attr(ArenaTrapAbAttr, (user, target) => target.isGrounded()) + .attr(ArenaTrapAbAttr, (_user, target) => target.isGrounded()) .attr(DoubleBattleChanceAbAttr), new Ability(AbilityId.VITAL_SPIRIT, 3) .attr(StatusEffectImmunityAbAttr, StatusEffect.SLEEP) @@ -6660,8 +8406,8 @@ export function initAbilities() { .attr(TypeImmunityStatStageChangeAbAttr, PokemonType.ELECTRIC, Stat.SPD, 1) .ignorable(), new Ability(AbilityId.RIVALRY, 4) - .attr(MovePowerBoostAbAttr, (user, target, move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender === target?.gender, 1.25, true) - .attr(MovePowerBoostAbAttr, (user, target, move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender !== target?.gender, 0.75), + .attr(MovePowerBoostAbAttr, (user, target, _move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender === target?.gender, 1.25, true) + .attr(MovePowerBoostAbAttr, (user, target, _move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender !== target?.gender, 0.75), new Ability(AbilityId.STEADFAST, 4) .attr(FlinchStatStageChangeAbAttr, [ Stat.SPD ], 1), new Ability(AbilityId.SNOW_CLOAK, 4) @@ -6693,7 +8439,7 @@ export function initAbilities() { new Ability(AbilityId.DOWNLOAD, 4) .attr(DownloadAbAttr), new Ability(AbilityId.IRON_FIST, 4) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.PUNCHING_MOVE), 1.2), + .attr(MovePowerBoostAbAttr, (_user, _target, move) => move.hasFlag(MoveFlags.PUNCHING_MOVE), 1.2), new Ability(AbilityId.POISON_HEAL, 4) .attr(PostTurnStatusHealAbAttr, StatusEffect.TOXIC, StatusEffect.POISON) .attr(BlockStatusDamageAbAttr, StatusEffect.TOXIC, StatusEffect.POISON), @@ -6721,7 +8467,7 @@ export function initAbilities() { .attr(AlwaysHitAbAttr) .attr(DoubleBattleChanceAbAttr), new Ability(AbilityId.STALL, 4) - .attr(ChangeMovePriorityAbAttr, (pokemon, move: Move) => true, -0.2), + .attr(ChangeMovePriorityAbAttr, (_pokemon, _move: Move) => true, -0.2), new Ability(AbilityId.TECHNICIAN, 4) .attr(MovePowerBoostAbAttr, (user, target, move) => { const power = new NumberHolder(move.power); @@ -6778,7 +8524,7 @@ export function initAbilities() { new Ability(AbilityId.FRISK, 4) .attr(FriskAbAttr), new Ability(AbilityId.RECKLESS, 4) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.RECKLESS_MOVE), 1.2), + .attr(MovePowerBoostAbAttr, (_user, _target, move) => move.hasFlag(MoveFlags.RECKLESS_MOVE), 1.2), new Ability(AbilityId.MULTITYPE, 4) .attr(NoFusionAbilityAbAttr) .uncopiable() @@ -6801,7 +8547,7 @@ export function initAbilities() { .attr(PostDefendStealHeldItemAbAttr, (target, user, move) => move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user, target})) .condition(getSheerForceHitDisableAbCondition()), new Ability(AbilityId.SHEER_FORCE, 5) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.chance >= 1, 1.3) + .attr(MovePowerBoostAbAttr, (_user, _target, move) => move.chance >= 1, 1.3) .attr(MoveEffectChanceMultiplierAbAttr, 0), // This attribute does not seem to function - Should disable life orb, eject button, red card, kee/maranga berry if they get implemented new Ability(AbilityId.CONTRARY, 5) .attr(StatStageChangeMultiplierAbAttr, -1) @@ -6809,7 +8555,7 @@ export function initAbilities() { new Ability(AbilityId.UNNERVE, 5) .attr(PreventBerryUseAbAttr), new Ability(AbilityId.DEFIANT, 5) - .attr(PostStatStageChangeStatStageChangeAbAttr, (target, statsChanged, stages) => stages < 0, [ Stat.ATK ], 2), + .attr(PostStatStageChangeStatStageChangeAbAttr, (_target, _statsChanged, stages) => stages < 0, [ Stat.ATK ], 2), new Ability(AbilityId.DEFEATIST, 5) .attr(StatMultiplierAbAttr, Stat.ATK, 0.5) .attr(StatMultiplierAbAttr, Stat.SPATK, 0.5) @@ -6823,8 +8569,8 @@ export function initAbilities() { .attr(AlliedFieldDamageReductionAbAttr, 0.75) .ignorable(), new Ability(AbilityId.WEAK_ARMOR, 5) - .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL, Stat.DEF, -1) - .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL, Stat.SPD, 2), + .attr(PostDefendStatStageChangeAbAttr, (_target, _user, move) => move.category === MoveCategory.PHYSICAL, Stat.DEF, -1) + .attr(PostDefendStatStageChangeAbAttr, (_target, _user, move) => move.category === MoveCategory.PHYSICAL, Stat.SPD, 2), new Ability(AbilityId.HEAVY_METAL, 5) .attr(WeightMultiplierAbAttr, 2) .ignorable(), @@ -6832,12 +8578,12 @@ export function initAbilities() { .attr(WeightMultiplierAbAttr, 0.5) .ignorable(), new Ability(AbilityId.MULTISCALE, 5) - .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.isFullHp(), 0.5) + .attr(ReceivedMoveDamageMultiplierAbAttr, (target, _user, _move) => target.isFullHp(), 0.5) .ignorable(), new Ability(AbilityId.TOXIC_BOOST, 5) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.category === MoveCategory.PHYSICAL && (user?.status?.effect === StatusEffect.POISON || user?.status?.effect === StatusEffect.TOXIC), 1.5), + .attr(MovePowerBoostAbAttr, (user, _target, move) => move.category === MoveCategory.PHYSICAL && (user?.status?.effect === StatusEffect.POISON || user?.status?.effect === StatusEffect.TOXIC), 1.5), new Ability(AbilityId.FLARE_BOOST, 5) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.category === MoveCategory.SPECIAL && user?.status?.effect === StatusEffect.BURN, 1.5), + .attr(MovePowerBoostAbAttr, (user, _target, move) => move.category === MoveCategory.SPECIAL && user?.status?.effect === StatusEffect.BURN, 1.5), new Ability(AbilityId.HARVEST, 5) .attr( PostTurnRestoreBerryAbAttr, @@ -6869,7 +8615,7 @@ export function initAbilities() { .attr(WonderSkinAbAttr) .ignorable(), new Ability(AbilityId.ANALYTIC, 5) - .attr(MovePowerBoostAbAttr, (user, target, move) => { + .attr(MovePowerBoostAbAttr, (user, _target, _move) => { const movePhase = globalScene.phaseManager.findPhase((phase) => phase.is("MovePhase") && phase.pokemon.id !== user?.id); return isNullOrUndefined(movePhase); }, 1.3), @@ -6897,9 +8643,9 @@ export function initAbilities() { new Ability(AbilityId.MOXIE, 5) .attr(PostVictoryStatStageChangeAbAttr, Stat.ATK, 1), new Ability(AbilityId.JUSTIFIED, 5) - .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === PokemonType.DARK && move.category !== MoveCategory.STATUS, Stat.ATK, 1), + .attr(PostDefendStatStageChangeAbAttr, (_target, user, move) => user.getMoveType(move) === PokemonType.DARK && move.category !== MoveCategory.STATUS, Stat.ATK, 1), new Ability(AbilityId.RATTLED, 5) - .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => { + .attr(PostDefendStatStageChangeAbAttr, (_target, user, move) => { const moveType = user.getMoveType(move); return move.category !== MoveCategory.STATUS && (moveType === PokemonType.DARK || moveType === PokemonType.BUG || moveType === PokemonType.GHOST); @@ -6915,7 +8661,7 @@ export function initAbilities() { .attr(TypeImmunityStatStageChangeAbAttr, PokemonType.GRASS, Stat.ATK, 1) .ignorable(), new Ability(AbilityId.PRANKSTER, 5) - .attr(ChangeMovePriorityAbAttr, (pokemon, move: Move) => move.category === MoveCategory.STATUS, 1), + .attr(ChangeMovePriorityAbAttr, (_pokemon, move: Move) => move.category === MoveCategory.STATUS, 1), new Ability(AbilityId.SAND_FORCE, 5) .attr(MoveTypePowerBoostAbAttr, PokemonType.ROCK, 1.3) .attr(MoveTypePowerBoostAbAttr, PokemonType.GROUND, 1.3) @@ -6966,7 +8712,7 @@ export function initAbilities() { .attr(PokemonTypeChangeAbAttr), //.condition((p) => !p.summonData.abilitiesApplied.includes(AbilityId.PROTEAN)), //Gen 9 Implementation new Ability(AbilityId.FUR_COAT, 6) - .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL, 0.5) + .attr(ReceivedMoveDamageMultiplierAbAttr, (_target, _user, move) => move.category === MoveCategory.PHYSICAL, 0.5) .ignorable(), new Ability(AbilityId.MAGICIAN, 6) .attr(PostAttackStealHeldItemAbAttr), @@ -6974,11 +8720,11 @@ export function initAbilities() { .attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.BALLBOMB_MOVE)) .ignorable(), new Ability(AbilityId.COMPETITIVE, 6) - .attr(PostStatStageChangeStatStageChangeAbAttr, (target, statsChanged, stages) => stages < 0, [ Stat.SPATK ], 2), + .attr(PostStatStageChangeStatStageChangeAbAttr, (_target, _statsChanged, stages) => stages < 0, [ Stat.SPATK ], 2), new Ability(AbilityId.STRONG_JAW, 6) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.BITING_MOVE), 1.5), + .attr(MovePowerBoostAbAttr, (_user, _target, move) => move.hasFlag(MoveFlags.BITING_MOVE), 1.5), new Ability(AbilityId.REFRIGERATE, 6) - .attr(MoveTypeChangeAbAttr, PokemonType.ICE, 1.2, (user, target, move) => move.type === PokemonType.NORMAL), + .attr(MoveTypeChangeAbAttr, PokemonType.ICE, 1.2, (_user, _target, move) => move.type === PokemonType.NORMAL), new Ability(AbilityId.SWEET_VEIL, 6) .attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.SLEEP) .attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.SLEEP) @@ -6993,20 +8739,20 @@ export function initAbilities() { new Ability(AbilityId.GALE_WINGS, 6) .attr(ChangeMovePriorityAbAttr, (pokemon, move) => pokemon.isFullHp() && pokemon.getMoveType(move) === PokemonType.FLYING, 1), new Ability(AbilityId.MEGA_LAUNCHER, 6) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.PULSE_MOVE), 1.5), + .attr(MovePowerBoostAbAttr, (_user, _target, move) => move.hasFlag(MoveFlags.PULSE_MOVE), 1.5), new Ability(AbilityId.GRASS_PELT, 6) .conditionalAttr(getTerrainCondition(TerrainType.GRASSY), StatMultiplierAbAttr, Stat.DEF, 1.5) .ignorable(), new Ability(AbilityId.SYMBIOSIS, 6) .unimplemented(), new Ability(AbilityId.TOUGH_CLAWS, 6) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), 1.3), + .attr(MovePowerBoostAbAttr, (_user, _target, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), 1.3), new Ability(AbilityId.PIXILATE, 6) - .attr(MoveTypeChangeAbAttr, PokemonType.FAIRY, 1.2, (user, target, move) => move.type === PokemonType.NORMAL), + .attr(MoveTypeChangeAbAttr, PokemonType.FAIRY, 1.2, (_user, _target, move) => move.type === PokemonType.NORMAL), new Ability(AbilityId.GOOEY, 6) - .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), Stat.SPD, -1, false), + .attr(PostDefendStatStageChangeAbAttr, (_target, _user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), Stat.SPD, -1, false), new Ability(AbilityId.AERILATE, 6) - .attr(MoveTypeChangeAbAttr, PokemonType.FLYING, 1.2, (user, target, move) => move.type === PokemonType.NORMAL), + .attr(MoveTypeChangeAbAttr, PokemonType.FLYING, 1.2, (_user, _target, move) => move.type === PokemonType.NORMAL), new Ability(AbilityId.PARENTAL_BOND, 6) .attr(AddSecondStrikeAbAttr, 0.25), new Ability(AbilityId.DARK_AURA, 6) @@ -7017,9 +8763,9 @@ export function initAbilities() { .attr(FieldMoveTypePowerBoostAbAttr, PokemonType.FAIRY, 4 / 3), new Ability(AbilityId.AURA_BREAK, 6) .ignorable() - .conditionalAttr(pokemon => globalScene.getField(true).some(p => p.hasAbility(AbilityId.DARK_AURA)), FieldMoveTypePowerBoostAbAttr, PokemonType.DARK, 9 / 16) - .conditionalAttr(pokemon => globalScene.getField(true).some(p => p.hasAbility(AbilityId.FAIRY_AURA)), FieldMoveTypePowerBoostAbAttr, PokemonType.FAIRY, 9 / 16) - .conditionalAttr(pokemon => globalScene.getField(true).some(p => p.hasAbility(AbilityId.DARK_AURA) || p.hasAbility(AbilityId.FAIRY_AURA)), + .conditionalAttr(_pokemon => globalScene.getField(true).some(p => p.hasAbility(AbilityId.DARK_AURA)), FieldMoveTypePowerBoostAbAttr, PokemonType.DARK, 9 / 16) + .conditionalAttr(_pokemon => globalScene.getField(true).some(p => p.hasAbility(AbilityId.FAIRY_AURA)), FieldMoveTypePowerBoostAbAttr, PokemonType.FAIRY, 9 / 16) + .conditionalAttr(_pokemon => globalScene.getField(true).some(p => p.hasAbility(AbilityId.DARK_AURA) || p.hasAbility(AbilityId.FAIRY_AURA)), PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonAuraBreak", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })), new Ability(AbilityId.PRIMORDIAL_SEA, 6) .attr(PostSummonWeatherChangeAbAttr, WeatherType.HEAVY_RAIN) @@ -7037,7 +8783,7 @@ export function initAbilities() { .attr(PreLeaveFieldClearWeatherAbAttr) .bypassFaint(), new Ability(AbilityId.STAMINA, 7) - .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, Stat.DEF, 1), + .attr(PostDefendStatStageChangeAbAttr, (_target, _user, move) => move.category !== MoveCategory.STATUS, Stat.DEF, 1), new Ability(AbilityId.WIMP_OUT, 7) .attr(PostDamageForceSwitchAbAttr) .edgeCase(), // Should not trigger when hurting itself in confusion, causes Fake Out to fail turn 1 and succeed turn 2 if pokemon is switched out before battle start via playing in Switch Mode @@ -7045,9 +8791,9 @@ export function initAbilities() { .attr(PostDamageForceSwitchAbAttr) .edgeCase(), // Should not trigger when hurting itself in confusion, causes Fake Out to fail turn 1 and succeed turn 2 if pokemon is switched out before battle start via playing in Switch Mode new Ability(AbilityId.WATER_COMPACTION, 7) - .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === PokemonType.WATER && move.category !== MoveCategory.STATUS, Stat.DEF, 2), + .attr(PostDefendStatStageChangeAbAttr, (_target, user, move) => user.getMoveType(move) === PokemonType.WATER && move.category !== MoveCategory.STATUS, Stat.DEF, 2), new Ability(AbilityId.MERCILESS, 7) - .attr(ConditionalCritAbAttr, (user, target, move) => target?.status?.effect === StatusEffect.TOXIC || target?.status?.effect === StatusEffect.POISON), + .attr(ConditionalCritAbAttr, (_user, target, _move) => target?.status?.effect === StatusEffect.TOXIC || target?.status?.effect === StatusEffect.POISON), new Ability(AbilityId.SHIELDS_DOWN, 7) .attr(PostBattleInitFormChangeAbAttr, () => 0) .attr(PostSummonFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) @@ -7061,7 +8807,7 @@ export function initAbilities() { .unsuppressable() .bypassFaint(), new Ability(AbilityId.STAKEOUT, 7) - .attr(MovePowerBoostAbAttr, (user, target, move) => !!target?.turnData.switchedInThisTurn, 2), + .attr(MovePowerBoostAbAttr, (_user, target, _move) => !!target?.turnData.switchedInThisTurn, 2), new Ability(AbilityId.WATER_BUBBLE, 7) .attr(ReceivedTypeDamageMultiplierAbAttr, PokemonType.FIRE, 0.5) .attr(MoveTypePowerBoostAbAttr, PokemonType.WATER, 2) @@ -7071,7 +8817,7 @@ export function initAbilities() { new Ability(AbilityId.STEELWORKER, 7) .attr(MoveTypePowerBoostAbAttr, PokemonType.STEEL), new Ability(AbilityId.BERSERK, 7) - .attr(PostDefendHpGatedStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ Stat.SPATK ], 1) + .attr(PostDefendHpGatedStatStageChangeAbAttr, (_target, _user, move) => move.category !== MoveCategory.STATUS, 0.5, [ Stat.SPATK ], 1) .condition(getSheerForceHitDisableAbCondition()), new Ability(AbilityId.SLUSH_RUSH, 7) .attr(StatMultiplierAbAttr, Stat.SPD, 2) @@ -7079,9 +8825,9 @@ export function initAbilities() { new Ability(AbilityId.LONG_REACH, 7) .attr(IgnoreContactAbAttr), new Ability(AbilityId.LIQUID_VOICE, 7) - .attr(MoveTypeChangeAbAttr, PokemonType.WATER, 1, (user, target, move) => move.hasFlag(MoveFlags.SOUND_BASED)), + .attr(MoveTypeChangeAbAttr, PokemonType.WATER, 1, (_user, _target, move) => move.hasFlag(MoveFlags.SOUND_BASED)), new Ability(AbilityId.TRIAGE, 7) - .attr(ChangeMovePriorityAbAttr, (pokemon, move) => move.hasFlag(MoveFlags.TRIAGE_MOVE), 3), + .attr(ChangeMovePriorityAbAttr, (_pokemon, move) => move.hasFlag(MoveFlags.TRIAGE_MOVE), 3), new Ability(AbilityId.GALVANIZE, 7) .attr(MoveTypeChangeAbAttr, PokemonType.ELECTRIC, 1.2, (_user, _target, move) => move.type === PokemonType.NORMAL), new Ability(AbilityId.SURGE_SURFER, 7) @@ -7151,7 +8897,7 @@ export function initAbilities() { .attr(AllyMoveCategoryPowerBoostAbAttr, [ MoveCategory.SPECIAL ], 1.3), new Ability(AbilityId.FLUFFY, 7) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user, target}), 0.5) - .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => user.getMoveType(move) === PokemonType.FIRE, 2) + .attr(ReceivedMoveDamageMultiplierAbAttr, (_target, user, move) => user.getMoveType(move) === PokemonType.FIRE, 2) .ignorable(), new Ability(AbilityId.DAZZLING, 7) .attr(FieldPriorityMoveImmunityAbAttr) @@ -7199,7 +8945,7 @@ export function initAbilities() { new Ability(AbilityId.FULL_METAL_BODY, 7) .attr(ProtectStatAbAttr), new Ability(AbilityId.SHADOW_SHIELD, 7) - .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.isFullHp(), 0.5), + .attr(ReceivedMoveDamageMultiplierAbAttr, (target, _user, _move) => target.isFullHp(), 0.5), new Ability(AbilityId.PRISM_ARMOR, 7) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75), new Ability(AbilityId.NEUROFORCE, 7) @@ -7215,7 +8961,7 @@ export function initAbilities() { .attr(FetchBallAbAttr) .condition(getOncePerBattleCondition(AbilityId.BALL_FETCH)), new Ability(AbilityId.COTTON_DOWN, 8) - .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, Stat.SPD, -1, false, true) + .attr(PostDefendStatStageChangeAbAttr, (_target, _user, move) => move.category !== MoveCategory.STATUS, Stat.SPD, -1, false, true) .bypassFaint(), new Ability(AbilityId.PROPELLER_TAIL, 8) .attr(BlockRedirectAbAttr), @@ -7238,20 +8984,20 @@ export function initAbilities() { new Ability(AbilityId.STALWART, 8) .attr(BlockRedirectAbAttr), new Ability(AbilityId.STEAM_ENGINE, 8) - .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => { + .attr(PostDefendStatStageChangeAbAttr, (_target, user, move) => { const moveType = user.getMoveType(move); return move.category !== MoveCategory.STATUS && (moveType === PokemonType.FIRE || moveType === PokemonType.WATER); }, Stat.SPD, 6), new Ability(AbilityId.PUNK_ROCK, 8) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SOUND_BASED), 1.3) - .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.SOUND_BASED), 0.5) + .attr(MovePowerBoostAbAttr, (_user, _target, move) => move.hasFlag(MoveFlags.SOUND_BASED), 1.3) + .attr(ReceivedMoveDamageMultiplierAbAttr, (_target, _user, move) => move.hasFlag(MoveFlags.SOUND_BASED), 0.5) .ignorable(), new Ability(AbilityId.SAND_SPIT, 8) - .attr(PostDefendWeatherChangeAbAttr, WeatherType.SANDSTORM, (target, user, move) => move.category !== MoveCategory.STATUS) + .attr(PostDefendWeatherChangeAbAttr, WeatherType.SANDSTORM, (_target, _user, move) => move.category !== MoveCategory.STATUS) .bypassFaint(), new Ability(AbilityId.ICE_SCALES, 8) - .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.category === MoveCategory.SPECIAL, 0.5) + .attr(ReceivedMoveDamageMultiplierAbAttr, (_target, _user, move) => move.category === MoveCategory.SPECIAL, 0.5) .ignorable(), new Ability(AbilityId.RIPEN, 8) .attr(DoubleBerryEffectAbAttr), @@ -7265,7 +9011,7 @@ export function initAbilities() { // When weather changes to HAIL or SNOW while pokemon is fielded, add BattlerTagType.ICE_FACE .attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.ICE_FACE, 0, WeatherType.HAIL, WeatherType.SNOW) .attr(FormBlockDamageAbAttr, - (target, user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0, BattlerTagType.ICE_FACE, + (target, _user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0, BattlerTagType.ICE_FACE, (pokemon, abilityName) => i18next.t("abilityTriggers:iceFaceAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName })) .attr(PostBattleInitFormChangeAbAttr, () => 0) .uncopiable() @@ -7343,13 +9089,13 @@ export function initAbilities() { .attr(PostDefendTerrainChangeAbAttr, TerrainType.GRASSY) .bypassFaint(), new Ability(AbilityId.THERMAL_EXCHANGE, 9) - .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === PokemonType.FIRE && move.category !== MoveCategory.STATUS, Stat.ATK, 1) + .attr(PostDefendStatStageChangeAbAttr, (_target, user, move) => user.getMoveType(move) === PokemonType.FIRE && move.category !== MoveCategory.STATUS, Stat.ATK, 1) .attr(StatusEffectImmunityAbAttr, StatusEffect.BURN) .attr(PostSummonHealStatusAbAttr, StatusEffect.BURN) .ignorable(), new Ability(AbilityId.ANGER_SHELL, 9) - .attr(PostDefendHpGatedStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ Stat.ATK, Stat.SPATK, Stat.SPD ], 1) - .attr(PostDefendHpGatedStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ Stat.DEF, Stat.SPDEF ], -1) + .attr(PostDefendHpGatedStatStageChangeAbAttr, (_target, _user, move) => move.category !== MoveCategory.STATUS, 0.5, [ Stat.ATK, Stat.SPATK, Stat.SPD ], 1) + .attr(PostDefendHpGatedStatStageChangeAbAttr, (_target, _user, move) => move.category !== MoveCategory.STATUS, 0.5, [ Stat.DEF, Stat.SPDEF ], -1) .condition(getSheerForceHitDisableAbCondition()), new Ability(AbilityId.PURIFYING_SALT, 9) .attr(StatusEffectImmunityAbAttr) @@ -7369,7 +9115,7 @@ export function initAbilities() { new Ability(AbilityId.ROCKY_PAYLOAD, 9) .attr(MoveTypePowerBoostAbAttr, PokemonType.ROCK), new Ability(AbilityId.WIND_POWER, 9) - .attr(PostDefendApplyBattlerTagAbAttr, (target, user, move) => move.hasFlag(MoveFlags.WIND_MOVE), BattlerTagType.CHARGED), + .attr(PostDefendApplyBattlerTagAbAttr, (_target, _user, move) => move.hasFlag(MoveFlags.WIND_MOVE), BattlerTagType.CHARGED), new Ability(AbilityId.ZERO_TO_HERO, 9) .uncopiable() .unreplaceable() @@ -7386,7 +9132,7 @@ export function initAbilities() { .unreplaceable() .edgeCase(), // Encore, Frenzy, and other non-`TURN_END` tags don't lapse correctly on the commanding Pokemon. new Ability(AbilityId.ELECTROMORPHOSIS, 9) - .attr(PostDefendApplyBattlerTagAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, BattlerTagType.CHARGED), + .attr(PostDefendApplyBattlerTagAbAttr, (_target, _user, move) => move.category !== MoveCategory.STATUS, BattlerTagType.CHARGED), new Ability(AbilityId.PROTOSYNTHESIS, 9) .conditionalAttr(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN), PostSummonAddBattlerTagAbAttr, BattlerTagType.PROTOSYNTHESIS, 0, true) .attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.PROTOSYNTHESIS, 0, WeatherType.SUNNY, WeatherType.HARSH_SUN) @@ -7432,14 +9178,14 @@ export function initAbilities() { new Ability(AbilityId.CUD_CHEW, 9) .attr(RepeatBerryNextTurnAbAttr), new Ability(AbilityId.SHARPNESS, 9) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5), + .attr(MovePowerBoostAbAttr, (_user, _target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5), new Ability(AbilityId.SUPREME_OVERLORD, 9) - .attr(VariableMovePowerBoostAbAttr, (user, target, move) => 1 + 0.1 * Math.min(user.isPlayer() ? globalScene.arena.playerFaints : globalScene.currentBattle.enemyFaints, 5)) + .attr(VariableMovePowerBoostAbAttr, (user, _target, _move) => 1 + 0.1 * Math.min(user.isPlayer() ? globalScene.arena.playerFaints : globalScene.currentBattle.enemyFaints, 5)) .partial(), // Should only boost once, on summon new Ability(AbilityId.COSTAR, 9) .attr(PostSummonCopyAllyStatsAbAttr), new Ability(AbilityId.TOXIC_DEBRIS, 9) - .attr(PostDefendApplyArenaTrapTagAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL, ArenaTagType.TOXIC_SPIKES) + .attr(PostDefendApplyArenaTrapTagAbAttr, (_target, _user, move) => move.category === MoveCategory.PHYSICAL, ArenaTagType.TOXIC_SPIKES) .bypassFaint(), new Ability(AbilityId.ARMOR_TAIL, 9) .attr(FieldPriorityMoveImmunityAbAttr) @@ -7448,9 +9194,9 @@ export function initAbilities() { .attr(TypeImmunityHealAbAttr, PokemonType.GROUND) .ignorable(), new Ability(AbilityId.MYCELIUM_MIGHT, 9) - .attr(ChangeMovePriorityAbAttr, (pokemon, move) => move.category === MoveCategory.STATUS, -0.2) - .attr(PreventBypassSpeedChanceAbAttr, (pokemon, move) => move.category === MoveCategory.STATUS) - .attr(MoveAbilityBypassAbAttr, (pokemon, move: Move) => move.category === MoveCategory.STATUS), + .attr(ChangeMovePriorityAbAttr, (_pokemon, move) => move.category === MoveCategory.STATUS, -0.2) + .attr(PreventBypassSpeedChanceAbAttr, (_pokemon, move) => move.category === MoveCategory.STATUS) + .attr(MoveAbilityBypassAbAttr, (_pokemon, move: Move) => move.category === MoveCategory.STATUS), new Ability(AbilityId.MINDS_EYE, 9) .attr(IgnoreTypeImmunityAbAttr, PokemonType.GHOST, [ PokemonType.NORMAL, PokemonType.FIGHTING ]) .attr(ProtectStatAbAttr, Stat.ACC) From 1c4edabd1dde2b30179aebe08f9a3bdfad199bc3 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Sun, 8 Jun 2025 01:55:30 -0500 Subject: [PATCH 23/23] [Refactor] Ensure that new phases are created through the phase manager https://github.com/pagefaultgames/pokerogue/pull/5955 * Add newPhase method to phase-manager * Update calls to append/prepend phase to use string phase * Replace instantiations of new phase with phase manager --- src/@types/phase-types.ts | 299 +--------------- src/battle-scene.ts | 47 +-- src/data/abilities/ability.ts | 325 ++++++++++-------- src/data/arena-tag.ts | 61 ++-- src/data/battler-tags.ts | 226 ++++++------ src/data/berry.ts | 37 +- src/data/moves/move.ts | 105 +++--- .../encounters/a-trainers-test-encounter.ts | 3 +- .../encounters/absolute-avarice-encounter.ts | 9 +- .../an-offer-you-cant-refuse-encounter.ts | 3 +- .../encounters/berries-abound-encounter.ts | 9 +- .../encounters/bug-type-superfan-encounter.ts | 7 +- .../encounters/dancing-lessons-encounter.ts | 21 +- .../encounters/dark-deal-encounter.ts | 3 +- .../encounters/delibirdy-encounter.ts | 13 +- .../encounters/fiery-fallout-encounter.ts | 17 +- .../encounters/fight-or-flight-encounter.ts | 9 +- .../encounters/fun-and-games-encounter.ts | 6 +- .../encounters/mysterious-chest-encounter.ts | 3 +- .../encounters/safari-zone-encounter.ts | 6 +- .../slumbering-snorlax-encounter.ts | 3 +- .../teleporting-hijinks-encounter.ts | 9 +- .../encounters/the-strong-stuff-encounter.ts | 9 +- .../the-winstrate-challenge-encounter.ts | 9 +- .../encounters/uncommon-breed-encounter.ts | 9 +- .../utils/encounter-phase-utils.ts | 56 ++- .../utils/encounter-pokemon-utils.ts | 3 +- src/field/arena.ts | 26 +- src/field/pokemon.ts | 51 ++- src/modifier/modifier.ts | 126 ++++--- src/phase-manager.ts | 294 +++++++++++++++- src/phases/attempt-capture-phase.ts | 3 +- src/phases/attempt-run-phase.ts | 9 +- src/phases/battle-end-phase.ts | 3 +- src/phases/berry-phase.ts | 8 +- src/phases/check-status-effect-phase.ts | 3 +- src/phases/check-switch-phase.ts | 8 +- src/phases/command-phase.ts | 9 +- src/phases/egg-lapse-phase.ts | 6 +- src/phases/encounter-phase.ts | 71 ++-- src/phases/evolution-phase.ts | 12 +- src/phases/exp-phase.ts | 3 +- src/phases/faint-phase.ts | 20 +- src/phases/form-change-phase.ts | 3 +- src/phases/game-over-phase.ts | 42 +-- src/phases/level-up-phase.ts | 6 +- src/phases/login-phase.ts | 8 +- src/phases/message-phase.ts | 9 +- src/phases/move-charge-phase.ts | 3 +- src/phases/move-effect-phase.ts | 27 +- src/phases/move-phase.ts | 38 +- src/phases/mystery-encounter-phases.ts | 58 ++-- src/phases/post-game-over-phase.ts | 3 +- src/phases/quiet-form-change-phase.ts | 12 +- src/phases/revival-blessing-phase.ts | 24 +- src/phases/select-biome-phase.ts | 6 +- src/phases/select-modifier-phase.ts | 12 +- src/phases/select-starter-phase.ts | 3 +- src/phases/select-target-phase.ts | 3 +- src/phases/show-ability-phase.ts | 5 +- src/phases/show-party-exp-bar-phase.ts | 6 +- src/phases/stat-stage-change-phase.ts | 23 +- src/phases/summon-phase.ts | 9 +- src/phases/switch-phase.ts | 5 +- src/phases/switch-summon-phase.ts | 3 +- src/phases/title-phase.ts | 23 +- src/phases/trainer-victory-phase.ts | 36 +- src/phases/turn-end-phase.ts | 18 +- src/phases/turn-init-phase.ts | 19 +- src/phases/turn-start-phase.ts | 138 ++++---- src/phases/unavailable-phase.ts | 3 +- src/phases/victory-phase.ts | 62 ++-- src/system/game-data.ts | 11 +- src/ui/challenges-select-ui-handler.ts | 6 +- src/ui/starter-select-ui-handler.ts | 9 +- test/testUtils/gameManager.ts | 4 +- 76 files changed, 1302 insertions(+), 1294 deletions(-) diff --git a/src/@types/phase-types.ts b/src/@types/phase-types.ts index 596d9b15723..1d68c7921dd 100644 --- a/src/@types/phase-types.ts +++ b/src/@types/phase-types.ts @@ -1,286 +1,25 @@ -import type { MoveAnim } from "#app/data/battle-anims"; -import type { AddEnemyBuffModifierPhase } from "#app/phases/add-enemy-buff-modifier-phase"; -import type { AttemptCapturePhase } from "#app/phases/attempt-capture-phase"; -import type { AttemptRunPhase } from "#app/phases/attempt-run-phase"; -import type { BattleEndPhase } from "#app/phases/battle-end-phase"; -import type { BerryPhase } from "#app/phases/berry-phase"; -import type { CheckStatusEffectPhase } from "#app/phases/check-status-effect-phase"; -import type { CheckSwitchPhase } from "#app/phases/check-switch-phase"; -import type { CommandPhase } from "#app/phases/command-phase"; -import type { CommonAnimPhase } from "#app/phases/common-anim-phase"; -import type { DamageAnimPhase } from "#app/phases/damage-anim-phase"; -import type { EggHatchPhase } from "#app/phases/egg-hatch-phase"; -import type { EggLapsePhase } from "#app/phases/egg-lapse-phase"; -import type { EggSummaryPhase } from "#app/phases/egg-summary-phase"; -import type { EncounterPhase } from "#app/phases/encounter-phase"; -import type { EndCardPhase } from "#app/phases/end-card-phase"; -import type { EndEvolutionPhase } from "#app/phases/end-evolution-phase"; -import type { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import type { EvolutionPhase } from "#app/phases/evolution-phase"; -import type { ExpPhase } from "#app/phases/exp-phase"; -import type { FaintPhase } from "#app/phases/faint-phase"; -import type { FormChangePhase } from "#app/phases/form-change-phase"; -import type { GameOverModifierRewardPhase } from "#app/phases/game-over-modifier-reward-phase"; -import type { GameOverPhase } from "#app/phases/game-over-phase"; -import type { HideAbilityPhase } from "#app/phases/hide-ability-phase"; -import type { HidePartyExpBarPhase } from "#app/phases/hide-party-exp-bar-phase"; -import type { LearnMovePhase } from "#app/phases/learn-move-phase"; -import type { LevelCapPhase } from "#app/phases/level-cap-phase"; -import type { LevelUpPhase } from "#app/phases/level-up-phase"; -import type { LoadMoveAnimPhase } from "#app/phases/load-move-anim-phase"; -import type { LoginPhase } from "#app/phases/login-phase"; -import type { MessagePhase } from "#app/phases/message-phase"; -import type { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; -import type { MoneyRewardPhase } from "#app/phases/money-reward-phase"; -import type { MoveAnimPhase } from "#app/phases/move-anim-phase"; -import type { MoveChargePhase } from "#app/phases/move-charge-phase"; -import type { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import type { MoveEndPhase } from "#app/phases/move-end-phase"; -import type { MoveHeaderPhase } from "#app/phases/move-header-phase"; -import type { MovePhase } from "#app/phases/move-phase"; -import type { - MysteryEncounterPhase, - MysteryEncounterOptionSelectedPhase, - MysteryEncounterBattlePhase, - MysteryEncounterRewardsPhase, - PostMysteryEncounterPhase, - MysteryEncounterBattleStartCleanupPhase, -} from "#app/phases/mystery-encounter-phases"; -import type { NewBattlePhase } from "#app/phases/new-battle-phase"; -import type { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase"; -import type { NextEncounterPhase } from "#app/phases/next-encounter-phase"; -import type { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase"; -import type { PartyExpPhase } from "#app/phases/party-exp-phase"; -import type { PartyHealPhase } from "#app/phases/party-heal-phase"; -import type { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase"; -import type { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; -import type { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase"; -import type { PostGameOverPhase } from "#app/phases/post-game-over-phase"; -import type { PostSummonPhase } from "#app/phases/post-summon-phase"; -import type { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase"; -import type { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; -import type { ReloadSessionPhase } from "#app/phases/reload-session-phase"; -import type { ResetStatusPhase } from "#app/phases/reset-status-phase"; -import type { ReturnPhase } from "#app/phases/return-phase"; -import type { RevivalBlessingPhase } from "#app/phases/revival-blessing-phase"; -import type { RibbonModifierRewardPhase } from "#app/phases/ribbon-modifier-reward-phase"; -import type { ScanIvsPhase } from "#app/phases/scan-ivs-phase"; -import type { SelectBiomePhase } from "#app/phases/select-biome-phase"; -import type { SelectChallengePhase } from "#app/phases/select-challenge-phase"; -import type { SelectGenderPhase } from "#app/phases/select-gender-phase"; -import type { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import type { SelectStarterPhase } from "#app/phases/select-starter-phase"; -import type { SelectTargetPhase } from "#app/phases/select-target-phase"; -import type { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase"; -import type { ShowAbilityPhase } from "#app/phases/show-ability-phase"; -import type { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase"; -import type { ShowTrainerPhase } from "#app/phases/show-trainer-phase"; -import type { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; -import type { SummonMissingPhase } from "#app/phases/summon-missing-phase"; -import type { SummonPhase } from "#app/phases/summon-phase"; -import type { SwitchBiomePhase } from "#app/phases/switch-biome-phase"; -import type { SwitchPhase } from "#app/phases/switch-phase"; -import type { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; -import type { TeraPhase } from "#app/phases/tera-phase"; -import type { TitlePhase } from "#app/phases/title-phase"; -import type { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; -import type { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase"; -import type { TurnEndPhase } from "#app/phases/turn-end-phase"; -import type { TurnInitPhase } from "#app/phases/turn-init-phase"; -import type { TurnStartPhase } from "#app/phases/turn-start-phase"; -import type { UnavailablePhase } from "#app/phases/unavailable-phase"; -import type { UnlockPhase } from "#app/phases/unlock-phase"; -import type { VictoryPhase } from "#app/phases/victory-phase"; -import type { WeatherEffectPhase } from "#app/phases/weather-effect-phase"; +import type { PhaseConstructorMap } from "#app/phase-manager"; -export type PhaseClass = - | typeof AddEnemyBuffModifierPhase - | typeof AttemptCapturePhase - | typeof AttemptRunPhase - | typeof BattleEndPhase - | typeof BerryPhase - | typeof CheckStatusEffectPhase - | typeof CheckSwitchPhase - | typeof CommandPhase - | typeof CommonAnimPhase - | typeof DamageAnimPhase - | typeof EggHatchPhase - | typeof EggLapsePhase - | typeof EggSummaryPhase - | typeof EncounterPhase - | typeof EndCardPhase - | typeof EndEvolutionPhase - | typeof EnemyCommandPhase - | typeof EvolutionPhase - | typeof FormChangePhase - | typeof ExpPhase - | typeof FaintPhase - | typeof FormChangePhase - | typeof GameOverPhase - | typeof GameOverModifierRewardPhase - | typeof HideAbilityPhase - | typeof HidePartyExpBarPhase - | typeof LearnMovePhase - | typeof LevelUpPhase - | typeof LevelCapPhase - | typeof LoadMoveAnimPhase - | typeof LoginPhase - | typeof MessagePhase - | typeof ModifierRewardPhase - | typeof MoneyRewardPhase - | typeof MoveAnimPhase - | typeof MoveChargePhase - | typeof MoveEffectPhase - | typeof MoveEndPhase - | typeof MoveHeaderPhase - | typeof MovePhase - | typeof MysteryEncounterPhase - | typeof MysteryEncounterOptionSelectedPhase - | typeof MysteryEncounterBattlePhase - | typeof MysteryEncounterRewardsPhase - | typeof MysteryEncounterBattleStartCleanupPhase - | typeof MysteryEncounterRewardsPhase - | typeof PostMysteryEncounterPhase - | typeof NewBattlePhase - | typeof NewBiomeEncounterPhase - | typeof NextEncounterPhase - | typeof ObtainStatusEffectPhase - | typeof PartyExpPhase - | typeof PartyHealPhase - | typeof PokemonAnimPhase - | typeof PokemonHealPhase - | typeof PokemonTransformPhase - | typeof PostGameOverPhase - | typeof PostSummonPhase - | typeof PostTurnStatusEffectPhase - | typeof QuietFormChangePhase - | typeof ReloadSessionPhase - | typeof ResetStatusPhase - | typeof ReturnPhase - | typeof RevivalBlessingPhase - | typeof RibbonModifierRewardPhase - | typeof ScanIvsPhase - | typeof SelectBiomePhase - | typeof SelectChallengePhase - | typeof SelectGenderPhase - | typeof SelectModifierPhase - | typeof SelectStarterPhase - | typeof SelectTargetPhase - | typeof ShinySparklePhase - | typeof ShowAbilityPhase - | typeof ShowTrainerPhase - | typeof ShowPartyExpBarPhase - | typeof StatStageChangePhase - | typeof SummonMissingPhase - | typeof SummonPhase - | typeof SwitchBiomePhase - | typeof SwitchPhase - | typeof SwitchSummonPhase - | typeof TeraPhase - | typeof TitlePhase - | typeof ToggleDoublePositionPhase - | typeof TrainerVictoryPhase - | typeof TurnEndPhase - | typeof TurnInitPhase - | typeof TurnStartPhase - | typeof UnavailablePhase - | typeof UnlockPhase - | typeof VictoryPhase - | typeof WeatherEffectPhase; +// Intentionally export the types of everything in phase-manager, as this file is meant to be +// the centralized place for type definitions for the phase system. +export type * from "#app/phase-manager"; -/** Typescript map used to map a string phase to the actual phase type */ +// This file includes helpful types for the phase system. +// It intentionally imports the phase constructor map from the phase manager (and re-exports it) + +/** + * Map of phase names to constructors for said phase + */ export type PhaseMap = { - AddEnemyBuffModifierPhase: AddEnemyBuffModifierPhase; - AttemptCapturePhase: AttemptCapturePhase; - AttemptRunPhase: AttemptRunPhase; - BattleEndPhase: BattleEndPhase; - BerryPhase: BerryPhase; - CheckStatusEffectPhase: CheckStatusEffectPhase; - CheckSwitchPhase: CheckSwitchPhase; - CommandPhase: CommandPhase; - CommonAnimPhase: CommonAnimPhase; - DamageAnimPhase: DamageAnimPhase; - EggHatchPhase: EggHatchPhase; - EggLapsePhase: EggLapsePhase; - EggSummaryPhase: EggSummaryPhase; - EncounterPhase: EncounterPhase; - EndCardPhase: EndCardPhase; - EndEvolutionPhase: EndEvolutionPhase; - EnemyCommandPhase: EnemyCommandPhase; - EvolutionPhase: EvolutionPhase; - ExpPhase: ExpPhase; - FaintPhase: FaintPhase; - FormChangePhase: FormChangePhase; - GameOverPhase: GameOverPhase; - GameOverModifierRewardPhase: GameOverModifierRewardPhase; - HideAbilityPhase: HideAbilityPhase; - HidePartyExpBarPhase: HidePartyExpBarPhase; - LearnMovePhase: LearnMovePhase; - LevelCapPhase: LevelCapPhase; - LevelUpPhase: LevelUpPhase; - LoadMoveAnimPhase: LoadMoveAnimPhase; - LoginPhase: LoginPhase; - MessagePhase: MessagePhase; - ModifierRewardPhase: ModifierRewardPhase; - MoneyRewardPhase: MoneyRewardPhase; - MoveAnimPhase: MoveAnimPhase; - MoveChargePhase: MoveChargePhase; - MoveEffectPhase: MoveEffectPhase; - MoveEndPhase: MoveEndPhase; - MoveHeaderPhase: MoveHeaderPhase; - MovePhase: MovePhase; - MysteryEncounterPhase: MysteryEncounterPhase; - MysteryEncounterOptionSelectedPhase: MysteryEncounterOptionSelectedPhase; - MysteryEncounterBattlePhase: MysteryEncounterBattlePhase; - MysteryEncounterBattleStartCleanupPhase: MysteryEncounterBattleStartCleanupPhase; - MysteryEncounterRewardsPhase: MysteryEncounterRewardsPhase; - PostMysteryEncounterPhase: PostMysteryEncounterPhase; - NewBattlePhase: NewBattlePhase; - NewBiomeEncounterPhase: NewBiomeEncounterPhase; - NextEncounterPhase: NextEncounterPhase; - ObtainStatusEffectPhase: ObtainStatusEffectPhase; - PartyExpPhase: PartyExpPhase; - PartyHealPhase: PartyHealPhase; - PokemonAnimPhase: PokemonAnimPhase; - PokemonHealPhase: PokemonHealPhase; - PokemonTransformPhase: PokemonTransformPhase; - PostGameOverPhase: PostGameOverPhase; - PostSummonPhase: PostSummonPhase; - PostTurnStatusEffectPhase: PostTurnStatusEffectPhase; - QuietFormChangePhase: QuietFormChangePhase; - ReloadSessionPhase: ReloadSessionPhase; - ResetStatusPhase: ResetStatusPhase; - ReturnPhase: ReturnPhase; - RevivalBlessingPhase: RevivalBlessingPhase; - RibbonModifierRewardPhase: RibbonModifierRewardPhase; - ScanIvsPhase: ScanIvsPhase; - SelectBiomePhase: SelectBiomePhase; - SelectChallengePhase: SelectChallengePhase; - SelectGenderPhase: SelectGenderPhase; - SelectModifierPhase: SelectModifierPhase; - SelectStarterPhase: SelectStarterPhase; - SelectTargetPhase: SelectTargetPhase; - ShinySparklePhase: ShinySparklePhase; - ShowAbilityPhase: ShowAbilityPhase; - ShowPartyExpBarPhase: ShowPartyExpBarPhase; - ShowTrainerPhase: ShowTrainerPhase; - StatStageChangePhase: StatStageChangePhase; - SummonMissingPhase: SummonMissingPhase; - SummonPhase: SummonPhase; - SwitchBiomePhase: SwitchBiomePhase; - SwitchPhase: SwitchPhase; - SwitchSummonPhase: SwitchSummonPhase; - TeraPhase: TeraPhase; - TitlePhase: TitlePhase; - ToggleDoublePositionPhase: ToggleDoublePositionPhase; - TrainerVictoryPhase: TrainerVictoryPhase; - TurnEndPhase: TurnEndPhase; - TurnInitPhase: TurnInitPhase; - TurnStartPhase: TurnStartPhase; - UnavailablePhase: UnavailablePhase; - UnlockPhase: UnlockPhase; - VictoryPhase: VictoryPhase; - WeatherEffectPhase: WeatherEffectPhase; + [K in keyof PhaseConstructorMap]: InstanceType; }; +/** + * Union type of all phase constructors. + */ +export type PhaseClass = PhaseConstructorMap[keyof PhaseConstructorMap]; + +/** + * Union type of all phase names as strings. + */ export type PhaseString = keyof PhaseMap; diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 23601910e4d..7743302cf94 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -108,7 +108,6 @@ import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, } from "#app/data/pokemon-forms"; -import { FormChangePhase } from "#app/phases/form-change-phase"; import { getTypeRgb } from "#app/data/type"; import { PokemonType } from "#enums/pokemon-type"; import PokemonSpriteSparkleHandler from "#app/field/pokemon-sprite-sparkle-handler"; @@ -142,18 +141,7 @@ import i18next from "i18next"; import { TrainerType } from "#enums/trainer-type"; import { battleSpecDialogue } from "#app/data/dialogue"; import { LoadingScene } from "#app/loading-scene"; -import { LevelCapPhase } from "#app/phases/level-cap-phase"; -import { LoginPhase } from "#app/phases/login-phase"; import type { MovePhase } from "#app/phases/move-phase"; -import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase"; -import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; -import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; -import { ReturnPhase } from "#app/phases/return-phase"; -import { ShowTrainerPhase } from "#app/phases/show-trainer-phase"; -import { SummonPhase } from "#app/phases/summon-phase"; -import { TitlePhase } from "#app/phases/title-phase"; -import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import { @@ -168,8 +156,6 @@ import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-e import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import type HeldModifierConfig from "#app/@types/held-modifier-config"; -import { ExpPhase } from "#app/phases/exp-phase"; -import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { ExpGainsSpeed } from "#enums/exp-gains-speed"; import { BattlerTagType } from "#enums/battler-tag-type"; @@ -699,8 +685,8 @@ export default class BattleScene extends SceneBase { ).then(() => loadMoveAnimAssets(defaultMoves, true)), this.initStarterColors(), ]).then(() => { - this.phaseManager.pushPhase(new LoginPhase()); - this.phaseManager.pushPhase(new TitlePhase()); + this.phaseManager.pushNew("LoginPhase"); + this.phaseManager.pushNew("TitlePhase"); this.phaseManager.shiftPhase(); }); @@ -1475,7 +1461,7 @@ export default class BattleScene extends SceneBase { playerField.forEach((pokemon, p) => { if (pokemon.isOnField()) { - this.phaseManager.pushPhase(new ReturnPhase(p)); + this.phaseManager.pushNew("ReturnPhase", p); } }); @@ -1492,7 +1478,7 @@ export default class BattleScene extends SceneBase { } if (!this.trainer.visible) { - this.phaseManager.pushPhase(new ShowTrainerPhase()); + this.phaseManager.pushNew("ShowTrainerPhase"); } } @@ -1501,13 +1487,13 @@ export default class BattleScene extends SceneBase { } if (!this.gameMode.hasRandomBiomes && !isNewBiome) { - this.phaseManager.pushPhase(new NextEncounterPhase()); + this.phaseManager.pushNew("NextEncounterPhase"); } else { - this.phaseManager.pushPhase(new NewBiomeEncounterPhase()); + this.phaseManager.pushNew("NewBiomeEncounterPhase"); const newMaxExpLevel = this.getMaxExpLevel(); if (newMaxExpLevel > maxExpLevel) { - this.phaseManager.pushPhase(new LevelCapPhase()); + this.phaseManager.pushNew("LevelCapPhase"); } } } @@ -3199,9 +3185,9 @@ export default class BattleScene extends SceneBase { if (matchingFormChange) { let phase: Phase; if (pokemon.isPlayer() && !matchingFormChange.quiet) { - phase = new FormChangePhase(pokemon, matchingFormChange, modal); + phase = this.phaseManager.create("FormChangePhase", pokemon, matchingFormChange, modal); } else { - phase = new QuietFormChangePhase(pokemon, matchingFormChange); + phase = this.phaseManager.create("QuietFormChangePhase", pokemon, matchingFormChange); } if (pokemon.isPlayer() && !matchingFormChange.quiet && modal) { this.phaseManager.overridePhase(phase); @@ -3223,11 +3209,12 @@ export default class BattleScene extends SceneBase { fieldAssets?: Phaser.GameObjects.Sprite[], delayed = false, ): boolean { - const phase: Phase = new PokemonAnimPhase(battleAnimType, pokemon, fieldAssets); + const phaseManager = this.phaseManager; + const phase: Phase = phaseManager.create("PokemonAnimPhase", battleAnimType, pokemon, fieldAssets); if (delayed) { - this.phaseManager.pushPhase(phase); + phaseManager.pushPhase(phase); } else { - this.phaseManager.unshiftPhase(phase); + phaseManager.unshiftPhase(phase); } return true; } @@ -3335,9 +3322,9 @@ export default class BattleScene extends SceneBase { this.currentBattle.double = true; const availablePartyMembers = this.getPlayerParty().filter(p => p.isAllowedInBattle()); if (availablePartyMembers.length > 1) { - this.phaseManager.pushPhase(new ToggleDoublePositionPhase(true)); + this.phaseManager.pushNew("ToggleDoublePositionPhase", true); if (!availablePartyMembers[1].isOnField()) { - this.phaseManager.pushPhase(new SummonPhase(1)); + this.phaseManager.pushNew("SummonPhase", 1); } } @@ -3461,8 +3448,8 @@ export default class BattleScene extends SceneBase { const partyMemberIndex = party.indexOf(expPartyMembers[pm]); this.phaseManager.unshiftPhase( expPartyMembers[pm].isOnField() - ? new ExpPhase(partyMemberIndex, exp) - : new ShowPartyExpBarPhase(partyMemberIndex, exp), + ? this.phaseManager.create("ExpPhase", partyMemberIndex, exp) + : this.phaseManager.create("ShowPartyExpBarPhase", partyMemberIndex, exp), ); } } diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 9269f84d269..a79e2206348 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -47,16 +47,8 @@ import { Command } from "#app/ui/command-ui-handler"; import { BerryModifierType } from "#app/modifier/modifier-type"; import { getPokeballName } from "#app/data/pokeball"; import { BattleType } from "#enums/battle-type"; -import { MovePhase } from "#app/phases/move-phase"; -import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; +import type { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { globalScene } from "#app/global-scene"; -import { SwitchPhase } from "#app/phases/switch-phase"; -import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; -import { BattleEndPhase } from "#app/phases/battle-end-phase"; -import { NewBattlePhase } from "#app/phases/new-battle-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase"; import { allAbilities } from "#app/data/data-lists"; import { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr"; import { Ability } from "#app/data/abilities/ability-class"; @@ -77,7 +69,6 @@ import { MoveFlags } from "#enums/MoveFlags"; import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; import type { BerryType } from "#enums/berry-type"; -import { CommonAnimPhase } from "#app/phases/common-anim-phase"; import { CommonAnim } from "../battle-anims"; import { getBerryEffectFunc } from "../berry"; import { BerryUsedEvent } from "#app/events/battle-scene"; @@ -98,7 +89,6 @@ import type { import type { BattlerIndex } from "#app/battle"; import type Move from "#app/data/moves/move"; import type { ArenaTrapTag, SuppressAbilitiesTag } from "#app/data/arena-tag"; -import { SelectBiomePhase } from "#app/phases/select-biome-phase"; import { noAbilityTypeOverrideMoves } from "../moves/invalid-moves"; export class BlockRecoilDamageAttr extends AbAttr { @@ -200,10 +190,13 @@ export class PostTeraFormChangeStatChangeAbAttr extends AbAttr { const statStageChangePhases: StatStageChangePhase[] = []; if (!simulated) { - statStageChangePhases.push(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages)); + const phaseManager = globalScene.phaseManager; + statStageChangePhases.push( + phaseManager.create("StatStageChangePhase", pokemon.getBattlerIndex(), true, this.stats, this.stages), + ); for (const statStageChangePhase of statStageChangePhases) { - globalScene.phaseManager.unshiftPhase(statStageChangePhase); + phaseManager.unshiftPhase(statStageChangePhase); } } } @@ -581,16 +574,15 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); if (!pokemon.isFullHp() && !simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - pokemon.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / 4), - i18next.t("abilityTriggers:typeImmunityHeal", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - abilityName, - }), - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / 4), + i18next.t("abilityTriggers:typeImmunityHeal", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + }), + true, ); cancelled.value = true; // Suppresses "No Effect" message } @@ -632,8 +624,12 @@ class TypeImmunityStatStageChangeAbAttr extends TypeImmunityAbAttr { super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); cancelled.value = true; // Suppresses "No Effect" message if (!simulated) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [this.stat], this.stages), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [this.stat], + this.stages, ); } } @@ -960,8 +956,12 @@ export class MoveImmunityStatStageChangeAbAttr extends MoveImmunityAbAttr { args: any[], ): void { super.applyPreDefend(pokemon, passive, simulated, attacker, move, cancelled, args); - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [this.stat], this.stages), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [this.stat], + this.stages, ); } } @@ -1063,18 +1063,21 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr { const ally = pokemon.getAlly(); const otherPokemon = !isNullOrUndefined(ally) ? pokemon.getOpponents().concat([ally]) : pokemon.getOpponents(); for (const other of otherPokemon) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(other.getBattlerIndex(), false, [this.stat], this.stages), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + other.getBattlerIndex(), + false, + [this.stat], + this.stages, ); } } else { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase( - (this.selfTarget ? pokemon : attacker).getBattlerIndex(), - this.selfTarget, - [this.stat], - this.stages, - ), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + (this.selfTarget ? pokemon : attacker).getBattlerIndex(), + this.selfTarget, + [this.stat], + this.stages, ); } } @@ -1130,13 +1133,12 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { _args: any[], ): void { if (!simulated) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase( - (this.selfTarget ? pokemon : attacker).getBattlerIndex(), - true, - this.stats, - this.stages, - ), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + (this.selfTarget ? pokemon : attacker).getBattlerIndex(), + true, + this.stats, + this.stages, ); } } @@ -1450,8 +1452,12 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { _args: any[], ): void { if (!simulated) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [this.stat], this.stages), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [this.stat], + this.stages, ); } } @@ -1766,8 +1772,12 @@ export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChang _args: any[], ): void { if (!simulated) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.statsToChange, this.stages), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + this.statsToChange, + this.stages, ); } } @@ -2928,9 +2938,7 @@ class PostVictoryStatStageChangeAbAttr extends PostVictoryAbAttr { override applyPostVictory(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; if (!simulated) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [stat], this.stages), - ); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [stat], this.stages); } } } @@ -2996,9 +3004,7 @@ export class PostKnockOutStatStageChangeAbAttr extends PostKnockOutAbAttr { ): void { const stat = typeof this.stat === "function" ? this.stat(pokemon) : this.stat; if (!simulated) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [stat], this.stages), - ); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [stat], this.stages); } } } @@ -3112,8 +3118,12 @@ export class PostIntimidateStatStageChangeAbAttr extends AbAttr { _args: any[], ): void { if (!simulated) { - globalScene.phaseManager.pushPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), false, this.stats, this.stages), + globalScene.phaseManager.pushNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + false, + this.stats, + this.stages, ); } cancelled.value = this.overwrites; @@ -3315,8 +3325,12 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { if (this.selfTarget) { // we unshift the StatStageChangePhase to put it right after the showAbility and not at the end of the // phase list (which could be after CommandPhase for example) - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + this.stats, + this.stages, ); } else { for (const opponent of pokemon.getOpponents()) { @@ -3330,8 +3344,12 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { } } if (!cancelled.value) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(opponent.getBattlerIndex(), false, this.stats, this.stages), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + opponent.getBattlerIndex(), + false, + this.stats, + this.stages, ); } } @@ -3357,17 +3375,16 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { override applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): void { const target = pokemon.getAlly(); if (!simulated && !isNullOrUndefined(target)) { - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - target.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / this.healRatio), - i18next.t("abilityTriggers:postSummonAllyHeal", { - pokemonNameWithAffix: getPokemonNameWithAffix(target), - pokemonName: pokemon.name, - }), - true, - !this.showAnim, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + target.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / this.healRatio), + i18next.t("abilityTriggers:postSummonAllyHeal", { + pokemonNameWithAffix: getPokemonNameWithAffix(target), + pokemonName: pokemon.name, + }), + true, + !this.showAnim, ); } } @@ -3445,7 +3462,7 @@ export class DownloadAbAttr extends PostSummonAbAttr { } if (!simulated) { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), false, this.stats, 1)); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), false, this.stats, 1); } } } @@ -3726,8 +3743,11 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { override applyPostSummon(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { const target = this.getTarget(pokemon.getOpponents()); - globalScene.phaseManager.unshiftPhase( - new PokemonTransformPhase(pokemon.getBattlerIndex(), target.getBattlerIndex(), true), + globalScene.phaseManager.unshiftNew( + "PokemonTransformPhase", + pokemon.getBattlerIndex(), + target.getBattlerIndex(), + true, ); } } @@ -4110,8 +4130,17 @@ export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { const stages = args[1]; this.reflectedStat = stat; if (!simulated) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(attacker.getBattlerIndex(), false, [stat], stages, true, false, true, null, true), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + attacker.getBattlerIndex(), + false, + [stat], + stages, + true, + false, + true, + null, + true, ); } cancelled.value = true; @@ -5212,16 +5241,15 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr { ): void { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; if (!simulated) { - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - pokemon.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / (16 / this.healFactor)), - i18next.t("abilityTriggers:postWeatherLapseHeal", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - abilityName, - }), - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / (16 / this.healFactor)), + i18next.t("abilityTriggers:postWeatherLapseHeal", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + }), + true, ); } } @@ -5366,13 +5394,12 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - pokemon.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / 8), - i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / 8), + i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), + true, ); } } @@ -5529,8 +5556,11 @@ export class RepeatBerryNextTurnAbAttr extends PostTurnAbAttr { _cancelled: BooleanHolder | null, _args: any[], ): void { - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM), + globalScene.phaseManager.unshiftNew( + "CommonAnimPhase", + pokemon.getBattlerIndex(), + pokemon.getBattlerIndex(), + CommonAnim.USE_ITEM, ); // Re-apply effects of all berries previously scarfed. @@ -5593,15 +5623,11 @@ export class MoodyAbAttr extends PostTurnAbAttr { if (canRaise.length > 0) { const raisedStat = canRaise[pokemon.randBattleSeedInt(canRaise.length)]; canLower = canRaise.filter(s => s !== raisedStat); - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [raisedStat], 2), - ); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [raisedStat], 2); } if (canLower.length > 0) { const loweredStat = canLower[pokemon.randBattleSeedInt(canLower.length)]; - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [loweredStat], -1), - ); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [loweredStat], -1); } } } @@ -5617,7 +5643,7 @@ export class SpeedBoostAbAttr extends PostTurnAbAttr { } override applyPostTurn(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): void { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPD], 1)); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [Stat.SPD], 1); } } @@ -5629,16 +5655,15 @@ export class PostTurnHealAbAttr extends PostTurnAbAttr { override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, _args: any[]): void { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - pokemon.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / 16), - i18next.t("abilityTriggers:postTurnHeal", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - abilityName, - }), - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / 16), + i18next.t("abilityTriggers:postTurnHeal", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + }), + true, ); } } @@ -5857,13 +5882,14 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr { ): void { if (!simulated) { dancer.turnData.extraTurns++; + const phaseManager = globalScene.phaseManager; // If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) { const target = this.getTarget(dancer, source, targets); - globalScene.phaseManager.unshiftPhase(new MovePhase(dancer, target, move, true, true)); + phaseManager.unshiftNew("MovePhase", dancer, target, move, true, true); } else if (move.getMove() instanceof SelfStatusMove) { // If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself - globalScene.phaseManager.unshiftPhase(new MovePhase(dancer, [dancer.getBattlerIndex()], move, true, true)); + phaseManager.unshiftNew("MovePhase", dancer, [dancer.getBattlerIndex()], move, true, true); } } } @@ -5949,16 +5975,15 @@ export class StatStageChangeCopyAbAttr extends AbAttr { args: any[], ): void { if (!simulated) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase( - pokemon.getBattlerIndex(), - true, - args[0] as BattleStat[], - args[1] as number, - true, - false, - false, - ), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + args[0] as BattleStat[], + args[1] as number, + true, + false, + false, ); } } @@ -6058,16 +6083,15 @@ export class HealFromBerryUseAbAttr extends AbAttr { } const { name: abilityName } = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - pokemon.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() * this.healPercent), - i18next.t("abilityTriggers:healFromBerryUse", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - abilityName, - }), - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() * this.healPercent), + i18next.t("abilityTriggers:healFromBerryUse", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + abilityName, + }), + true, ); } } @@ -6502,8 +6526,12 @@ export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr { _args: any[], ): void { if (!simulated) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + this.stats, + this.stages, ); } } @@ -7241,9 +7269,13 @@ class ForceSwitchOutHelper { if (switchOutTarget.hp > 0) { switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); - globalScene.phaseManager.prependToPhase( - new SwitchPhase(this.switchType, switchOutTarget.getFieldIndex(), true, true), - MoveEndPhase, + globalScene.phaseManager.prependNewToPhase( + "MoveEndPhase", + "SwitchPhase", + this.switchType, + switchOutTarget.getFieldIndex(), + true, + true, ); return true; } @@ -7260,9 +7292,14 @@ class ForceSwitchOutHelper { const summonIndex = globalScene.currentBattle.trainer ? globalScene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0; - globalScene.phaseManager.prependToPhase( - new SwitchSummonPhase(this.switchType, switchOutTarget.getFieldIndex(), summonIndex, false, false), - MoveEndPhase, + globalScene.phaseManager.prependNewToPhase( + "MoveEndPhase", + "SwitchSummonPhase", + this.switchType, + switchOutTarget.getFieldIndex(), + summonIndex, + false, + false, ); return true; } @@ -7294,13 +7331,13 @@ class ForceSwitchOutHelper { globalScene.clearEnemyHeldItemModifiers(); if (switchOutTarget.hp) { - globalScene.phaseManager.pushPhase(new BattleEndPhase(false)); + globalScene.phaseManager.pushNew("BattleEndPhase", false); if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { - globalScene.phaseManager.pushPhase(new SelectBiomePhase()); + globalScene.phaseManager.pushNew("SelectBiomePhase"); } - globalScene.phaseManager.pushPhase(new NewBattlePhase()); + globalScene.phaseManager.pushNew("NewBattlePhase"); } } } diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 70fceb17c49..28b8c6acd41 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -26,10 +26,6 @@ import { AbilityId } from "#enums/ability-id"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; -import { CommonAnimPhase } from "#app/phases/common-anim-phase"; export enum ArenaTagSide { BOTH, @@ -568,9 +564,7 @@ class WishTag extends ArenaTag { const target = globalScene.getField()[this.battlerIndex]; if (target?.isActive(true)) { globalScene.phaseManager.queueMessage(this.triggerMessage); - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase(target.getBattlerIndex(), this.healHp, null, true, false), - ); + globalScene.phaseManager.unshiftNew("PokemonHealPhase", target.getBattlerIndex(), this.healHp, null, true, false); } } } @@ -893,8 +887,13 @@ export class DelayedAttackTag extends ArenaTag { const ret = super.lapse(arena); if (!ret) { - globalScene.phaseManager.unshiftPhase( - new MoveEffectPhase(this.sourceId!, [this.targetIndex], allMoves[this.sourceMove!], false, true), + globalScene.phaseManager.unshiftNew( + "MoveEffectPhase", + this.sourceId!, + [this.targetIndex], + allMoves[this.sourceMove!], + false, + true, ); // TODO: are those bangs correct? } @@ -1028,19 +1027,18 @@ class StickyWebTag extends ArenaTrapTag { }), ); const stages = new NumberHolder(-1); - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase( - pokemon.getBattlerIndex(), - false, - [Stat.SPD], - stages.value, - true, - false, - true, - null, - false, - true, - ), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + false, + [Stat.SPD], + stages.value, + true, + false, + true, + null, + false, + true, ); return true; } @@ -1138,26 +1136,26 @@ class TailwindTag extends ArenaTag { const source = globalScene.getPokemonById(this.sourceId!); //TODO: this bang is questionable! const party = (source?.isPlayer() ? globalScene.getPlayerField() : globalScene.getEnemyField()) ?? []; + const phaseManager = globalScene.phaseManager; for (const pokemon of party) { // Apply the CHARGED tag to party members with the WIND_POWER ability if (pokemon.hasAbility(AbilityId.WIND_POWER) && !pokemon.getTag(BattlerTagType.CHARGED)) { pokemon.addTag(BattlerTagType.CHARGED); - globalScene.phaseManager.queueMessage( + phaseManager.queueMessage( i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName(), }), ); } + // Raise attack by one stage if party member has WIND_RIDER ability // TODO: Ability displays should be handled by the ability if (pokemon.hasAbility(AbilityId.WIND_RIDER)) { - globalScene.phaseManager.queueAbilityDisplay(pokemon, false, true); - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true), - ); - globalScene.phaseManager.queueAbilityDisplay(pokemon, false, false); + phaseManager.queueAbilityDisplay(pokemon, false, true); + phaseManager.unshiftNew("StatStageChangePhase", pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true); + phaseManager.queueAbilityDisplay(pokemon, false, false); } } } @@ -1319,8 +1317,11 @@ class FireGrassPledgeTag extends ArenaTag { }), ); // TODO: Replace this with a proper animation - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM), + globalScene.phaseManager.unshiftNew( + "CommonAnimPhase", + pokemon.getBattlerIndex(), + pokemon.getBattlerIndex(), + CommonAnim.MAGMA_STORM, ); pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT }); }); diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index d0b4620d8eb..d4f62237446 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -27,12 +27,9 @@ import { PokemonType } from "#enums/pokemon-type"; import type Pokemon from "#app/field/pokemon"; import { HitResult, MoveResult } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; -import { CommonAnimPhase } from "#app/phases/common-anim-phase"; import type { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { MovePhase } from "#app/phases/move-phase"; -import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; +import type { MovePhase } from "#app/phases/move-phase"; import type { StatStageChangeCallback } from "#app/phases/stat-stage-change-phase"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import i18next from "#app/plugins/i18n"; import { BooleanHolder, getFrameMs, NumberHolder, toDmgValue } from "#app/utils/common"; import { AbilityId } from "#enums/ability-id"; @@ -560,7 +557,7 @@ export class ShellTrapTag extends BattlerTag { // Only shift MovePhase timing if it's not already next up if (shellTrapPhaseIndex !== -1 && shellTrapPhaseIndex !== firstMovePhaseIndex) { const shellTrapMovePhase = globalScene.phaseManager.phaseQueue.splice(shellTrapPhaseIndex, 1)[0]; - globalScene.phaseManager.prependToPhase(shellTrapMovePhase, MovePhase); + globalScene.phaseManager.prependToPhase(shellTrapMovePhase, "MovePhase"); } this.activated = true; @@ -719,9 +716,7 @@ export class ConfusedTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION), - ); + globalScene.phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION); globalScene.phaseManager.queueMessage( i18next.t("battlerTags:confusedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), @@ -756,14 +751,14 @@ export class ConfusedTag extends BattlerTag { return false; } - globalScene.phaseManager.queueMessage( + const phaseManager = globalScene.phaseManager; + + phaseManager.queueMessage( i18next.t("battlerTags:confusedLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION), - ); + phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, CommonAnim.CONFUSION); // 1/3 chance of hitting self with a 40 base power move if (pokemon.randBattleSeedInt(3) === 0 || Overrides.CONFUSION_ACTIVATION_OVERRIDE === true) { @@ -773,9 +768,9 @@ export class ConfusedTag extends BattlerTag { ((((2 * pokemon.level) / 5 + 2) * 40 * atk) / def / 50 + 2) * (pokemon.randBattleSeedIntRange(85, 100) / 100), ); // Intentionally don't increment rage fist's hitCount - globalScene.phaseManager.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself")); + phaseManager.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself")); pokemon.damageAndUpdate(damage, { result: HitResult.CONFUSION }); - (globalScene.phaseManager.getCurrentPhase() as MovePhase).cancel(); + (phaseManager.getCurrentPhase() as MovePhase).cancel(); } return true; @@ -881,24 +876,24 @@ export class InfatuatedTag extends BattlerTag { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); + const phaseManager = globalScene.phaseManager; + if (ret) { - globalScene.phaseManager.queueMessage( + phaseManager.queueMessage( i18next.t("battlerTags:infatuatedLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), sourcePokemonName: getPokemonNameWithAffix(globalScene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? }), ); - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT), - ); + phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT); if (pokemon.randBattleSeedInt(2)) { - globalScene.phaseManager.queueMessage( + phaseManager.queueMessage( i18next.t("battlerTags:infatuatedLapseImmobilize", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - (globalScene.phaseManager.getCurrentPhase() as MovePhase).cancel(); + (phaseManager.getCurrentPhase() as MovePhase).cancel(); } } @@ -965,26 +960,28 @@ export class SeedTag extends BattlerTag { applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); if (!cancelled.value) { - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED), + globalScene.phaseManager.unshiftNew( + "CommonAnimPhase", + source.getBattlerIndex(), + pokemon.getBattlerIndex(), + CommonAnim.LEECH_SEED, ); const damage = pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT }); const reverseDrain = pokemon.hasAbilityWithAttr(ReverseDrainAbAttr, false); - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - source.getBattlerIndex(), - !reverseDrain ? damage : damage * -1, - !reverseDrain - ? i18next.t("battlerTags:seededLapse", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - }) - : i18next.t("battlerTags:seededLapseShed", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - }), - false, - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + source.getBattlerIndex(), + !reverseDrain ? damage : damage * -1, + !reverseDrain + ? i18next.t("battlerTags:seededLapse", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + }) + : i18next.t("battlerTags:seededLapseShed", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + }), + false, + true, ); } } @@ -1039,9 +1036,9 @@ export class PowderTag extends BattlerTag { movePhase.fail(); movePhase.showMoveText(); - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.POWDER), - ); + const idx = pokemon.getBattlerIndex(); + + globalScene.phaseManager.unshiftNew("CommonAnimPhase", idx, idx, CommonAnim.POWDER); const cancelDamage = new BooleanHolder(false); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelDamage); @@ -1088,14 +1085,13 @@ export class NightmareTag extends BattlerTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) { - globalScene.phaseManager.queueMessage( + const phaseManager = globalScene.phaseManager; + phaseManager.queueMessage( i18next.t("battlerTags:nightmareLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE), - ); // TODO: Update animation type + phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE); // TODO: Update animation type const cancelled = new BooleanHolder(false); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); @@ -1194,7 +1190,7 @@ export class EncoreTag extends MoveRestrictionBattlerTag { const lastMove = pokemon.getLastXMoves(1)[0]; globalScene.phaseManager.tryReplacePhase( m => m.is("MovePhase") && m.pokemon === pokemon, - new MovePhase(pokemon, lastMove.targets ?? [], movesetMove), + globalScene.phaseManager.create("MovePhase", pokemon, lastMove.targets ?? [], movesetMove), ); } } @@ -1276,15 +1272,14 @@ export class IngrainTag extends TrappedTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) { - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - pokemon.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / 16), - i18next.t("battlerTags:ingrainLapse", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - }), - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / 16), + i18next.t("battlerTags:ingrainLapse", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + }), + true, ); } @@ -1315,8 +1310,12 @@ export class OctolockTag extends TrappedTag { const shouldLapse = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (shouldLapse) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), false, [Stat.DEF, Stat.SPDEF], -1), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + false, + [Stat.DEF, Stat.SPDEF], + -1, ); return true; } @@ -1344,16 +1343,15 @@ export class AquaRingTag extends BattlerTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) { - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - pokemon.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / 16), - i18next.t("battlerTags:aquaRingLapse", { - moveName: this.getMoveName(), - pokemonName: getPokemonNameWithAffix(pokemon), - }), - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / 16), + i18next.t("battlerTags:aquaRingLapse", { + moveName: this.getMoveName(), + pokemonName: getPokemonNameWithAffix(pokemon), + }), + true, ); } @@ -1445,13 +1443,14 @@ export abstract class DamagingTrapTag extends TrappedTag { const ret = super.lapse(pokemon, lapseType); if (ret) { - globalScene.phaseManager.queueMessage( + const phaseManager = globalScene.phaseManager; + phaseManager.queueMessage( i18next.t("battlerTags:damagingTrapLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: this.getMoveName(), }), ); - globalScene.phaseManager.unshiftPhase(new CommonAnimPhase(pokemon.getBattlerIndex(), undefined, this.commonAnim)); + phaseManager.unshiftNew("CommonAnimPhase", pokemon.getBattlerIndex(), undefined, this.commonAnim); const cancelled = new BooleanHolder(false); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); @@ -1764,8 +1763,12 @@ export class ContactStatStageChangeProtectedTag extends DamageProtectedTag { * @param user - The pokemon that is being attacked and has the tag */ override onContact(attacker: Pokemon, _user: Pokemon): void { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(attacker.getBattlerIndex(), false, [this.stat], this.levels), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + attacker.getBattlerIndex(), + false, + [this.stat], + this.levels, ); } } @@ -2281,8 +2284,11 @@ export class SaltCuredTag extends BattlerTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) { - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.SALT_CURE), + globalScene.phaseManager.unshiftNew( + "CommonAnimPhase", + pokemon.getBattlerIndex(), + pokemon.getBattlerIndex(), + CommonAnim.SALT_CURE, ); const cancelled = new BooleanHolder(false); @@ -2332,8 +2338,11 @@ export class CursedTag extends BattlerTag { const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType); if (ret) { - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.SALT_CURE), + globalScene.phaseManager.unshiftNew( + "CommonAnimPhase", + pokemon.getBattlerIndex(), + pokemon.getBattlerIndex(), + CommonAnim.SALT_CURE, ); const cancelled = new BooleanHolder(false); @@ -2509,13 +2518,12 @@ export class CommandedTag extends BattlerTag { /** Caches the Tatsugiri's form key and sharply boosts the tagged Pokemon's stats */ override onAdd(pokemon: Pokemon): void { this._tatsugiriFormKey = this.getSourcePokemon()?.getFormKey() ?? "curly"; - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase( - pokemon.getBattlerIndex(), - true, - [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], - 2, - ), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], + 2, ); } @@ -2592,17 +2600,16 @@ export class StockpilingTag extends BattlerTag { ); // Attempt to increase DEF and SPDEF by one stage, keeping track of successful changes. - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase( - pokemon.getBattlerIndex(), - true, - [Stat.SPDEF, Stat.DEF], - 1, - true, - false, - true, - this.onStatStagesChanged, - ), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [Stat.SPDEF, Stat.DEF], + 1, + true, + false, + true, + this.onStatStagesChanged, ); } } @@ -2620,14 +2627,28 @@ export class StockpilingTag extends BattlerTag { const spDefChange = this.statChangeCounts[Stat.SPDEF]; if (defChange) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.DEF], -defChange, true, false, true), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [Stat.DEF], + -defChange, + true, + false, + true, ); } if (spDefChange) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPDEF], -spDefChange, true, false, true), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [Stat.SPDEF], + -spDefChange, + true, + false, + true, ); } } @@ -2667,9 +2688,7 @@ export class GulpMissileTag extends BattlerTag { } if (this.tagType === BattlerTagType.GULP_MISSILE_ARROKUDA) { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(attacker.getBattlerIndex(), false, [Stat.DEF], -1), - ); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", attacker.getBattlerIndex(), false, [Stat.DEF], -1); } else { attacker.trySetStatus(StatusEffect.PARALYSIS, true, pokemon); } @@ -3290,8 +3309,15 @@ export class SyrupBombTag extends BattlerTag { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }), ); - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPD], -1, true, false, true), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [Stat.SPD], + -1, + true, + false, + true, ); return --this.turnCount > 0; } diff --git a/src/data/berry.ts b/src/data/berry.ts index defc9b85541..52ea1c44391 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -8,8 +8,6 @@ import i18next from "i18next"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; import { Stat, type BattleStat } from "#app/enums/stat"; -import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { globalScene } from "#app/global-scene"; export function getBerryName(berryType: BerryType): string { @@ -75,16 +73,15 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { { const hpHealed = new NumberHolder(toDmgValue(consumer.getMaxHp() / 4)); applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, hpHealed); - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - consumer.getBattlerIndex(), - hpHealed.value, - i18next.t("battle:hpHealBerry", { - pokemonNameWithAffix: getPokemonNameWithAffix(consumer), - berryName: getBerryName(berryType), - }), - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + consumer.getBattlerIndex(), + hpHealed.value, + i18next.t("battle:hpHealBerry", { + pokemonNameWithAffix: getPokemonNameWithAffix(consumer), + berryName: getBerryName(berryType), + }), + true, ); } break; @@ -109,8 +106,12 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { const stat: BattleStat = berryType - BerryType.ENIGMA; const statStages = new NumberHolder(1); applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, statStages); - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(consumer.getBattlerIndex(), true, [stat], statStages.value), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + consumer.getBattlerIndex(), + true, + [stat], + statStages.value, ); } break; @@ -126,8 +127,12 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { const randStat = randSeedInt(Stat.SPD, Stat.ATK); const stages = new NumberHolder(2); applyAbAttrs(DoubleBerryEffectAbAttr, consumer, null, false, stages); - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(consumer.getBattlerIndex(), true, [randStat], stages.value), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + consumer.getBattlerIndex(), + true, + [randStat], + stages.value, ); } break; diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index d44ecbed4cf..976c0cd7d97 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -1890,8 +1890,8 @@ export class HealAttr extends MoveEffectAttr { * This heals the target and shows the appropriate message. */ addHealPhase(target: Pokemon, healRatio: number) { - globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), - toDmgValue(target.getMaxHp() * healRatio), i18next.t("moveTriggers:healHp", { pokemonName: getPokemonNameWithAffix(target) }), true, !this.showAnim)); + globalScene.phaseManager.unshiftNew("PokemonHealPhase", target.getBattlerIndex(), + toDmgValue(target.getMaxHp() * healRatio), i18next.t("moveTriggers:healHp", { pokemonName: getPokemonNameWithAffix(target) }), true, !this.showAnim); } getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { @@ -2021,8 +2021,10 @@ export class SacrificialFullRestoreAttr extends SacrificialAttr { const party = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); const maxPartyMemberHp = party.map(p => p.getMaxHp()).reduce((maxHp: number, hp: number) => Math.max(hp, maxHp), 0); - globalScene.phaseManager.pushPhase( - new PokemonHealPhase( + const pm = globalScene.phaseManager; + + pm.pushPhase( + pm.create("PokemonHealPhase", user.getBattlerIndex(), maxPartyMemberHp, i18next.t(this.moveMessage, { pokemonName: getPokemonNameWithAffix(user) }), @@ -2233,7 +2235,7 @@ export class HitHealAttr extends MoveEffectAttr { message = ""; } } - globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(user.getBattlerIndex(), healAmount, message, false, true)); + globalScene.phaseManager.unshiftNew("PokemonHealPhase", user.getBattlerIndex(), healAmount, message, false, true); return true; } @@ -3067,7 +3069,7 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { if (!virtual) { overridden.value = true; - globalScene.phaseManager.unshiftPhase(new MoveAnimPhase(new MoveChargeAnim(this.chargeAnim, move.id, user))); + globalScene.phaseManager.unshiftNew("MoveAnimPhase", new MoveChargeAnim(this.chargeAnim, move.id, user)); globalScene.phaseManager.queueMessage(this.chargeText.replace("{TARGET}", getPokemonNameWithAffix(target)).replace("{USER}", getPokemonNameWithAffix(user))); user.pushMoveHistory({ move: move.id, targets: [ target.getBattlerIndex() ], result: MoveResult.OTHER }); const side = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; @@ -3125,7 +3127,7 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr { const allyMovePhaseIndex = globalScene.phaseManager.phaseQueue.indexOf(allyMovePhase); const firstMovePhaseIndex = globalScene.phaseManager.phaseQueue.findIndex((phase) => phase.is("MovePhase")); if (allyMovePhaseIndex !== firstMovePhaseIndex) { - globalScene.phaseManager.prependToPhase(globalScene.phaseManager.phaseQueue.splice(allyMovePhaseIndex, 1)[0], MovePhase); + globalScene.phaseManager.prependToPhase(globalScene.phaseManager.phaseQueue.splice(allyMovePhaseIndex, 1)[0], "MovePhase"); } overridden.value = true; @@ -3207,7 +3209,7 @@ export class StatStageChangeAttr extends MoveEffectAttr { const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); if (moveChance < 0 || moveChance === 100 || user.randBattleSeedInt(100) < moveChance) { const stages = this.getLevels(user); - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase((this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, stages, this.showMessage)); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", (this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, stages, this.showMessage); return true; } @@ -3432,7 +3434,7 @@ export class AcupressureStatStageChangeAttr extends MoveEffectAttr { const randStats = BATTLE_STATS.filter((s) => target.getStatStage(s) < 6); if (randStats.length > 0) { const boostStat = [ randStats[user.randBattleSeedInt(randStats.length)] ]; - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(target.getBattlerIndex(), this.selfTarget, boostStat, 2)); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", target.getBattlerIndex(), this.selfTarget, boostStat, 2); return true; } return false; @@ -3510,7 +3512,7 @@ export class OrderUpStatBoostAttr extends MoveEffectAttr { break; } - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(user.getBattlerIndex(), this.selfTarget, [ increasedStat ], 1)); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", user.getBattlerIndex(), this.selfTarget, [ increasedStat ], 1); return true; } } @@ -4227,8 +4229,8 @@ export class PresentPowerAttr extends VariablePowerAttr { // If this move is multi-hit, disable all other hits user.turnData.hitCount = 1; user.turnData.hitsLeft = 1; - globalScene.phaseManager.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), - toDmgValue(target.getMaxHp() / 4), i18next.t("moveTriggers:regainedHealth", { pokemonName: getPokemonNameWithAffix(target) }), true)); + globalScene.phaseManager.unshiftNew("PokemonHealPhase", target.getBattlerIndex(), + toDmgValue(target.getMaxHp() / 4), i18next.t("moveTriggers:regainedHealth", { pokemonName: getPokemonNameWithAffix(target) }), true); } return true; @@ -4488,7 +4490,7 @@ export class CueNextRoundAttr extends MoveEffectAttr { const nextRoundIndex = globalScene.phaseManager.phaseQueue.indexOf(nextRoundPhase); const nextMoveIndex = globalScene.phaseManager.phaseQueue.findIndex(phase => phase.is("MovePhase")); if (nextRoundIndex !== nextMoveIndex) { - globalScene.phaseManager.prependToPhase(globalScene.phaseManager.phaseQueue.splice(nextRoundIndex, 1)[0], MovePhase); + globalScene.phaseManager.prependToPhase(globalScene.phaseManager.phaseQueue.splice(nextRoundIndex, 1)[0], "MovePhase"); } // Mark the corresponding Pokemon as having "joined the Round" (for doubling power later) @@ -4546,7 +4548,7 @@ export class SpectralThiefAttr extends StatChangeBeforeDmgCalcAttr { */ const availableToSteal = Math.min(statStageValueTarget, 6 - statStageValueUser); - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(user.getBattlerIndex(), this.selfTarget, [ s ], availableToSteal)); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", user.getBattlerIndex(), this.selfTarget, [ s ], availableToSteal); target.setStatStage(s, statStageValueTarget - availableToSteal); } } @@ -5680,8 +5682,8 @@ export class CurseAttr extends MoveEffectAttr { target.addTag(BattlerTagType.CURSED, 0, move.id, user.id); return true; } else { - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(user.getBattlerIndex(), true, [ Stat.ATK, Stat.DEF ], 1)); - globalScene.phaseManager.unshiftPhase(new StatStageChangePhase(user.getBattlerIndex(), true, [ Stat.SPD ], -1)); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", user.getBattlerIndex(), true, [ Stat.ATK, Stat.DEF ], 1); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", user.getBattlerIndex(), true, [ Stat.SPD ], -1); return true; } } @@ -6154,7 +6156,7 @@ export class RevivalBlessingAttr extends MoveEffectAttr { override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { // If user is player, checks if the user has fainted pokemon if (user.isPlayer()) { - globalScene.phaseManager.unshiftPhase(new RevivalBlessingPhase(user)); + globalScene.phaseManager.unshiftNew("RevivalBlessingPhase", user); return true; } else if (user.isEnemy() && user.hasTrainer() && globalScene.getEnemyParty().findIndex((p) => p.isFainted() && !p.isBoss()) > -1) { // If used by an enemy trainer with at least one fainted non-boss Pokemon, this @@ -6177,7 +6179,7 @@ export class RevivalBlessingAttr extends MoveEffectAttr { if (user.fieldPosition === FieldPosition.CENTER) { user.setFieldPosition(FieldPosition.LEFT); } - globalScene.phaseManager.unshiftPhase(new SwitchSummonPhase(SwitchType.SWITCH, allyPokemon.getFieldIndex(), slotIndex, false, false)); + globalScene.phaseManager.unshiftNew("SwitchSummonPhase", SwitchType.SWITCH, allyPokemon.getFieldIndex(), slotIndex, false, false); } } return true; @@ -6255,26 +6257,23 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { if (this.switchType === SwitchType.FORCE_SWITCH) { switchOutTarget.leaveField(true); const slotIndex = eligibleNewIndices[user.randBattleSeedInt(eligibleNewIndices.length)]; - globalScene.phaseManager.prependToPhase( - new SwitchSummonPhase( - this.switchType, - switchOutTarget.getFieldIndex(), - slotIndex, - false, - true - ), - MoveEndPhase + globalScene.phaseManager.prependNewToPhase( + "MoveEndPhase", + "SwitchSummonPhase", + this.switchType, + switchOutTarget.getFieldIndex(), + slotIndex, + false, + true ); } else { switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); - globalScene.phaseManager.prependToPhase( - new SwitchPhase( + globalScene.phaseManager.prependNewToPhase("MoveEndPhase", + "SwitchPhase", this.switchType, switchOutTarget.getFieldIndex(), true, true - ), - MoveEndPhase ); return true; } @@ -6298,27 +6297,23 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { if (this.switchType === SwitchType.FORCE_SWITCH) { switchOutTarget.leaveField(true); const slotIndex = eligibleNewIndices[user.randBattleSeedInt(eligibleNewIndices.length)]; - globalScene.phaseManager.prependToPhase( - new SwitchSummonPhase( + globalScene.phaseManager.prependNewToPhase("MoveEndPhase", + "SwitchSummonPhase", this.switchType, switchOutTarget.getFieldIndex(), slotIndex, false, false - ), - MoveEndPhase ); } else { switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); - globalScene.phaseManager.prependToPhase( - new SwitchSummonPhase( - this.switchType, - switchOutTarget.getFieldIndex(), - (globalScene.currentBattle.trainer ? globalScene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0), - false, - false - ), - MoveEndPhase + globalScene.phaseManager.prependNewToPhase("MoveEndPhase", + "SwitchSummonPhase", + this.switchType, + switchOutTarget.getFieldIndex(), + (globalScene.currentBattle.trainer ? globalScene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0), + false, + false ); } } @@ -6351,13 +6346,13 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { globalScene.clearEnemyHeldItemModifiers(switchOutTarget); if (!allyPokemon?.isActive(true) && switchOutTarget.hp) { - globalScene.phaseManager.pushPhase(new BattleEndPhase(false)); + globalScene.phaseManager.pushNew("BattleEndPhase", false); if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { - globalScene.phaseManager.pushPhase(new SelectBiomePhase()); + globalScene.phaseManager.pushNew("SelectBiomePhase"); } - globalScene.phaseManager.pushPhase(new NewBattlePhase()); + globalScene.phaseManager.pushNew("NewBattlePhase"); } } @@ -6733,8 +6728,8 @@ class CallMoveAttr extends OverrideMoveEffectAttr { ? moveTargets.targets : [ this.hasTarget ? target.getBattlerIndex() : moveTargets.targets[user.randBattleSeedInt(moveTargets.targets.length)] ]; // account for Mirror Move having a target already user.getMoveQueue().push({ move: move.id, targets: targets, virtual: true, ignorePP: true }); - globalScene.phaseManager.unshiftPhase(new LoadMoveAnimPhase(move.id)); - globalScene.phaseManager.unshiftPhase(new MovePhase(user, targets, new PokemonMove(move.id, 0, 0, true), true, true)); + globalScene.phaseManager.unshiftNew("LoadMoveAnimPhase", move.id); + globalScene.phaseManager.unshiftNew("MovePhase", user, targets, new PokemonMove(move.id, 0, 0, true), true, true); return true; } } @@ -6962,8 +6957,8 @@ export class NaturePowerAttr extends OverrideMoveEffectAttr { } user.getMoveQueue().push({ move: moveId, targets: [ target.getBattlerIndex() ], ignorePP: true }); - globalScene.phaseManager.unshiftPhase(new LoadMoveAnimPhase(moveId)); - globalScene.phaseManager.unshiftPhase(new MovePhase(user, [ target.getBattlerIndex() ], new PokemonMove(moveId, 0, 0, true), true)); + globalScene.phaseManager.unshiftNew("LoadMoveAnimPhase", moveId); + globalScene.phaseManager.unshiftNew("MovePhase", user, [ target.getBattlerIndex() ], new PokemonMove(moveId, 0, 0, true), true); return true; } } @@ -7050,7 +7045,7 @@ export class RepeatMoveAttr extends MoveEffectAttr { })); target.getMoveQueue().unshift({ move: lastMove.move, targets: moveTargets, ignorePP: false }); target.turnData.extraTurns++; - globalScene.phaseManager.appendToPhase(new MovePhase(target, moveTargets, movesetMove), MoveEndPhase); + globalScene.phaseManager.appendNewToPhase("MoveEndPhase", "MovePhase", target, moveTargets, movesetMove); return true; } @@ -7550,7 +7545,7 @@ export class TransformAttr extends MoveEffectAttr { return false; } - globalScene.phaseManager.unshiftPhase(new PokemonTransformPhase(user.getBattlerIndex(), target.getBattlerIndex())); + globalScene.phaseManager.unshiftNew("PokemonTransformPhase", user.getBattlerIndex(), target.getBattlerIndex()); globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:transformedIntoTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); @@ -7852,7 +7847,7 @@ export class AfterYouAttr extends MoveEffectAttr { //Will find next acting phase of the targeted pokémon, delete it and queue it next on successful delete. const nextAttackPhase = globalScene.phaseManager.findPhase((phase) => phase.pokemon === target); if (nextAttackPhase && globalScene.phaseManager.tryRemovePhase((phase: MovePhase) => phase.pokemon === target)) { - globalScene.phaseManager.prependToPhase(new MovePhase(target, [ ...nextAttackPhase.targets ], nextAttackPhase.move), MovePhase); + globalScene.phaseManager.prependNewToPhase("MovePhase", "MovePhase", target, [ ...nextAttackPhase.targets ], nextAttackPhase.move); } return true; @@ -7889,7 +7884,7 @@ export class ForceLastAttr extends MoveEffectAttr { globalScene.phaseManager.phaseQueue.splice( globalScene.phaseManager.phaseQueue.indexOf(prependPhase), 0, - new MovePhase(target, [ ...targetMovePhase.targets ], targetMovePhase.move, false, false, false, true) + globalScene.phaseManager.create("MovePhase", target, [ ...targetMovePhase.targets ], targetMovePhase.move, false, false, false, true) ); } } diff --git a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts index 5f69090064f..11081892205 100644 --- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts +++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts @@ -19,7 +19,6 @@ import i18next from "i18next"; import type { IEggOptions } from "#app/data/egg"; import { EggSourceType } from "#enums/egg-source-types"; import { EggTier } from "#enums/egg-type"; -import { PartyHealPhase } from "#app/phases/party-heal-phase"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { modifierTypes } from "#app/modifier/modifier-type"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; @@ -182,7 +181,7 @@ export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder. async () => { const encounter = globalScene.currentBattle.mysteryEncounter!; // Full heal party - globalScene.phaseManager.unshiftPhase(new PartyHealPhase(true)); + globalScene.phaseManager.unshiftNew("PartyHealPhase", true); const eggOptions: IEggOptions = { pulled: false, diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index e23b5024599..82fbc0efd69 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -35,7 +35,6 @@ import { TrainerSlot } from "#enums/trainer-slot"; import { PokeballType } from "#enums/pokeball"; import type HeldModifierConfig from "#app/@types/held-modifier-config"; import type { BerryType } from "#enums/berry-type"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { Stat } from "#enums/stat"; import i18next from "i18next"; @@ -237,8 +236,12 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { queueEncounterMessage(`${namespace}:option.1.boss_enraged`); - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + statChangesForBattle, + 1, ); }, }, diff --git a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts index 87cd8f207c4..cdb98c56ed1 100644 --- a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts +++ b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts @@ -22,7 +22,6 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { speciesStarterCosts } from "#app/data/balance/starters"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import i18next from "i18next"; @@ -137,7 +136,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = MysteryEncounterB }) .withOptionPhase(async () => { // Give the player a Shiny Charm - globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.SHINY_CHARM)); + globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.SHINY_CHARM); leaveEncounterWithoutBattle(true); }) .build(), diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index 3de16c7086e..65ae3ea6c4f 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -35,7 +35,6 @@ import { BerryModifier } from "#app/modifier/modifier"; import i18next from "#app/plugins/i18n"; import { BerryType } from "#enums/berry-type"; import { PERMANENT_STATS, Stat } from "#enums/stat"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; /** the i18n namespace for the encounter */ @@ -237,8 +236,12 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder. config.pokemonConfigs![0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON]; config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { queueEncounterMessage(`${namespace}:option.2.boss_enraged`); - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + statChangesForBattle, + 1, ); }; setEncounterRewards( diff --git a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts index cd8289163eb..938a136bced 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -26,7 +26,6 @@ import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { PokemonMove } from "#app/field/pokemon"; import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { LearnMovePhase } from "#app/phases/learn-move-phase"; import { MoveId } from "#enums/move-id"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; @@ -766,8 +765,10 @@ function doBugTypeMoveTutor(): Promise { // Option select complete, handle if they are learning a move if (result && result.selectedOptionIndex < moveOptions.length) { - globalScene.phaseManager.unshiftPhase( - new LearnMovePhase(result.selectedPokemonIndex, moveOptions[result.selectedOptionIndex].moveId), + globalScene.phaseManager.unshiftNew( + "LearnMovePhase", + result.selectedPokemonIndex, + moveOptions[result.selectedOptionIndex].moveId, ); } diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index 66cd1ae0509..80465e1d20c 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -26,8 +26,6 @@ import type Pokemon from "#app/field/pokemon"; import { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { LearnMovePhase } from "#app/phases/learn-move-phase"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import PokemonData from "#app/system/pokemon-data"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { BattlerTagType } from "#enums/battler-tag-type"; @@ -176,13 +174,12 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { queueEncounterMessage(`${namespace}:option.1.boss_enraged`); - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase( - pokemon.getBattlerIndex(), - true, - [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF], - 1, - ), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF], + 1, ); }, }, @@ -245,8 +242,10 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder const onPokemonSelected = (pokemon: PlayerPokemon) => { encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender()); - globalScene.phaseManager.unshiftPhase( - new LearnMovePhase(globalScene.getPlayerParty().indexOf(pokemon), MoveId.REVELATION_DANCE), + globalScene.phaseManager.unshiftNew( + "LearnMovePhase", + globalScene.getPlayerParty().indexOf(pokemon), + MoveId.REVELATION_DANCE, ); // Play animation again to "learn" the dance diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index 1385e9c5a75..6474df3570e 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -16,7 +16,6 @@ import { } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; import type { PokemonHeldItemModifier } from "#app/modifier/modifier"; import { PokemonFormChangeItemModifier } from "#app/modifier/modifier"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; @@ -165,7 +164,7 @@ export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder.withE .withOptionPhase(async () => { // Give the player 5 Rogue Balls const encounter = globalScene.currentBattle.mysteryEncounter!; - globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.ROGUE_BALL)); + globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.ROGUE_BALL); // Start encounter with random legendary (7-10 starter strength) that has level additive // If this is a mono-type challenge, always ensure the required type is filtered for diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts index c321b5f9674..40893d93930 100644 --- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts +++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts @@ -29,7 +29,6 @@ import { } from "#app/modifier/modifier"; import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; import i18next from "#app/plugins/i18n"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { randSeedItem } from "#app/utils/common"; @@ -65,10 +64,10 @@ const doEventReward = () => { return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount()); }); if (candidates.length > 0) { - globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes[randSeedItem(candidates)])); + globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes[randSeedItem(candidates)]); } else { // At max stacks, give a Voucher instead - globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.VOUCHER)); + globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.VOUCHER); } } }; @@ -181,7 +180,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with ); doEventReward(); } else { - globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.AMULET_COIN)); + globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.AMULET_COIN); doEventReward(); } @@ -266,7 +265,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with ); doEventReward(); } else { - globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.CANDY_JAR)); + globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.CANDY_JAR); doEventReward(); } } else { @@ -288,7 +287,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with ); doEventReward(); } else { - globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.BERRY_POUCH)); + globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.BERRY_POUCH); doEventReward(); } } @@ -372,7 +371,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.with ); doEventReward(); } else { - globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierTypes.HEALING_CHARM)); + globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierTypes.HEALING_CHARM); doEventReward(); } diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index ced04ce224d..8b0e5a08020 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -44,7 +44,6 @@ import { EncounterAnim } from "#enums/encounter-anims"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { AbilityId } from "#enums/ability-id"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { Stat } from "#enums/stat"; import { Ability } from "#app/data/abilities/ability-class"; import { FIRE_RESISTANT_ABILITIES } from "#app/data/mystery-encounters/requirements/requirement-groups"; @@ -92,8 +91,12 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w gender: Gender.MALE, tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPDEF, Stat.SPD], 1), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [Stat.SPDEF, Stat.SPD], + 1, ); }, }, @@ -103,8 +106,12 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w gender: Gender.FEMALE, tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.SPDEF, Stat.SPD], 1), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [Stat.SPDEF, Stat.SPD], + 1, ); }, }, diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index 6df8ca8b167..83538e9e0e9 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -32,7 +32,6 @@ import PokemonData from "#app/system/pokemon-data"; import { BattlerTagType } from "#enums/battler-tag-type"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { randSeedInt } from "#app/utils/common"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; /** the i18n namespace for the encounter */ @@ -76,8 +75,12 @@ export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder. queueEncounterMessage(`${namespace}:option.1.stat_boost`); // Randomly boost 1 stat 2 stages // Cannot boost Spd, Acc, or Evasion - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [randSeedInt(4, 1)], 2), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [randSeedInt(4, 1)], + 2, ); }, }, diff --git a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts index 42bfe76d98e..a52641f857d 100644 --- a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts +++ b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts @@ -25,9 +25,7 @@ import { getPokemonNameWithAffix } from "#app/messages"; import { PlayerGender } from "#enums/player-gender"; import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball"; import { addPokeballOpenParticles } from "#app/field/anims"; -import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase"; import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms"; -import { PostSummonPhase } from "#app/phases/post-summon-phase"; import { modifierTypes } from "#app/modifier/modifier-type"; import { Nature } from "#enums/nature"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; @@ -411,13 +409,13 @@ function summonPlayerPokemonAnimation(pokemon: PlayerPokemon): Promise { pokemon.resetSummonData(); globalScene.time.delayedCall(1000, () => { if (pokemon.isShiny()) { - globalScene.phaseManager.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex())); + globalScene.phaseManager.unshiftNew("ShinySparklePhase", pokemon.getBattlerIndex()); } pokemon.resetTurnData(); globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true); - globalScene.phaseManager.pushPhase(new PostSummonPhase(pokemon.getBattlerIndex())); + globalScene.phaseManager.pushNew("PostSummonPhase", pokemon.getBattlerIndex()); resolve(); }); }, diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 42167d240f9..62029eb1847 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -17,7 +17,6 @@ import { import { getPokemonSpecies } from "#app/data/pokemon-species"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { ModifierTier } from "#app/modifier/modifier-tier"; -import { GameOverPhase } from "#app/phases/game-over-phase"; import { randSeedInt } from "#app/utils/common"; import { MoveId } from "#enums/move-id"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -190,7 +189,7 @@ export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilde if (allowedPokemon.length === 0) { // If there are no longer any legal pokemon in the party, game over. globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.unshiftNew("GameOverPhase"); } else { // Show which Pokemon was KOed, then start battle against Gimmighoul await transitionMysteryEncounterIntroVisuals(true, true, 500); diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 54e15dcb102..d324e9f9b6c 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -29,8 +29,6 @@ import { getEncounterText, showEncounterText } from "#app/data/mystery-encounter import { getPokemonNameWithAffix } from "#app/messages"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { ScanIvsPhase } from "#app/phases/scan-ivs-phase"; -import { SummonPhase } from "#app/phases/summon-phase"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups"; @@ -325,7 +323,7 @@ async function summonSafariPokemon() { encounter.misc.pokemon = pokemon; encounter.misc.safariPokemonRemaining -= 1; - globalScene.phaseManager.unshiftPhase(new SummonPhase(0, false)); + globalScene.phaseManager.unshiftNew("SummonPhase", 0, false); encounter.setDialogueToken("pokemonName", getPokemonNameWithAffix(pokemon)); @@ -336,7 +334,7 @@ async function summonSafariPokemon() { const ivScannerModifier = globalScene.findModifier(m => m instanceof IvScannerModifier); if (ivScannerModifier) { - globalScene.phaseManager.pushPhase(new ScanIvsPhase(pokemon.getBattlerIndex())); + globalScene.phaseManager.pushNew("ScanIvsPhase", pokemon.getBattlerIndex()); } } diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index a0898f7cfec..bb1529e3695 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -26,7 +26,6 @@ import { AiType, PokemonMove } from "#app/field/pokemon"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { PartyHealPhase } from "#app/phases/party-heal-phase"; import { BerryType } from "#enums/berry-type"; import { Stat } from "#enums/stat"; import { CustomPokemonData } from "#app/data/custom-pokemon-data"; @@ -155,7 +154,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil async () => { // Fall asleep waiting for Snorlax // Full heal party - globalScene.phaseManager.unshiftPhase(new PartyHealPhase(true)); + globalScene.phaseManager.unshiftNew("PartyHealPhase", true); queueEncounterMessage(`${namespace}:option.2.rest_result`); leaveEncounterWithoutBattle(); }, diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index b101837adb9..edc9cf13834 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -27,7 +27,6 @@ import { getPartyLuckValue, modifierTypes } from "#app/modifier/modifier-type"; import { TrainerSlot } from "#enums/trainer-slot"; import { BattlerTagType } from "#enums/battler-tag-type"; import { getPokemonNameWithAffix } from "#app/messages"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { Stat } from "#enums/stat"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { @@ -227,8 +226,12 @@ async function doBiomeTransitionDialogueAndBattleInit() { tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { queueEncounterMessage(`${namespace}:boss_enraged`); - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + statChangesForBattle, + 1, ); }, }, diff --git a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts index e2b6d62745b..be347fb0035 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -27,7 +27,6 @@ import { BerryType } from "#enums/berry-type"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { CustomPokemonData } from "#app/data/custom-pokemon-data"; import { Stat } from "#enums/stat"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; /** the i18n namespace for the encounter */ @@ -116,8 +115,12 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { queueEncounterMessage(`${namespace}:option.2.stat_boost`); - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.DEF, Stat.SPDEF], 1), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + [Stat.DEF, Stat.SPDEF], + 1, ); }, }, diff --git a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts index 05e036e9189..4b17260098f 100644 --- a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts @@ -27,9 +27,6 @@ import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms"; import { applyPostBattleInitAbAttrs, PostBattleInitAbAttr } from "#app/data/abilities/ability"; import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; -import { PartyHealPhase } from "#app/phases/party-heal-phase"; -import { ShowTrainerPhase } from "#app/phases/show-trainer-phase"; -import { ReturnPhase } from "#app/phases/return-phase"; import i18next from "i18next"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; @@ -143,7 +140,7 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter = MysteryEncounter }, async () => { // Refuse the challenge, they full heal the party and give the player a Rarer Candy - globalScene.phaseManager.unshiftPhase(new PartyHealPhase(true)); + globalScene.phaseManager.unshiftNew("PartyHealPhase", true); setEncounterRewards({ guaranteedModifierTypeFuncs: [modifierTypes.RARER_CANDY], fillRemaining: false, @@ -209,7 +206,7 @@ function endTrainerBattleAndShowDialogue(): Promise { for (const pokemon of playerField) { pokemon.lapseTag(BattlerTagType.COMMANDED); } - playerField.forEach((_, p) => globalScene.phaseManager.unshiftPhase(new ReturnPhase(p))); + playerField.forEach((_, p) => globalScene.phaseManager.unshiftNew("ReturnPhase", p)); for (const pokemon of globalScene.getPlayerParty()) { // Only trigger form change when Eiscue is in Noice form @@ -227,7 +224,7 @@ function endTrainerBattleAndShowDialogue(): Promise { applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon); } - globalScene.phaseManager.unshiftPhase(new ShowTrainerPhase()); + globalScene.phaseManager.unshiftNew("ShowTrainerPhase"); // Hide the trainer and init next battle const trainer = globalScene.currentBattle.trainer; // Unassign previous trainer from battle so it isn't destroyed before animation completes diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index e917574a065..411ecdec080 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -35,7 +35,6 @@ import { PokeballType } from "#enums/pokeball"; import { BattlerTagType } from "#enums/battler-tag-type"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { BerryModifier } from "#app/modifier/modifier"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { Stat } from "#enums/stat"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; @@ -103,8 +102,12 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder. tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON], mysteryEncounterBattleEffects: (pokemon: Pokemon) => { queueEncounterMessage(`${namespace}:option.1.stat_boost`); - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(pokemon.getBattlerIndex(), true, statChangesForBattle, 1), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + pokemon.getBattlerIndex(), + true, + statChangesForBattle, + 1, ); }, }, diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index c566ed68476..b86cbaa18c9 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -20,12 +20,6 @@ import { modifierTypes, regenerateModifierPoolThresholds, } from "#app/modifier/modifier-type"; -import { - MysteryEncounterBattlePhase, - MysteryEncounterBattleStartCleanupPhase, - MysteryEncounterPhase, - MysteryEncounterRewardsPhase, -} from "#app/phases/mystery-encounter-phases"; import type PokemonData from "#app/system/pokemon-data"; import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import type { PartyOption, PokemonSelectFilter } from "#app/ui/party-ui-handler"; @@ -51,13 +45,6 @@ import type { IEggOptions } from "#app/data/egg"; import { Egg } from "#app/data/egg"; import type { CustomPokemonData } from "#app/data/custom-pokemon-data"; import type HeldModifierConfig from "#app/@types/held-modifier-config"; -import { MovePhase } from "#app/phases/move-phase"; -import { EggLapsePhase } from "#app/phases/egg-lapse-phase"; -import { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase"; -import { BattleEndPhase } from "#app/phases/battle-end-phase"; -import { GameOverPhase } from "#app/phases/game-over-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { PartyExpPhase } from "#app/phases/party-exp-phase"; import type { Variant } from "#app/sprites/variant"; import { StatusEffect } from "#enums/status-effect"; import { globalScene } from "#app/global-scene"; @@ -428,7 +415,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig): console.log("Moveset:", moveset); }); - globalScene.phaseManager.pushPhase(new MysteryEncounterBattlePhase(partyConfig.disableSwitch)); + globalScene.phaseManager.pushNew("MysteryEncounterBattlePhase", partyConfig.disableSwitch); await Promise.all(loadEnemyAssets); battle.enemyParty.forEach((enemyPokemon_2, e_1) => { @@ -767,7 +754,7 @@ export function setEncounterRewards( } if (customShopRewards) { - globalScene.phaseManager.unshiftPhase(new SelectModifierPhase(0, undefined, customShopRewards)); + globalScene.phaseManager.unshiftNew("SelectModifierPhase", 0, undefined, customShopRewards); } else { globalScene.phaseManager.tryRemovePhase(p => p.is("MysteryEncounterRewardsPhase")); } @@ -807,7 +794,7 @@ export function setEncounterExp(participantId: number | number[], baseExpValue: const participantIds = Array.isArray(participantId) ? participantId : [participantId]; globalScene.currentBattle.mysteryEncounter!.doEncounterExp = () => { - globalScene.phaseManager.unshiftPhase(new PartyExpPhase(baseExpValue, useWaveIndex, new Set(participantIds))); + globalScene.phaseManager.unshiftNew("PartyExpPhase", baseExpValue, useWaveIndex, new Set(participantIds)); return true; }; @@ -829,7 +816,7 @@ export class OptionSelectSettings { * @param optionSelectSettings */ export function initSubsequentOptionSelect(optionSelectSettings: OptionSelectSettings) { - globalScene.phaseManager.pushPhase(new MysteryEncounterPhase(optionSelectSettings)); + globalScene.phaseManager.pushNew("MysteryEncounterPhase", optionSelectSettings); } /** @@ -858,7 +845,7 @@ export function handleMysteryEncounterVictory(addHealPhase = false, doNotContinu if (allowedPkm.length === 0) { globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.unshiftNew("GameOverPhase"); return; } @@ -869,8 +856,8 @@ export function handleMysteryEncounterVictory(addHealPhase = false, doNotContinu return; } if (encounter.encounterMode === MysteryEncounterMode.NO_BATTLE) { - globalScene.phaseManager.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase)); - globalScene.phaseManager.pushPhase(new EggLapsePhase()); + globalScene.phaseManager.pushNew("MysteryEncounterRewardsPhase", addHealPhase); + globalScene.phaseManager.pushNew("EggLapsePhase"); } else if ( !globalScene .getEnemyParty() @@ -878,15 +865,15 @@ export function handleMysteryEncounterVictory(addHealPhase = false, doNotContinu encounter.encounterMode !== MysteryEncounterMode.TRAINER_BATTLE ? p.isOnField() : !p?.isFainted(true), ) ) { - globalScene.phaseManager.pushPhase(new BattleEndPhase(true)); + globalScene.phaseManager.pushNew("BattleEndPhase", true); if (encounter.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) { - globalScene.phaseManager.pushPhase(new TrainerVictoryPhase()); + globalScene.phaseManager.pushNew("TrainerVictoryPhase"); } if (globalScene.gameMode.isEndless || !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) { - globalScene.phaseManager.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase)); + globalScene.phaseManager.pushNew("MysteryEncounterRewardsPhase", addHealPhase); if (!encounter.doContinueEncounter) { // Only lapse eggs once for multi-battle encounters - globalScene.phaseManager.pushPhase(new EggLapsePhase()); + globalScene.phaseManager.pushNew("EggLapsePhase"); } } } @@ -901,7 +888,7 @@ export function handleMysteryEncounterBattleFailed(addHealPhase = false, doNotCo if (allowedPkm.length === 0) { globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.unshiftNew("GameOverPhase"); return; } @@ -912,14 +899,14 @@ export function handleMysteryEncounterBattleFailed(addHealPhase = false, doNotCo return; } if (encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE) { - globalScene.phaseManager.pushPhase(new BattleEndPhase(false)); + globalScene.phaseManager.pushNew("BattleEndPhase", false); } - globalScene.phaseManager.pushPhase(new MysteryEncounterRewardsPhase(addHealPhase)); + globalScene.phaseManager.pushNew("MysteryEncounterRewardsPhase", addHealPhase); if (!encounter.doContinueEncounter) { // Only lapse eggs once for multi-battle encounters - globalScene.phaseManager.pushPhase(new EggLapsePhase()); + globalScene.phaseManager.pushNew("EggLapsePhase"); } } @@ -1004,14 +991,19 @@ export function handleMysteryEncounterBattleStartEffects() { } else { source = globalScene.getEnemyField()[0]; } - globalScene.phaseManager.pushPhase( - // @ts-ignore: source cannot be undefined - new MovePhase(source, effect.targets, effect.move, effect.followUp, effect.ignorePp), + globalScene.phaseManager.pushNew( + "MovePhase", + // @ts-expect-error: source is guaranteed to be defined + source, + effect.targets, + effect.move, + effect.followUp, + effect.ignorePp, ); }); // Pseudo turn end phase to reset flinch states, Endure, etc. - globalScene.phaseManager.pushPhase(new MysteryEncounterBattleStartCleanupPhase()); + globalScene.phaseManager.pushNew("MysteryEncounterBattleStartCleanupPhase"); encounter.startOfBattleEffectsComplete = true; } diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index 757d8173820..e8a3db46cff 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -32,7 +32,6 @@ import type { PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type"; import { Gender } from "#app/data/gender"; import type { PermanentStat } from "#enums/stat"; -import { VictoryPhase } from "#app/phases/victory-phase"; import { SummaryUiMode } from "#app/ui/summary-ui-handler"; import { CustomPokemonData } from "#app/data/custom-pokemon-data"; import type { AbilityId } from "#enums/ability-id"; @@ -675,7 +674,7 @@ export async function catchPokemon( if (!globalScene.getEnemyParty().some(p => p.id === pokemon.id)) { globalScene.getEnemyParty().push(pokemon); } - globalScene.phaseManager.unshiftPhase(new VictoryPhase(pokemon.id, true)); + globalScene.phaseManager.unshiftNew("VictoryPhase", pokemon.id, true); globalScene.pokemonInfoContainer.hide(); if (pokeball) { removePb(pokeball); diff --git a/src/field/arena.ts b/src/field/arena.ts index 1c54382c89b..82a8afbedad 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -38,7 +38,6 @@ import { TimeOfDay } from "#enums/time-of-day"; import { TrainerType } from "#enums/trainer-type"; import { AbilityId } from "#enums/ability-id"; import { SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "#app/data/pokemon-forms"; -import { CommonAnimPhase } from "#app/phases/common-anim-phase"; import { WeatherType } from "#enums/weather-type"; import { FieldEffectModifier } from "#app/modifier/modifier"; @@ -297,7 +296,7 @@ export class Arena { */ trySetWeatherOverride(weather: WeatherType): boolean { this.weather = new Weather(weather, 0); - globalScene.phaseManager.unshiftPhase(new CommonAnimPhase(undefined, undefined, CommonAnim.SUNNY + (weather - 1))); + globalScene.phaseManager.unshiftNew("CommonAnimPhase", undefined, undefined, CommonAnim.SUNNY + (weather - 1)); globalScene.phaseManager.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct? return true; } @@ -328,8 +327,12 @@ export class Arena { this.weather?.isImmutable() && ![WeatherType.HARSH_SUN, WeatherType.HEAVY_RAIN, WeatherType.STRONG_WINDS, WeatherType.NONE].includes(weather) ) { - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(undefined, undefined, CommonAnim.SUNNY + (oldWeatherType - 1), true), + globalScene.phaseManager.unshiftNew( + "CommonAnimPhase", + undefined, + undefined, + CommonAnim.SUNNY + (oldWeatherType - 1), + true, ); globalScene.phaseManager.queueMessage(getLegendaryWeatherContinuesMessage(oldWeatherType)!); return false; @@ -348,8 +351,12 @@ export class Arena { ); // TODO: is this bang correct? if (this.weather) { - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(undefined, undefined, CommonAnim.SUNNY + (weather - 1), true), + globalScene.phaseManager.unshiftNew( + "CommonAnimPhase", + undefined, + undefined, + CommonAnim.SUNNY + (weather - 1), + true, ); globalScene.phaseManager.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct? } else { @@ -433,8 +440,11 @@ export class Arena { if (this.terrain) { if (!ignoreAnim) { - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(undefined, undefined, CommonAnim.MISTY_TERRAIN + (terrain - 1)), + globalScene.phaseManager.unshiftNew( + "CommonAnimPhase", + undefined, + undefined, + CommonAnim.MISTY_TERRAIN + (terrain - 1), ); } globalScene.phaseManager.queueMessage(getTerrainStartMessage(terrain)!); // TODO: is this bang correct? diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 5b38419e708..71b21076ae6 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -230,13 +230,6 @@ import { BiomeId } from "#enums/biome-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { getPokemonNameWithAffix } from "#app/messages"; -import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; -import { FaintPhase } from "#app/phases/faint-phase"; -import { LearnMovePhase } from "#app/phases/learn-move-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; -import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase"; -import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; -import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; import { Challenges } from "#enums/challenges"; import { PokemonAnimType } from "#enums/pokemon-anim-type"; import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; @@ -256,7 +249,6 @@ import { doShinySparkleAnim } from "#app/field/anims"; import { MoveFlags } from "#enums/MoveFlags"; import { timedEventManager } from "#app/global-event-manager"; import { loadMoveAnimations } from "#app/sprites/pokemon-asset-loader"; -import { ResetStatusPhase } from "#app/phases/reset-status-phase"; export enum LearnMoveSituation { MISC, @@ -4013,7 +4005,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * Once the MoveEffectPhase is over (and calls it's .end() function, shiftPhase() will reset the PhaseQueueSplice via clearPhaseQueueSplice() ) */ globalScene.phaseManager.setPhaseQueueSplice(); - globalScene.phaseManager.unshiftPhase(new FaintPhase(this.getBattlerIndex(), preventEndure)); + globalScene.phaseManager.unshiftNew("FaintPhase", this.getBattlerIndex(), preventEndure); this.destroySubstitute(); this.lapseTag(BattlerTagType.COMMANDED); } @@ -4049,7 +4041,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } = {}, ): number { const isIndirectDamage = [HitResult.INDIRECT, HitResult.INDIRECT_KO].includes(result); - const damagePhase = new DamageAnimPhase(this.getBattlerIndex(), damage, result as DamageResult, isCritical); + const damagePhase = globalScene.phaseManager.create( + "DamageAnimPhase", + this.getBattlerIndex(), + damage, + result as DamageResult, + isCritical, + ); globalScene.phaseManager.unshiftPhase(damagePhase); if (this.switchOutStatus && source) { damage = 0; @@ -4778,8 +4776,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (overrideStatus) { this.resetStatus(false); } - globalScene.phaseManager.unshiftPhase( - new ObtainStatusEffectPhase(this.getBattlerIndex(), effect, turnsRemaining, sourceText, sourcePokemon), + globalScene.phaseManager.unshiftNew( + "ObtainStatusEffectPhase", + this.getBattlerIndex(), + effect, + turnsRemaining, + sourceText, + sourcePokemon, ); return true; } @@ -4828,7 +4831,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (asPhase) { - globalScene.phaseManager.unshiftPhase(new ResetStatusPhase(this, confusion, reloadAssets)); + globalScene.phaseManager.unshiftNew("ResetStatusPhase", this, confusion, reloadAssets); } else { this.clearStatus(confusion, reloadAssets); } @@ -5635,9 +5638,13 @@ export class PlayerPokemon extends Pokemon { this.getFieldIndex(), (slotIndex: number, _option: PartyOption) => { if (slotIndex >= globalScene.currentBattle.getBattlerCount() && slotIndex < 6) { - globalScene.phaseManager.prependToPhase( - new SwitchSummonPhase(switchType, this.getFieldIndex(), slotIndex, false), - MoveEndPhase, + globalScene.phaseManager.prependNewToPhase( + "MoveEndPhase", + "SwitchSummonPhase", + switchType, + this.getFieldIndex(), + slotIndex, + false, ); } globalScene.ui.setMode(UiMode.MESSAGE).then(resolve); @@ -6001,7 +6008,7 @@ export class PlayerPokemon extends Pokemon { pokemon .getMoveset(true) .map((m: PokemonMove) => - globalScene.phaseManager.unshiftPhase(new LearnMovePhase(newPartyMemberIndex, m.getMove().id)), + globalScene.phaseManager.unshiftNew("LearnMovePhase", newPartyMemberIndex, m.getMove().id), ); pokemon.destroy(); this.updateFusionPalette(); @@ -6644,8 +6651,14 @@ export class EnemyPokemon extends Pokemon { stages++; } - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase(this.getBattlerIndex(), true, [boostedStat!], stages, true, true), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + this.getBattlerIndex(), + true, + [boostedStat!], + stages, + true, + true, ); this.bossSegmentIndex--; } diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 81d9bf7189c..b6f96db751a 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -8,10 +8,7 @@ import { getStatusEffectHealText } from "#app/data/status-effect"; import Pokemon, { type PlayerPokemon } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import Overrides from "#app/overrides"; -import { EvolutionPhase } from "#app/phases/evolution-phase"; -import { LearnMovePhase, LearnMoveType } from "#app/phases/learn-move-phase"; -import { LevelUpPhase } from "#app/phases/level-up-phase"; -import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; +import { LearnMoveType } from "#app/phases/learn-move-phase"; import type { VoucherType } from "#app/system/voucher"; import { Command } from "#app/ui/command-ui-handler"; import { addTextObject, TextStyle } from "#app/ui/text"; @@ -1684,16 +1681,15 @@ export class TurnHealModifier extends PokemonHeldItemModifier { */ override apply(pokemon: Pokemon): boolean { if (!pokemon.isFullHp()) { - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - pokemon.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / 16) * this.stackCount, - i18next.t("modifier:turnHealApply", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - typeName: this.type.name, - }), - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / 16) * this.stackCount, + i18next.t("modifier:turnHealApply", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + typeName: this.type.name, + }), + true, ); return true; } @@ -1782,16 +1778,15 @@ export class HitHealModifier extends PokemonHeldItemModifier { override apply(pokemon: Pokemon): boolean { if (pokemon.turnData.totalDamageDealt && !pokemon.isFullHp()) { // TODO: this shouldn't be undefined AFAIK - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - pokemon.getBattlerIndex(), - toDmgValue(pokemon.turnData.totalDamageDealt / 8) * this.stackCount, - i18next.t("modifier:hitHealApply", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - typeName: this.type.name, - }), - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + pokemon.getBattlerIndex(), + toDmgValue(pokemon.turnData.totalDamageDealt / 8) * this.stackCount, + i18next.t("modifier:hitHealApply", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + typeName: this.type.name, + }), + true, ); } @@ -1950,18 +1945,17 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier { */ override apply(pokemon: Pokemon): boolean { // Restore the Pokemon to half HP - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - pokemon.getBattlerIndex(), - toDmgValue(pokemon.getMaxHp() / 2), - i18next.t("modifier:pokemonInstantReviveApply", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - typeName: this.type.name, - }), - false, - false, - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / 2), + i18next.t("modifier:pokemonInstantReviveApply", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + typeName: this.type.name, + }), + false, + false, + true, ); // Remove the Pokemon's FAINT status @@ -2323,12 +2317,11 @@ export class PokemonLevelIncrementModifier extends ConsumablePokemonModifier { playerPokemon.addFriendship(FRIENDSHIP_GAIN_FROM_RARE_CANDY); - globalScene.phaseManager.unshiftPhase( - new LevelUpPhase( - globalScene.getPlayerParty().indexOf(playerPokemon), - playerPokemon.level - levelCount.value, - playerPokemon.level, - ), + globalScene.phaseManager.unshiftNew( + "LevelUpPhase", + globalScene.getPlayerParty().indexOf(playerPokemon), + playerPokemon.level - levelCount.value, + playerPokemon.level, ); return true; @@ -2344,8 +2337,11 @@ export class TmModifier extends ConsumablePokemonModifier { * @returns always `true` */ override apply(playerPokemon: PlayerPokemon): boolean { - globalScene.phaseManager.unshiftPhase( - new LearnMovePhase(globalScene.getPlayerParty().indexOf(playerPokemon), this.type.moveId, LearnMoveType.TM), + globalScene.phaseManager.unshiftNew( + "LearnMovePhase", + globalScene.getPlayerParty().indexOf(playerPokemon), + this.type.moveId, + LearnMoveType.TM, ); return true; @@ -2367,13 +2363,12 @@ export class RememberMoveModifier extends ConsumablePokemonModifier { * @returns always `true` */ override apply(playerPokemon: PlayerPokemon, cost?: number): boolean { - globalScene.phaseManager.unshiftPhase( - new LearnMovePhase( - globalScene.getPlayerParty().indexOf(playerPokemon), - playerPokemon.getLearnableLevelMoves()[this.levelMoveIndex], - LearnMoveType.MEMORY, - cost, - ), + globalScene.phaseManager.unshiftNew( + "LearnMovePhase", + globalScene.getPlayerParty().indexOf(playerPokemon), + playerPokemon.getLearnableLevelMoves()[this.levelMoveIndex], + LearnMoveType.MEMORY, + cost, ); return true; @@ -2410,9 +2405,7 @@ export class EvolutionItemModifier extends ConsumablePokemonModifier { } if (matchingEvolution) { - globalScene.phaseManager.unshiftPhase( - new EvolutionPhase(playerPokemon, matchingEvolution, playerPokemon.level - 1), - ); + globalScene.phaseManager.unshiftNew("EvolutionPhase", playerPokemon, matchingEvolution, playerPokemon.level - 1); return true; } @@ -3574,19 +3567,18 @@ export class EnemyTurnHealModifier extends EnemyPersistentModifier { */ override apply(enemyPokemon: Pokemon): boolean { if (!enemyPokemon.isFullHp()) { - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - enemyPokemon.getBattlerIndex(), - Math.max(Math.floor(enemyPokemon.getMaxHp() / (100 / this.healPercent)) * this.stackCount, 1), - i18next.t("modifier:enemyTurnHealApply", { - pokemonNameWithAffix: getPokemonNameWithAffix(enemyPokemon), - }), - true, - false, - false, - false, - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + enemyPokemon.getBattlerIndex(), + Math.max(Math.floor(enemyPokemon.getMaxHp() / (100 / this.healPercent)) * this.stackCount, 1), + i18next.t("modifier:enemyTurnHealApply", { + pokemonNameWithAffix: getPokemonNameWithAffix(enemyPokemon), + }), + true, + false, + false, + false, + true, ); return true; } diff --git a/src/phase-manager.ts b/src/phase-manager.ts index 8869f4b27b7..230e0331caf 100644 --- a/src/phase-manager.ts +++ b/src/phase-manager.ts @@ -1,11 +1,100 @@ -import { HideAbilityPhase } from "./phases/hide-ability-phase"; -import { ShowAbilityPhase } from "./phases/show-ability-phase"; -import { TurnInitPhase } from "./phases/turn-init-phase"; import type { Phase } from "#app/phase"; import type { default as Pokemon } from "#app/field/pokemon"; -import type { Constructor } from "#app/utils/common"; -import { MessagePhase } from "./phases/message-phase"; +import type { PhaseMap, PhaseString } from "./@types/phase-types"; import { globalScene } from "#app/global-scene"; +import { AddEnemyBuffModifierPhase } from "#app/phases/add-enemy-buff-modifier-phase"; +import { AttemptCapturePhase } from "#app/phases/attempt-capture-phase"; +import { AttemptRunPhase } from "#app/phases/attempt-run-phase"; +import { BattleEndPhase } from "#app/phases/battle-end-phase"; +import { BerryPhase } from "#app/phases/berry-phase"; +import { CheckStatusEffectPhase } from "#app/phases/check-status-effect-phase"; +import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; +import { CommandPhase } from "#app/phases/command-phase"; +import { CommonAnimPhase } from "#app/phases/common-anim-phase"; +import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; +import { EggHatchPhase } from "#app/phases/egg-hatch-phase"; +import { EggLapsePhase } from "#app/phases/egg-lapse-phase"; +import { EggSummaryPhase } from "#app/phases/egg-summary-phase"; +import { EncounterPhase } from "#app/phases/encounter-phase"; +import { EndCardPhase } from "#app/phases/end-card-phase"; +import { EndEvolutionPhase } from "#app/phases/end-evolution-phase"; +import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; +import { EvolutionPhase } from "#app/phases/evolution-phase"; +import { ExpPhase } from "#app/phases/exp-phase"; +import { FaintPhase } from "#app/phases/faint-phase"; +import { FormChangePhase } from "#app/phases/form-change-phase"; +import { GameOverModifierRewardPhase } from "#app/phases/game-over-modifier-reward-phase"; +import { GameOverPhase } from "#app/phases/game-over-phase"; +import { HideAbilityPhase } from "#app/phases/hide-ability-phase"; +import { HidePartyExpBarPhase } from "#app/phases/hide-party-exp-bar-phase"; +import { LearnMovePhase } from "#app/phases/learn-move-phase"; +import { LevelCapPhase } from "#app/phases/level-cap-phase"; +import { LevelUpPhase } from "#app/phases/level-up-phase"; +import { LoadMoveAnimPhase } from "#app/phases/load-move-anim-phase"; +import { LoginPhase } from "#app/phases/login-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; +import { MoneyRewardPhase } from "#app/phases/money-reward-phase"; +import { MoveAnimPhase } from "#app/phases/move-anim-phase"; +import { MoveChargePhase } from "#app/phases/move-charge-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { MoveHeaderPhase } from "#app/phases/move-header-phase"; +import { MovePhase } from "#app/phases/move-phase"; +import { + MysteryEncounterPhase, + MysteryEncounterOptionSelectedPhase, + MysteryEncounterBattlePhase, + MysteryEncounterRewardsPhase, + PostMysteryEncounterPhase, + MysteryEncounterBattleStartCleanupPhase, +} from "#app/phases/mystery-encounter-phases"; +import { NewBattlePhase } from "#app/phases/new-battle-phase"; +import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase"; +import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; +import { ObtainStatusEffectPhase } from "#app/phases/obtain-status-effect-phase"; +import { PartyExpPhase } from "#app/phases/party-exp-phase"; +import { PartyHealPhase } from "#app/phases/party-heal-phase"; +import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase"; +import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; +import { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase"; +import { PostGameOverPhase } from "#app/phases/post-game-over-phase"; +import { PostSummonPhase } from "#app/phases/post-summon-phase"; +import { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase"; +import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; +import { ReloadSessionPhase } from "#app/phases/reload-session-phase"; +import { ResetStatusPhase } from "#app/phases/reset-status-phase"; +import { ReturnPhase } from "#app/phases/return-phase"; +import { RevivalBlessingPhase } from "#app/phases/revival-blessing-phase"; +import { RibbonModifierRewardPhase } from "#app/phases/ribbon-modifier-reward-phase"; +import { ScanIvsPhase } from "#app/phases/scan-ivs-phase"; +import { SelectBiomePhase } from "#app/phases/select-biome-phase"; +import { SelectChallengePhase } from "#app/phases/select-challenge-phase"; +import { SelectGenderPhase } from "#app/phases/select-gender-phase"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import { SelectStarterPhase } from "#app/phases/select-starter-phase"; +import { SelectTargetPhase } from "#app/phases/select-target-phase"; +import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase"; +import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; +import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase"; +import { ShowTrainerPhase } from "#app/phases/show-trainer-phase"; +import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; +import { SummonMissingPhase } from "#app/phases/summon-missing-phase"; +import { SummonPhase } from "#app/phases/summon-phase"; +import { SwitchBiomePhase } from "#app/phases/switch-biome-phase"; +import { SwitchPhase } from "#app/phases/switch-phase"; +import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; +import { TeraPhase } from "#app/phases/tera-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; +import { TrainerVictoryPhase } from "#app/phases/trainer-victory-phase"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { TurnStartPhase } from "#app/phases/turn-start-phase"; +import { UnavailablePhase } from "#app/phases/unavailable-phase"; +import { UnlockPhase } from "#app/phases/unlock-phase"; +import { VictoryPhase } from "#app/phases/victory-phase"; +import { WeatherEffectPhase } from "#app/phases/weather-effect-phase"; /** * Manager for phases used by battle scene. @@ -13,6 +102,115 @@ import { globalScene } from "#app/global-scene"; * *This file must not be imported or used directly. The manager is exclusively used by the battle scene and is not intended for external use.* */ +/** + * Object that holds all of the phase constructors. + * This is used to create new phases dynamically using the `newPhase` method in the `PhaseManager`. + * + * @remarks + * The keys of this object are the names of the phases, and the values are the constructors of the phases. + * This allows for easy creation of new phases without needing to import each phase individually. + */ +const PHASES = Object.freeze({ + AddEnemyBuffModifierPhase, + AttemptCapturePhase, + AttemptRunPhase, + BattleEndPhase, + BerryPhase, + CheckStatusEffectPhase, + CheckSwitchPhase, + CommandPhase, + CommonAnimPhase, + DamageAnimPhase, + EggHatchPhase, + EggLapsePhase, + EggSummaryPhase, + EncounterPhase, + EndCardPhase, + EndEvolutionPhase, + EnemyCommandPhase, + EvolutionPhase, + ExpPhase, + FaintPhase, + FormChangePhase, + GameOverPhase, + GameOverModifierRewardPhase, + HideAbilityPhase, + HidePartyExpBarPhase, + LearnMovePhase, + LevelCapPhase, + LevelUpPhase, + LoadMoveAnimPhase, + LoginPhase, + MessagePhase, + ModifierRewardPhase, + MoneyRewardPhase, + MoveAnimPhase, + MoveChargePhase, + MoveEffectPhase, + MoveEndPhase, + MoveHeaderPhase, + MovePhase, + MysteryEncounterPhase, + MysteryEncounterOptionSelectedPhase, + MysteryEncounterBattlePhase, + MysteryEncounterBattleStartCleanupPhase, + MysteryEncounterRewardsPhase, + PostMysteryEncounterPhase, + NewBattlePhase, + NewBiomeEncounterPhase, + NextEncounterPhase, + ObtainStatusEffectPhase, + PartyExpPhase, + PartyHealPhase, + PokemonAnimPhase, + PokemonHealPhase, + PokemonTransformPhase, + PostGameOverPhase, + PostSummonPhase, + PostTurnStatusEffectPhase, + QuietFormChangePhase, + ReloadSessionPhase, + ResetStatusPhase, + ReturnPhase, + RevivalBlessingPhase, + RibbonModifierRewardPhase, + ScanIvsPhase, + SelectBiomePhase, + SelectChallengePhase, + SelectGenderPhase, + SelectModifierPhase, + SelectStarterPhase, + SelectTargetPhase, + ShinySparklePhase, + ShowAbilityPhase, + ShowPartyExpBarPhase, + ShowTrainerPhase, + StatStageChangePhase, + SummonMissingPhase, + SummonPhase, + SwitchBiomePhase, + SwitchPhase, + SwitchSummonPhase, + TeraPhase, + TitlePhase, + ToggleDoublePositionPhase, + TrainerVictoryPhase, + TurnEndPhase, + TurnInitPhase, + TurnStartPhase, + UnavailablePhase, + UnlockPhase, + VictoryPhase, + WeatherEffectPhase, +}); + +// This type export cannot be moved to `@types`, as `Phases` is intentionally private to this file +/** Maps Phase strings to their constructors */ +export type PhaseConstructorMap = typeof PHASES; + +/** + * PhaseManager is responsible for managing the phases in the battle scene + */ export class PhaseManager { /** PhaseQueue: dequeue/remove the first element to get the next phase */ public phaseQueue: Phase[] = []; @@ -213,15 +411,16 @@ export class PhaseManager { /** * Tries to add the input phase to index before target phase in the phaseQueue, else simply calls unshiftPhase() - * @param phase {@linkcode Phase} the phase to be added - * @param targetPhase {@linkcode Phase} the type of phase to search for in phaseQueue + * @param phase - The phase to be added + * @param targetPhase - The phase to search for in phaseQueue * @returns boolean if a targetPhase was found and added */ - prependToPhase(phase: Phase | Phase[], targetPhase: Constructor): boolean { + prependToPhase(phase: Phase | Phase[], targetPhase: PhaseString): boolean { if (!Array.isArray(phase)) { phase = [phase]; } - const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof targetPhase); + const target = PHASES[targetPhase]; + const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target); if (targetIndex !== -1) { this.phaseQueue.splice(targetIndex, 0, ...phase); @@ -234,14 +433,15 @@ export class PhaseManager { /** * Attempt to add the input phase(s) to index after target phase in the {@linkcode phaseQueue}, else simply calls {@linkcode unshiftPhase()} * @param phase - The phase(s) to be added - * @param targetPhase - The type of phase to search for in {@linkcode phaseQueue} + * @param targetPhase - The phase to search for in phaseQueue * @returns `true` if a `targetPhase` was found to append to */ - appendToPhase(phase: Phase | Phase[], targetPhase: Constructor): boolean { + appendToPhase(phase: Phase | Phase[], targetPhase: PhaseString): boolean { if (!Array.isArray(phase)) { phase = [phase]; } - const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof targetPhase); + const target = PHASES[targetPhase]; + const targetIndex = this.phaseQueue.findIndex(ph => ph instanceof target); if (targetIndex !== -1 && this.phaseQueue.length > targetIndex) { this.phaseQueue.splice(targetIndex + 1, 0, ...phase); @@ -308,4 +508,74 @@ export class PhaseManager { } this.phaseQueue.push(new TurnInitPhase()); } + + /** + * Dynamically create the named phase from the provided arguments + * + * @remarks + * Used to avoid importing each phase individually, allowing for dynamic creation of phases. + * @param phase - The name of the phase to create. + * @param args - The arguments to pass to the phase constructor. + * @returns The requested phase instance + */ + public create(phase: T, ...args: ConstructorParameters): PhaseMap[T] { + const PhaseClass = PHASES[phase]; + + if (!PhaseClass) { + throw new Error(`Phase ${phase} does not exist in PhaseMap.`); + } + + // @ts-expect-error: Typescript does not support narrowing the type of operands in generic methods (see https://stackoverflow.com/a/72891234) + return new PhaseClass(...args); + } + + /** + * Create a new phase and immediately push it to the phase queue. Equivalent to calling {@linkcode create} followed by {@linkcode pushPhase}. + * @param phase - The name of the phase to create + * @param args - The arguments to pass to the phase constructor + */ + public pushNew(phase: T, ...args: ConstructorParameters): void { + this.pushPhase(this.create(phase, ...args)); + } + + /** + * Create a new phase and immediately unshift it to the phase queue. Equivalent to calling {@linkcode create} followed by {@linkcode unshiftPhase}. + * @param phase - The name of the phase to create + * @param args - The arguments to pass to the phase constructor + */ + public unshiftNew(phase: T, ...args: ConstructorParameters): void { + this.unshiftPhase(this.create(phase, ...args)); + } + + /** + * Create a new phase and immediately prepend it to an existing phase in the phase queue. + * Equivalent to calling {@linkcode create} followed by {@linkcode prependToPhase}. + * @param targetPhase - The phase to search for in phaseQueue + * @param phase - The name of the phase to create + * @param args - The arguments to pass to the phase constructor + * @returns `true` if a `targetPhase` was found to prepend to + */ + public prependNewToPhase( + targetPhase: PhaseString, + phase: T, + ...args: ConstructorParameters + ): boolean { + return this.prependToPhase(this.create(phase, ...args), targetPhase); + } + + /** + * Create a new phase and immediately append it to an existing phase the phase queue. + * Equivalent to calling {@linkcode create} followed by {@linkcode appendToPhase}. + * @param targetPhase - The phase to search for in phaseQueue + * @param phase - The name of the phase to create + * @param args - The arguments to pass to the phase constructor + * @returns `true` if a `targetPhase` was found to append to + */ + public appendNewToPhase( + targetPhase: PhaseString, + phase: T, + ...args: ConstructorParameters + ): boolean { + return this.appendToPhase(this.create(phase, ...args), targetPhase); + } } diff --git a/src/phases/attempt-capture-phase.ts b/src/phases/attempt-capture-phase.ts index 09eeb6e0a19..4f3f54a7e5b 100644 --- a/src/phases/attempt-capture-phase.ts +++ b/src/phases/attempt-capture-phase.ts @@ -14,7 +14,6 @@ import type { EnemyPokemon } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { PokemonHeldItemModifier } from "#app/modifier/modifier"; import { PokemonPhase } from "#app/phases/pokemon-phase"; -import { VictoryPhase } from "#app/phases/victory-phase"; import { achvs } from "#app/system/achv"; import type { PartyOption } from "#app/ui/party-ui-handler"; import { PartyUiMode } from "#app/ui/party-ui-handler"; @@ -257,7 +256,7 @@ export class AttemptCapturePhase extends PokemonPhase { null, () => { const end = () => { - globalScene.phaseManager.unshiftPhase(new VictoryPhase(this.battlerIndex)); + globalScene.phaseManager.unshiftNew("VictoryPhase", this.battlerIndex); globalScene.pokemonInfoContainer.hide(); this.removePb(); this.end(); diff --git a/src/phases/attempt-run-phase.ts b/src/phases/attempt-run-phase.ts index 2bf6ba2fb5b..525be8c21ab 100644 --- a/src/phases/attempt-run-phase.ts +++ b/src/phases/attempt-run-phase.ts @@ -10,11 +10,8 @@ import type { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import i18next from "i18next"; import { NumberHolder } from "#app/utils/common"; -import { BattleEndPhase } from "./battle-end-phase"; -import { NewBattlePhase } from "./new-battle-phase"; import { PokemonPhase } from "./pokemon-phase"; import { globalScene } from "#app/global-scene"; -import { SelectBiomePhase } from "./select-biome-phase"; export class AttemptRunPhase extends PokemonPhase { public readonly phaseName = "AttemptRunPhase"; @@ -60,13 +57,13 @@ export class AttemptRunPhase extends PokemonPhase { enemyPokemon.trySetStatus(StatusEffect.FAINT); }); - globalScene.phaseManager.pushPhase(new BattleEndPhase(false)); + globalScene.phaseManager.pushNew("BattleEndPhase", false); if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { - globalScene.phaseManager.pushPhase(new SelectBiomePhase()); + globalScene.phaseManager.pushNew("SelectBiomePhase"); } - globalScene.phaseManager.pushPhase(new NewBattlePhase()); + globalScene.phaseManager.pushNew("NewBattlePhase"); } else { playerPokemon.turnData.failedRunAway = true; globalScene.phaseManager.queueMessage(i18next.t("battle:runAwayCannotEscape"), null, true, 500); diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index 4e3543be03a..e169de58cb3 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -2,7 +2,6 @@ import { globalScene } from "#app/global-scene"; import { applyPostBattleAbAttrs, PostBattleAbAttr } from "#app/data/abilities/ability"; import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier"; import { BattlePhase } from "./battle-phase"; -import { GameOverPhase } from "./game-over-phase"; export class BattleEndPhase extends BattlePhase { public readonly phaseName = "BattleEndPhase"; @@ -56,7 +55,7 @@ export class BattleEndPhase extends BattlePhase { // Endless graceful end if (globalScene.gameMode.isEndless && globalScene.currentBattle.waveIndex >= 5850) { globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.unshiftPhase(new GameOverPhase(true)); + globalScene.phaseManager.unshiftNew("GameOverPhase", true); } for (const pokemon of globalScene.getField()) { diff --git a/src/phases/berry-phase.ts b/src/phases/berry-phase.ts index e9e177a8fe4..6e40e299e7c 100644 --- a/src/phases/berry-phase.ts +++ b/src/phases/berry-phase.ts @@ -11,7 +11,6 @@ import { BerryModifier } from "#app/modifier/modifier"; import i18next from "i18next"; import { BooleanHolder } from "#app/utils/common"; import { FieldPhase } from "./field-phase"; -import { CommonAnimPhase } from "./common-anim-phase"; import { globalScene } from "#app/global-scene"; import type Pokemon from "#app/field/pokemon"; @@ -58,8 +57,11 @@ export class BerryPhase extends FieldPhase { return; } - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM), + globalScene.phaseManager.unshiftNew( + "CommonAnimPhase", + pokemon.getBattlerIndex(), + pokemon.getBattlerIndex(), + CommonAnim.USE_ITEM, ); for (const berryModifier of globalScene.applyModifiers(BerryModifier, pokemon.isPlayer(), pokemon)) { diff --git a/src/phases/check-status-effect-phase.ts b/src/phases/check-status-effect-phase.ts index ec15676ebcc..e4793fae076 100644 --- a/src/phases/check-status-effect-phase.ts +++ b/src/phases/check-status-effect-phase.ts @@ -1,4 +1,3 @@ -import { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase"; import { Phase } from "#app/phase"; import type { BattlerIndex } from "#app/battle"; import { globalScene } from "#app/global-scene"; @@ -15,7 +14,7 @@ export class CheckStatusEffectPhase extends Phase { const field = globalScene.getField(); for (const o of this.order) { if (field[o].status?.isPostTurn()) { - globalScene.phaseManager.unshiftPhase(new PostTurnStatusEffectPhase(o)); + globalScene.phaseManager.unshiftNew("PostTurnStatusEffectPhase", o); } } this.end(); diff --git a/src/phases/check-switch-phase.ts b/src/phases/check-switch-phase.ts index 2299bf0c0a5..97f4092096f 100644 --- a/src/phases/check-switch-phase.ts +++ b/src/phases/check-switch-phase.ts @@ -5,8 +5,6 @@ import { getPokemonNameWithAffix } from "#app/messages"; import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import { BattlePhase } from "./battle-phase"; -import { SummonMissingPhase } from "./summon-missing-phase"; -import { SwitchPhase } from "./switch-phase"; import { SwitchType } from "#enums/switch-type"; export class CheckSwitchPhase extends BattlePhase { @@ -35,7 +33,7 @@ export class CheckSwitchPhase extends BattlePhase { // ...if the checked Pokemon is somehow not on the field if (globalScene.field.getAll().indexOf(pokemon) === -1) { - globalScene.phaseManager.unshiftPhase(new SummonMissingPhase(this.fieldIndex)); + globalScene.phaseManager.unshiftNew("SummonMissingPhase", this.fieldIndex); return super.end(); } @@ -68,9 +66,7 @@ export class CheckSwitchPhase extends BattlePhase { UiMode.CONFIRM, () => { globalScene.ui.setMode(UiMode.MESSAGE); - globalScene.phaseManager.unshiftPhase( - new SwitchPhase(SwitchType.INITIAL_SWITCH, this.fieldIndex, false, true), - ); + globalScene.phaseManager.unshiftNew("SwitchPhase", SwitchType.INITIAL_SWITCH, this.fieldIndex, false, true); this.end(); }, () => { diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 7a2e427ecce..afd9cb3bf93 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -18,7 +18,6 @@ import { Command } from "#app/ui/command-ui-handler"; import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import { FieldPhase } from "./field-phase"; -import { SelectTargetPhase } from "./select-target-phase"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { isNullOrUndefined } from "#app/utils/common"; import { ArenaTagSide } from "#app/data/arena-tag"; @@ -192,7 +191,7 @@ export class CommandPhase extends FieldPhase { } console.log(moveTargets, getPokemonNameWithAffix(playerPokemon)); if (moveTargets.targets.length > 1 && moveTargets.multiple) { - globalScene.phaseManager.unshiftPhase(new SelectTargetPhase(this.fieldIndex)); + globalScene.phaseManager.unshiftNew("SelectTargetPhase", this.fieldIndex); } if (turnCommand.move && (moveTargets.targets.length <= 1 || moveTargets.multiple)) { turnCommand.move.targets = moveTargets.targets; @@ -203,7 +202,7 @@ export class CommandPhase extends FieldPhase { ) { turnCommand.move.targets = playerPokemon.getMoveQueue()[0].targets; } else { - globalScene.phaseManager.unshiftPhase(new SelectTargetPhase(this.fieldIndex)); + globalScene.phaseManager.unshiftNew("SelectTargetPhase", this.fieldIndex); } globalScene.currentBattle.preTurnCommands[this.fieldIndex] = preTurnCommand; globalScene.currentBattle.turnCommands[this.fieldIndex] = turnCommand; @@ -457,8 +456,8 @@ export class CommandPhase extends FieldPhase { cancel() { if (this.fieldIndex) { - globalScene.phaseManager.unshiftPhase(new CommandPhase(0)); - globalScene.phaseManager.unshiftPhase(new CommandPhase(1)); + globalScene.phaseManager.unshiftNew("CommandPhase", 0); + globalScene.phaseManager.unshiftNew("CommandPhase", 1); this.end(); } } diff --git a/src/phases/egg-lapse-phase.ts b/src/phases/egg-lapse-phase.ts index a19bf8f50e5..206bef1a33c 100644 --- a/src/phases/egg-lapse-phase.ts +++ b/src/phases/egg-lapse-phase.ts @@ -4,11 +4,9 @@ import { EGG_SEED } from "#app/data/egg"; import { Phase } from "#app/phase"; import i18next from "i18next"; import Overrides from "#app/overrides"; -import { EggHatchPhase } from "./egg-hatch-phase"; import { UiMode } from "#enums/ui-mode"; import { achvs } from "#app/system/achv"; import type { PlayerPokemon } from "#app/field/pokemon"; -import { EggSummaryPhase } from "./egg-summary-phase"; import { EggHatchData } from "#app/data/egg-hatch-data"; /** @@ -83,7 +81,7 @@ export class EggLapsePhase extends Phase { hatchEggsRegular(eggsToHatch: Egg[]) { let eggsToHatchCount: number = eggsToHatch.length; for (const egg of eggsToHatch) { - globalScene.phaseManager.unshiftPhase(new EggHatchPhase(this, egg, eggsToHatchCount)); + globalScene.phaseManager.unshiftNew("EggHatchPhase", this, egg, eggsToHatchCount); eggsToHatchCount--; } } @@ -99,7 +97,7 @@ export class EggLapsePhase extends Phase { } showSummary() { - globalScene.phaseManager.unshiftPhase(new EggSummaryPhase(this.eggHatchData)); + globalScene.phaseManager.unshiftNew("EggSummaryPhase", this.eggHatchData); this.end(); } diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index cdd17c6d6d6..e3b33122ac2 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -23,15 +23,6 @@ import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier import { ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; import Overrides from "#app/overrides"; import { BattlePhase } from "#app/phases/battle-phase"; -import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; -import { GameOverPhase } from "#app/phases/game-over-phase"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { PostSummonPhase } from "#app/phases/post-summon-phase"; -import { ReturnPhase } from "#app/phases/return-phase"; -import { ScanIvsPhase } from "#app/phases/scan-ivs-phase"; -import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase"; -import { SummonPhase } from "#app/phases/summon-phase"; -import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; import { achvs } from "#app/system/achv"; import { handleTutorial, Tutorial } from "#app/tutorial"; import { UiMode } from "#enums/ui-mode"; @@ -68,7 +59,7 @@ export class EncounterPhase extends BattlePhase { // Failsafe if players somehow skip floor 200 in classic mode if (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex > 200) { - globalScene.phaseManager.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.unshiftNew("GameOverPhase"); } const loadEnemyAssets: Promise[] = []; @@ -438,9 +429,9 @@ export class EncounterPhase extends BattlePhase { const doTrainerSummon = () => { this.hideEnemyTrainer(); const availablePartyMembers = globalScene.getEnemyParty().filter(p => !p.isFainted()).length; - globalScene.phaseManager.unshiftPhase(new SummonPhase(0, false)); + globalScene.phaseManager.unshiftNew("SummonPhase", 0, false); if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.phaseManager.unshiftPhase(new SummonPhase(1, false)); + globalScene.phaseManager.unshiftNew("SummonPhase", 1, false); } this.end(); }; @@ -496,7 +487,7 @@ export class EncounterPhase extends BattlePhase { globalScene.ui.clearText(); globalScene.ui.getMessageHandler().hideNameText(); - globalScene.phaseManager.unshiftPhase(new MysteryEncounterPhase()); + globalScene.phaseManager.unshiftNew("MysteryEncounterPhase"); this.end(); }; @@ -554,7 +545,7 @@ export class EncounterPhase extends BattlePhase { enemyField.forEach((enemyPokemon, e) => { if (enemyPokemon.isShiny(true)) { - globalScene.phaseManager.unshiftPhase(new ShinySparklePhase(BattlerIndex.ENEMY + e)); + globalScene.phaseManager.unshiftNew("ShinySparklePhase", BattlerIndex.ENEMY + e); } /** This sets Eternatus' held item to be untransferrable, preventing it from being stolen */ if ( @@ -576,25 +567,31 @@ export class EncounterPhase extends BattlePhase { if (![BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType)) { enemyField.map(p => - globalScene.phaseManager.pushConditionalPhase(new PostSummonPhase(p.getBattlerIndex()), () => { - // if there is not a player party, we can't continue - if (!globalScene.getPlayerParty().length) { - return false; - } - // how many player pokemon are on the field ? - const pokemonsOnFieldCount = globalScene.getPlayerParty().filter(p => p.isOnField()).length; - // if it's a 2vs1, there will never be a 2nd pokemon on our field even - const requiredPokemonsOnField = Math.min(globalScene.getPlayerParty().filter(p => !p.isFainted()).length, 2); - // if it's a double, there should be 2, otherwise 1 - if (globalScene.currentBattle.double) { - return pokemonsOnFieldCount === requiredPokemonsOnField; - } - return pokemonsOnFieldCount === 1; - }), + globalScene.phaseManager.pushConditionalPhase( + globalScene.phaseManager.create("PostSummonPhase", p.getBattlerIndex()), + () => { + // if there is not a player party, we can't continue + if (!globalScene.getPlayerParty().length) { + return false; + } + // how many player pokemon are on the field ? + const pokemonsOnFieldCount = globalScene.getPlayerParty().filter(p => p.isOnField()).length; + // if it's a 2vs1, there will never be a 2nd pokemon on our field even + const requiredPokemonsOnField = Math.min( + globalScene.getPlayerParty().filter(p => !p.isFainted()).length, + 2, + ); + // if it's a double, there should be 2, otherwise 1 + if (globalScene.currentBattle.double) { + return pokemonsOnFieldCount === requiredPokemonsOnField; + } + return pokemonsOnFieldCount === 1; + }, + ), ); const ivScannerModifier = globalScene.findModifier(m => m instanceof IvScannerModifier); if (ivScannerModifier) { - enemyField.map(p => globalScene.phaseManager.pushPhase(new ScanIvsPhase(p.getBattlerIndex()))); + enemyField.map(p => globalScene.phaseManager.pushNew("ScanIvsPhase", p.getBattlerIndex())); } } @@ -602,21 +599,21 @@ export class EncounterPhase extends BattlePhase { const availablePartyMembers = globalScene.getPokemonAllowedInBattle(); if (!availablePartyMembers[0].isOnField()) { - globalScene.phaseManager.pushPhase(new SummonPhase(0)); + globalScene.phaseManager.pushNew("SummonPhase", 0); } if (globalScene.currentBattle.double) { if (availablePartyMembers.length > 1) { - globalScene.phaseManager.pushPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.pushNew("ToggleDoublePositionPhase", true); if (!availablePartyMembers[1].isOnField()) { - globalScene.phaseManager.pushPhase(new SummonPhase(1)); + globalScene.phaseManager.pushNew("SummonPhase", 1); } } } else { if (availablePartyMembers.length > 1 && availablePartyMembers[1].isOnField()) { - globalScene.phaseManager.pushPhase(new ReturnPhase(1)); + globalScene.phaseManager.pushNew("ReturnPhase", 1); } - globalScene.phaseManager.pushPhase(new ToggleDoublePositionPhase(false)); + globalScene.phaseManager.pushNew("ToggleDoublePositionPhase", false); } if ( @@ -625,9 +622,9 @@ export class EncounterPhase extends BattlePhase { ) { const minPartySize = globalScene.currentBattle.double ? 2 : 1; if (availablePartyMembers.length > minPartySize) { - globalScene.phaseManager.pushPhase(new CheckSwitchPhase(0, globalScene.currentBattle.double)); + globalScene.phaseManager.pushNew("CheckSwitchPhase", 0, globalScene.currentBattle.double); if (globalScene.currentBattle.double) { - globalScene.phaseManager.pushPhase(new CheckSwitchPhase(1, globalScene.currentBattle.double)); + globalScene.phaseManager.pushNew("CheckSwitchPhase", 1, globalScene.currentBattle.double); } } } diff --git a/src/phases/evolution-phase.ts b/src/phases/evolution-phase.ts index 937a7d31542..eaedb6d32b0 100644 --- a/src/phases/evolution-phase.ts +++ b/src/phases/evolution-phase.ts @@ -14,8 +14,6 @@ import { LearnMoveSituation } from "#app/field/pokemon"; import { getTypeRgb } from "#app/data/type"; import i18next from "i18next"; import { getPokemonNameWithAffix } from "#app/messages"; -import { LearnMovePhase } from "#app/phases/learn-move-phase"; -import { EndEvolutionPhase } from "#app/phases/end-evolution-phase"; import { EVOLVE_MOVE } from "#app/data/balance/pokemon-level-moves"; export class EvolutionPhase extends Phase { @@ -262,7 +260,7 @@ export class EvolutionPhase extends Phase { SoundFade.fadeOut(globalScene, this.evolutionBgm, 100); - globalScene.phaseManager.unshiftPhase(new EndEvolutionPhase()); + globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); globalScene.ui.showText( i18next.t("menu:stoppedEvolving", { @@ -355,11 +353,13 @@ export class EvolutionPhase extends Phase { .getLevelMoves(this.lastLevel + 1, true, false, false, learnSituation) .filter(lm => lm[0] === EVOLVE_MOVE); for (const lm of levelMoves) { - globalScene.phaseManager.unshiftPhase( - new LearnMovePhase(globalScene.getPlayerParty().indexOf(this.pokemon), lm[1]), + globalScene.phaseManager.unshiftNew( + "LearnMovePhase", + globalScene.getPlayerParty().indexOf(this.pokemon), + lm[1], ); } - globalScene.phaseManager.unshiftPhase(new EndEvolutionPhase()); + globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); globalScene.playSound("se/shine"); this.doSpray(); diff --git a/src/phases/exp-phase.ts b/src/phases/exp-phase.ts index 8084ae78221..74768e86186 100644 --- a/src/phases/exp-phase.ts +++ b/src/phases/exp-phase.ts @@ -4,7 +4,6 @@ import { ExpBoosterModifier } from "#app/modifier/modifier"; import i18next from "i18next"; import { NumberHolder } from "#app/utils/common"; import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; -import { LevelUpPhase } from "./level-up-phase"; export class ExpPhase extends PlayerPartyMemberPokemonPhase { public readonly phaseName = "ExpPhase"; @@ -34,7 +33,7 @@ export class ExpPhase extends PlayerPartyMemberPokemonPhase { pokemon.addExp(exp.value); const newLevel = pokemon.level; if (newLevel > lastLevel) { - globalScene.phaseManager.unshiftPhase(new LevelUpPhase(this.partyMemberIndex, lastLevel, newLevel)); + globalScene.phaseManager.unshiftNew("LevelUpPhase", this.partyMemberIndex, lastLevel, newLevel); } pokemon.updateInfo().then(() => this.end()); }, diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 41e21162dbf..38376af4356 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -24,13 +24,7 @@ import { getPokemonNameWithAffix } from "#app/messages"; import { PokemonInstantReviveModifier } from "#app/modifier/modifier"; import { SwitchType } from "#enums/switch-type"; import i18next from "i18next"; -import { DamageAnimPhase } from "./damage-anim-phase"; -import { GameOverPhase } from "./game-over-phase"; import { PokemonPhase } from "./pokemon-phase"; -import { SwitchPhase } from "./switch-phase"; -import { SwitchSummonPhase } from "./switch-summon-phase"; -import { ToggleDoublePositionPhase } from "./toggle-double-position-phase"; -import { VictoryPhase } from "./victory-phase"; import { isNullOrUndefined } from "#app/utils/common"; import { FRIENDSHIP_LOSS_FROM_FAINT } from "#app/data/balance/starters"; import { BattlerTagType } from "#enums/battler-tag-type"; @@ -166,7 +160,7 @@ export class FaintPhase extends PokemonPhase { const legalPlayerPartyPokemon = legalPlayerPokemon.filter(p => !p.isActive(true)); if (!legalPlayerPokemon.length) { /** If the player doesn't have any legal Pokemon, end the game */ - globalScene.phaseManager.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.unshiftNew("GameOverPhase"); } else if ( globalScene.currentBattle.double && legalPlayerPokemon.length === 1 && @@ -176,25 +170,23 @@ export class FaintPhase extends PokemonPhase { * If the player has exactly one Pokemon in total at this point in a double battle, and that Pokemon * is already on the field, unshift a phase that moves that Pokemon to center position. */ - globalScene.phaseManager.unshiftPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.unshiftNew("ToggleDoublePositionPhase", true); } else if (legalPlayerPartyPokemon.length > 0) { /** * 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. */ - globalScene.phaseManager.pushPhase(new SwitchPhase(SwitchType.SWITCH, this.fieldIndex, true, false)); + globalScene.phaseManager.pushNew("SwitchPhase", SwitchType.SWITCH, this.fieldIndex, true, false); } } else { - globalScene.phaseManager.unshiftPhase(new VictoryPhase(this.battlerIndex)); + globalScene.phaseManager.unshiftNew("VictoryPhase", this.battlerIndex); if ([BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType)) { const hasReservePartyMember = !!globalScene .getEnemyParty() .filter(p => p.isActive() && !p.isOnField() && p.trainerSlot === (pokemon as EnemyPokemon).trainerSlot) .length; if (hasReservePartyMember) { - globalScene.phaseManager.pushPhase( - new SwitchSummonPhase(SwitchType.SWITCH, this.fieldIndex, -1, false, false), - ); + globalScene.phaseManager.pushNew("SwitchSummonPhase", SwitchType.SWITCH, this.fieldIndex, -1, false, false); } } } @@ -249,7 +241,7 @@ export class FaintPhase extends PokemonPhase { } else { // Final boss' HP threshold has been bypassed; cancel faint and force check for 2nd phase enemy.hp++; - globalScene.phaseManager.unshiftPhase(new DamageAnimPhase(enemy.getBattlerIndex(), 0, HitResult.INDIRECT)); + globalScene.phaseManager.unshiftNew("DamageAnimPhase", enemy.getBattlerIndex(), 0, HitResult.INDIRECT); this.end(); } return true; diff --git a/src/phases/form-change-phase.ts b/src/phases/form-change-phase.ts index c0d2a9a11eb..3813359d432 100644 --- a/src/phases/form-change-phase.ts +++ b/src/phases/form-change-phase.ts @@ -7,7 +7,6 @@ import type { PlayerPokemon } from "../field/pokemon"; import { UiMode } from "#enums/ui-mode"; import type PartyUiHandler from "../ui/party-ui-handler"; import { getPokemonNameWithAffix } from "../messages"; -import { EndEvolutionPhase } from "./end-evolution-phase"; import { EvolutionPhase } from "./evolution-phase"; import { BattlerTagType } from "#enums/battler-tag-type"; import { SpeciesFormKey } from "#enums/species-form-key"; @@ -100,7 +99,7 @@ export class FormChangePhase extends EvolutionPhase { globalScene.time.delayedCall(900, () => { this.pokemon.changeForm(this.formChange).then(() => { if (!this.modal) { - globalScene.phaseManager.unshiftPhase(new EndEvolutionPhase()); + globalScene.phaseManager.unshiftNew("EndEvolutionPhase"); } globalScene.playSound("se/shine"); diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index 166bb955c24..5fabc5cee81 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -9,14 +9,7 @@ import { trainerConfigs } from "#app/data/trainers/trainer-config"; import type Pokemon from "#app/field/pokemon"; import { modifierTypes } from "#app/modifier/modifier-type"; import { BattlePhase } from "#app/phases/battle-phase"; -import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; -import { EncounterPhase } from "#app/phases/encounter-phase"; -import { EndCardPhase } from "#app/phases/end-card-phase"; -import { GameOverModifierRewardPhase } from "#app/phases/game-over-modifier-reward-phase"; -import { PostGameOverPhase } from "#app/phases/post-game-over-phase"; -import { RibbonModifierRewardPhase } from "#app/phases/ribbon-modifier-reward-phase"; -import { SummonPhase } from "#app/phases/summon-phase"; -import { UnlockPhase } from "#app/phases/unlock-phase"; +import type { EndCardPhase } from "#app/phases/end-card-phase"; import { achvs, ChallengeAchv } from "#app/system/achv"; import { Unlockables } from "#app/system/unlockables"; import { UiMode } from "#enums/ui-mode"; @@ -31,7 +24,6 @@ import ChallengeData from "#app/system/challenge-data"; import TrainerData from "#app/system/trainer-data"; import ArenaData from "#app/system/arena-data"; import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; -import { MessagePhase } from "./message-phase"; export class GameOverPhase extends BattlePhase { public readonly phaseName = "GameOverPhase"; @@ -86,21 +78,21 @@ export class GameOverPhase extends BattlePhase { globalScene.reset(); globalScene.phaseManager.clearPhaseQueue(); globalScene.gameData.loadSession(globalScene.sessionSlotId).then(() => { - globalScene.phaseManager.pushPhase(new EncounterPhase(true)); + globalScene.phaseManager.pushNew("EncounterPhase", true); const availablePartyMembers = globalScene.getPokemonAllowedInBattle().length; - globalScene.phaseManager.pushPhase(new SummonPhase(0)); + globalScene.phaseManager.pushNew("SummonPhase", 0); if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.phaseManager.pushPhase(new SummonPhase(1)); + globalScene.phaseManager.pushNew("SummonPhase", 1); } if ( globalScene.currentBattle.waveIndex > 1 && globalScene.currentBattle.battleType !== BattleType.TRAINER ) { - globalScene.phaseManager.pushPhase(new CheckSwitchPhase(0, globalScene.currentBattle.double)); + globalScene.phaseManager.pushNew("CheckSwitchPhase", 0, globalScene.currentBattle.double); if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.phaseManager.pushPhase(new CheckSwitchPhase(1, globalScene.currentBattle.double)); + globalScene.phaseManager.pushNew("CheckSwitchPhase", 1, globalScene.currentBattle.double); } } @@ -160,17 +152,15 @@ export class GameOverPhase extends BattlePhase { this.handleUnlocks(); for (const species of this.firstRibbons) { - globalScene.phaseManager.unshiftPhase( - new RibbonModifierRewardPhase(modifierTypes.VOUCHER_PLUS, species), - ); + globalScene.phaseManager.unshiftNew("RibbonModifierRewardPhase", modifierTypes.VOUCHER_PLUS, species); } if (!firstClear) { - globalScene.phaseManager.unshiftPhase(new GameOverModifierRewardPhase(modifierTypes.VOUCHER_PREMIUM)); + globalScene.phaseManager.unshiftNew("GameOverModifierRewardPhase", modifierTypes.VOUCHER_PREMIUM); } } this.getRunHistoryEntry().then(runHistoryEntry => { globalScene.gameData.saveRunHistory(runHistoryEntry, this.isVictory); - globalScene.phaseManager.pushPhase(new PostGameOverPhase(endCardPhase)); + globalScene.phaseManager.pushNew("PostGameOverPhase", endCardPhase); this.end(); }); }; @@ -199,7 +189,7 @@ export class GameOverPhase extends BattlePhase { () => { globalScene.ui.fadeOut(500).then(() => { globalScene.charSprite.hide().then(() => { - const endCardPhase = new EndCardPhase(); + const endCardPhase = globalScene.phaseManager.create("EndCardPhase"); globalScene.phaseManager.unshiftPhase(endCardPhase); clear(endCardPhase); }); @@ -209,7 +199,7 @@ export class GameOverPhase extends BattlePhase { }); }); } else { - const endCardPhase = new EndCardPhase(); + const endCardPhase = globalScene.phaseManager.create("EndCardPhase"); globalScene.phaseManager.unshiftPhase(endCardPhase); clear(endCardPhase); } @@ -234,7 +224,7 @@ export class GameOverPhase extends BattlePhase { .catch(_err => { globalScene.phaseManager.clearPhaseQueue(); globalScene.phaseManager.clearPhaseQueueSplice(); - globalScene.phaseManager.unshiftPhase(new MessagePhase(i18next.t("menu:serverCommunicationFailed"), 2500)); + globalScene.phaseManager.unshiftNew("MessagePhase", i18next.t("menu:serverCommunicationFailed"), 2500); // force the game to reload after 2 seconds. setTimeout(() => { window.location.reload(); @@ -253,22 +243,22 @@ export class GameOverPhase extends BattlePhase { handleUnlocks(): void { if (this.isVictory && globalScene.gameMode.isClassic) { if (!globalScene.gameData.unlocks[Unlockables.ENDLESS_MODE]) { - globalScene.phaseManager.unshiftPhase(new UnlockPhase(Unlockables.ENDLESS_MODE)); + globalScene.phaseManager.unshiftNew("UnlockPhase", Unlockables.ENDLESS_MODE); } if ( globalScene.getPlayerParty().filter(p => p.fusionSpecies).length && !globalScene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE] ) { - globalScene.phaseManager.unshiftPhase(new UnlockPhase(Unlockables.SPLICED_ENDLESS_MODE)); + globalScene.phaseManager.unshiftNew("UnlockPhase", Unlockables.SPLICED_ENDLESS_MODE); } if (!globalScene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE]) { - globalScene.phaseManager.unshiftPhase(new UnlockPhase(Unlockables.MINI_BLACK_HOLE)); + globalScene.phaseManager.unshiftNew("UnlockPhase", Unlockables.MINI_BLACK_HOLE); } if ( !globalScene.gameData.unlocks[Unlockables.EVIOLITE] && globalScene.getPlayerParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions) ) { - globalScene.phaseManager.unshiftPhase(new UnlockPhase(Unlockables.EVIOLITE)); + globalScene.phaseManager.unshiftNew("UnlockPhase", Unlockables.EVIOLITE); } } } diff --git a/src/phases/level-up-phase.ts b/src/phases/level-up-phase.ts index b3b82f13f42..c78a1798304 100644 --- a/src/phases/level-up-phase.ts +++ b/src/phases/level-up-phase.ts @@ -2,8 +2,6 @@ import { globalScene } from "#app/global-scene"; import { ExpNotification } from "#app/enums/exp-notification"; import type { PlayerPokemon } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; -import { EvolutionPhase } from "#app/phases/evolution-phase"; -import { LearnMovePhase } from "#app/phases/learn-move-phase"; import { PlayerPartyMemberPokemonPhase } from "#app/phases/player-party-member-pokemon-phase"; import { LevelAchv } from "#app/system/achv"; import { NumberHolder } from "#app/utils/common"; @@ -66,14 +64,14 @@ export class LevelUpPhase extends PlayerPartyMemberPokemonPhase { // this feels like an unnecessary optimization const levelMoves = this.getPokemon().getLevelMoves(this.lastLevel + 1); for (const lm of levelMoves) { - globalScene.phaseManager.unshiftPhase(new LearnMovePhase(this.partyMemberIndex, lm[1])); + globalScene.phaseManager.unshiftNew("LearnMovePhase", this.partyMemberIndex, lm[1]); } } if (!this.pokemon.pauseEvolutions) { const evolution = this.pokemon.getEvolution(); if (evolution) { this.pokemon.breakIllusion(); - globalScene.phaseManager.unshiftPhase(new EvolutionPhase(this.pokemon, evolution, this.lastLevel)); + globalScene.phaseManager.unshiftNew("EvolutionPhase", this.pokemon, evolution, this.lastLevel); } } return super.end(); diff --git a/src/phases/login-phase.ts b/src/phases/login-phase.ts index 5e1728d4415..de426866baa 100644 --- a/src/phases/login-phase.ts +++ b/src/phases/login-phase.ts @@ -7,8 +7,6 @@ import { UiMode } from "#enums/ui-mode"; import i18next, { t } from "i18next"; import { sessionIdKey, executeIf } from "#app/utils/common"; import { getCookie, removeCookie } from "#app/utils/cookies"; -import { SelectGenderPhase } from "./select-gender-phase"; -import { UnavailablePhase } from "./unavailable-phase"; export class LoginPhase extends Phase { public readonly phaseName = "LoginPhase"; @@ -70,7 +68,7 @@ export class LoginPhase extends Phase { }); }, () => { - globalScene.phaseManager.unshiftPhase(new LoginPhase(false)); + globalScene.phaseManager.unshiftNew("LoginPhase", false); this.end(); }, ], @@ -94,7 +92,7 @@ export class LoginPhase extends Phase { removeCookie(sessionIdKey); globalScene.reset(true, true); } else { - globalScene.phaseManager.unshiftPhase(new UnavailablePhase()); + globalScene.phaseManager.unshiftNew("UnavailablePhase"); super.end(); } return null; @@ -114,7 +112,7 @@ export class LoginPhase extends Phase { globalScene.ui.setMode(UiMode.MESSAGE); if (!globalScene.gameData.gender) { - globalScene.phaseManager.unshiftPhase(new SelectGenderPhase()); + globalScene.phaseManager.unshiftNew("SelectGenderPhase"); } handleTutorial(Tutorial.Intro).then(() => super.end()); diff --git a/src/phases/message-phase.ts b/src/phases/message-phase.ts index 2a485d837b0..61f9b74a037 100644 --- a/src/phases/message-phase.ts +++ b/src/phases/message-phase.ts @@ -44,8 +44,13 @@ export class MessagePhase extends Phase { page0 = page0.split(repname[p]).join(pokename[p]); page1 = page1.split(repname[p]).join(pokename[p]); } - globalScene.phaseManager.unshiftPhase( - new MessagePhase(page1, this.callbackDelay, this.prompt, this.promptDelay, this.speaker), + globalScene.phaseManager.unshiftNew( + "MessagePhase", + page1, + this.callbackDelay, + this.prompt, + this.promptDelay, + this.speaker, ); this.text = page0.trim(); } else { diff --git a/src/phases/move-charge-phase.ts b/src/phases/move-charge-phase.ts index 263306acbf2..a481f4e37b8 100644 --- a/src/phases/move-charge-phase.ts +++ b/src/phases/move-charge-phase.ts @@ -6,7 +6,6 @@ import type { PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import { BooleanHolder } from "#app/utils/common"; -import { MovePhase } from "#app/phases/move-phase"; import { PokemonPhase } from "#app/phases/pokemon-phase"; import { BattlerTagType } from "#enums/battler-tag-type"; @@ -64,7 +63,7 @@ export class MoveChargePhase extends PokemonPhase { // this MoveEndPhase will be duplicated by the queued MovePhase if not removed globalScene.phaseManager.tryRemovePhase(phase => phase.is("MoveEndPhase") && phase.getPokemon() === user); // queue a new MovePhase for this move's attack phase - globalScene.phaseManager.unshiftPhase(new MovePhase(user, [this.targetIndex], this.move, false)); + globalScene.phaseManager.unshiftNew("MovePhase", user, [this.targetIndex], this.move, false); } else { user.getMoveQueue().push({ move: move.id, targets: [this.targetIndex] }); } diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index e135f5bd161..e0fa381447b 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -68,15 +68,10 @@ import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import i18next from "i18next"; import type { Phase } from "#app/phase"; -import { ShowAbilityPhase } from "./show-ability-phase"; -import { MovePhase } from "./move-phase"; -import { MoveEndPhase } from "./move-end-phase"; -import { HideAbilityPhase } from "#app/phases/hide-ability-phase"; import type { TypeDamageMultiplier } from "#app/data/type"; import { HitCheckResult } from "#enums/hit-check-result"; import type Move from "#app/data/moves/move"; import { isFieldTargeted } from "#app/data/moves/move-utils"; -import { FaintPhase } from "./faint-phase"; import { DamageAchv } from "#app/system/achv"; type HitCheckEntry = [HitCheckResult, TypeDamageMultiplier]; @@ -191,13 +186,25 @@ export class MoveEffectPhase extends PokemonPhase { // TODO: ability displays should be handled by the ability if (!target.getTag(BattlerTagType.MAGIC_COAT)) { this.queuedPhases.push( - new ShowAbilityPhase(target.getBattlerIndex(), target.getPassiveAbility().hasAttr(ReflectStatusMoveAbAttr)), + globalScene.phaseManager.create( + "ShowAbilityPhase", + target.getBattlerIndex(), + target.getPassiveAbility().hasAttr(ReflectStatusMoveAbAttr), + ), ); - this.queuedPhases.push(new HideAbilityPhase()); + this.queuedPhases.push(globalScene.phaseManager.create("HideAbilityPhase")); } this.queuedPhases.push( - new MovePhase(target, newTargets, new PokemonMove(this.move.id, 0, 0, true), true, true, true), + globalScene.phaseManager.create( + "MovePhase", + target, + newTargets, + new PokemonMove(this.move.id, 0, 0, true), + true, + true, + true, + ), ); } @@ -384,7 +391,7 @@ export class MoveEffectPhase extends PokemonPhase { } if (this.queuedPhases.length) { - globalScene.phaseManager.appendToPhase(this.queuedPhases, MoveEndPhase); + globalScene.phaseManager.appendToPhase(this.queuedPhases, "MoveEndPhase"); } const moveType = user.getMoveType(this.move, true); if (this.move.category !== MoveCategory.STATUS && !user.stellarTypesBoosted.includes(moveType)) { @@ -903,7 +910,7 @@ export class MoveEffectPhase extends PokemonPhase { // set splice index here, so future scene queues happen before FaintedPhase globalScene.phaseManager.setPhaseQueueSplice(); - globalScene.phaseManager.unshiftPhase(new FaintPhase(target.getBattlerIndex(), false, user)); + globalScene.phaseManager.unshiftNew("FaintPhase", target.getBattlerIndex(), false, user); target.destroySubstitute(); target.lapseTag(BattlerTagType.COMMANDED); diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 03f94ad3d1d..7fc6a86e3f7 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -39,10 +39,6 @@ import { MoveResult } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import Overrides from "#app/overrides"; import { BattlePhase } from "#app/phases/battle-phase"; -import { CommonAnimPhase } from "#app/phases/common-anim-phase"; -import { MoveChargePhase } from "#app/phases/move-charge-phase"; -import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; import { NumberHolder } from "#app/utils/common"; import { AbilityId } from "#enums/ability-id"; import { ArenaTagType } from "#enums/arena-tag-type"; @@ -271,12 +267,11 @@ export class MovePhase extends BattlePhase { globalScene.phaseManager.queueMessage( getStatusEffectActivationText(this.pokemon.status.effect, getPokemonNameWithAffix(this.pokemon)), ); - globalScene.phaseManager.unshiftPhase( - new CommonAnimPhase( - this.pokemon.getBattlerIndex(), - undefined, - CommonAnim.POISON + (this.pokemon.status.effect - 1), - ), + globalScene.phaseManager.unshiftNew( + "CommonAnimPhase", + this.pokemon.getBattlerIndex(), + undefined, + CommonAnim.POISON + (this.pokemon.status.effect - 1), ); } else if (healed) { globalScene.phaseManager.queueMessage( @@ -407,8 +402,13 @@ export class MovePhase extends BattlePhase { if (success) { const move = this.move.getMove(); applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, move); - globalScene.phaseManager.unshiftPhase( - new MoveEffectPhase(this.pokemon.getBattlerIndex(), this.targets, move, this.reflected, this.move.virtual), + globalScene.phaseManager.unshiftNew( + "MoveEffectPhase", + this.pokemon.getBattlerIndex(), + this.targets, + move, + this.reflected, + this.move.virtual, ); } else { if ([MoveId.ROAR, MoveId.WHIRLWIND, MoveId.TRICK_OR_TREAT, MoveId.FORESTS_CURSE].includes(this.move.moveId)) { @@ -457,8 +457,11 @@ export class MovePhase extends BattlePhase { applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove()); this.showMoveText(); - globalScene.phaseManager.unshiftPhase( - new MoveChargePhase(this.pokemon.getBattlerIndex(), this.targets[0], this.move), + globalScene.phaseManager.unshiftNew( + "MoveChargePhase", + this.pokemon.getBattlerIndex(), + this.targets[0], + this.move, ); } else { this.pokemon.pushMoveHistory({ @@ -481,8 +484,11 @@ export class MovePhase extends BattlePhase { * Queues a {@linkcode MoveEndPhase} and then ends the phase */ public end(): void { - globalScene.phaseManager.unshiftPhase( - new MoveEndPhase(this.pokemon.getBattlerIndex(), this.getActiveTargetPokemon(), this.followUp), + globalScene.phaseManager.unshiftNew( + "MoveEndPhase", + this.pokemon.getBattlerIndex(), + this.getActiveTargetPokemon(), + this.followUp, ); super.end(); diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index b1ca11d45a5..1f27db7ff64 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -3,15 +3,6 @@ import type { OptionPhaseCallback } from "#app/data/mystery-encounters/mystery-e import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; import { SeenEncounterData } from "#app/data/mystery-encounters/mystery-encounter-save-data"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { CheckSwitchPhase } from "#app/phases/check-switch-phase"; -import { GameOverPhase } from "#app/phases/game-over-phase"; -import { NewBattlePhase } from "#app/phases/new-battle-phase"; -import { ReturnPhase } from "#app/phases/return-phase"; -import { ScanIvsPhase } from "#app/phases/scan-ivs-phase"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { SummonPhase } from "#app/phases/summon-phase"; -import { SwitchPhase } from "#app/phases/switch-phase"; -import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; import { BattleSpec } from "#enums/battle-spec"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; @@ -26,7 +17,6 @@ import { IvScannerModifier } from "../modifier/modifier"; import { Phase } from "../phase"; import { UiMode } from "#enums/ui-mode"; import { isNullOrUndefined, randSeedItem } from "#app/utils/common"; -import { SelectBiomePhase } from "./select-biome-phase"; /** * Will handle (in order): @@ -124,7 +114,7 @@ export class MysteryEncounterPhase extends Phase { */ continueEncounter() { const endDialogueAndContinueEncounter = () => { - globalScene.phaseManager.pushPhase(new MysteryEncounterOptionSelectedPhase()); + globalScene.phaseManager.pushNew("MysteryEncounterOptionSelectedPhase"); this.end(); }; @@ -256,7 +246,7 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase { // The total number of legal player Pokemon that aren't currently on the field const legalPlayerPartyPokemon = legalPlayerPokemon.filter(p => !p.isActive(true)); if (!legalPlayerPokemon.length) { - globalScene.phaseManager.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.unshiftNew("GameOverPhase"); return this.end(); } @@ -265,13 +255,13 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase { const playerField = globalScene.getPlayerField(); playerField.forEach((pokemon, i) => { if (!pokemon.isAllowedInBattle() && legalPlayerPartyPokemon.length > i) { - globalScene.phaseManager.unshiftPhase(new SwitchPhase(SwitchType.SWITCH, i, true, false)); + globalScene.phaseManager.unshiftNew("SwitchPhase", SwitchType.SWITCH, i, true, false); } }); // THEN, if is a double battle, and player only has 1 summoned pokemon, center pokemon on field if (globalScene.currentBattle.double && legalPlayerPokemon.length === 1 && legalPlayerPartyPokemon.length === 0) { - globalScene.phaseManager.unshiftPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.unshiftNew("ToggleDoublePositionPhase", true); } this.end(); @@ -348,9 +338,9 @@ export class MysteryEncounterBattlePhase extends Phase { globalScene.playBgm(); } const availablePartyMembers = globalScene.getEnemyParty().filter(p => !p.isFainted()).length; - globalScene.phaseManager.unshiftPhase(new SummonPhase(0, false)); + globalScene.phaseManager.unshiftNew("SummonPhase", 0, false); if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.phaseManager.unshiftPhase(new SummonPhase(1, false)); + globalScene.phaseManager.unshiftNew("SummonPhase", 1, false); } if (!globalScene.currentBattle.mysteryEncounter?.hideBattleIntroMessage) { @@ -368,9 +358,9 @@ export class MysteryEncounterBattlePhase extends Phase { const doTrainerSummon = () => { this.hideEnemyTrainer(); const availablePartyMembers = globalScene.getEnemyParty().filter(p => !p.isFainted()).length; - globalScene.phaseManager.unshiftPhase(new SummonPhase(0, false)); + globalScene.phaseManager.unshiftNew("SummonPhase", 0, false); if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.phaseManager.unshiftPhase(new SummonPhase(1, false)); + globalScene.phaseManager.unshiftNew("SummonPhase", 1, false); } this.endBattleSetup(); }; @@ -426,37 +416,37 @@ export class MysteryEncounterBattlePhase extends Phase { if (encounterMode !== MysteryEncounterMode.TRAINER_BATTLE) { const ivScannerModifier = globalScene.findModifier(m => m instanceof IvScannerModifier); if (ivScannerModifier) { - enemyField.map(p => globalScene.phaseManager.pushPhase(new ScanIvsPhase(p.getBattlerIndex()))); + enemyField.map(p => globalScene.phaseManager.pushNew("ScanIvsPhase", p.getBattlerIndex())); } } const availablePartyMembers = globalScene.getPlayerParty().filter(p => p.isAllowedInBattle()); if (!availablePartyMembers[0].isOnField()) { - globalScene.phaseManager.pushPhase(new SummonPhase(0)); + globalScene.phaseManager.pushNew("SummonPhase", 0); } if (globalScene.currentBattle.double) { if (availablePartyMembers.length > 1) { - globalScene.phaseManager.pushPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.pushNew("ToggleDoublePositionPhase", true); if (!availablePartyMembers[1].isOnField()) { - globalScene.phaseManager.pushPhase(new SummonPhase(1)); + globalScene.phaseManager.pushNew("SummonPhase", 1); } } } else { if (availablePartyMembers.length > 1 && availablePartyMembers[1].isOnField()) { globalScene.getPlayerField().forEach(pokemon => pokemon.lapseTag(BattlerTagType.COMMANDED)); - globalScene.phaseManager.pushPhase(new ReturnPhase(1)); + globalScene.phaseManager.pushNew("ReturnPhase", 1); } - globalScene.phaseManager.pushPhase(new ToggleDoublePositionPhase(false)); + globalScene.phaseManager.pushNew("ToggleDoublePositionPhase", false); } if (encounterMode !== MysteryEncounterMode.TRAINER_BATTLE && !this.disableSwitch) { const minPartySize = globalScene.currentBattle.double ? 2 : 1; if (availablePartyMembers.length > minPartySize) { - globalScene.phaseManager.pushPhase(new CheckSwitchPhase(0, globalScene.currentBattle.double)); + globalScene.phaseManager.pushNew("CheckSwitchPhase", 0, globalScene.currentBattle.double); if (globalScene.currentBattle.double) { - globalScene.phaseManager.pushPhase(new CheckSwitchPhase(1, globalScene.currentBattle.double)); + globalScene.phaseManager.pushNew("CheckSwitchPhase", 1, globalScene.currentBattle.double); } } } @@ -563,15 +553,13 @@ export class MysteryEncounterRewardsPhase extends Phase { encounter.doEncounterRewards(); } else if (this.addHealPhase) { globalScene.phaseManager.tryRemovePhase(p => p.is("SelectModifierPhase")); - globalScene.phaseManager.unshiftPhase( - new SelectModifierPhase(0, undefined, { - fillRemaining: false, - rerollMultiplier: -1, - }), - ); + globalScene.phaseManager.unshiftNew("SelectModifierPhase", 0, undefined, { + fillRemaining: false, + rerollMultiplier: -1, + }); } - globalScene.phaseManager.pushPhase(new PostMysteryEncounterPhase()); + globalScene.phaseManager.pushNew("PostMysteryEncounterPhase"); this.end(); } } @@ -618,10 +606,10 @@ export class PostMysteryEncounterPhase extends Phase { continueEncounter() { const endPhase = () => { if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { - globalScene.phaseManager.pushPhase(new SelectBiomePhase()); + globalScene.phaseManager.pushNew("SelectBiomePhase"); } - globalScene.phaseManager.pushPhase(new NewBattlePhase()); + globalScene.phaseManager.pushNew("NewBattlePhase"); this.end(); }; diff --git a/src/phases/post-game-over-phase.ts b/src/phases/post-game-over-phase.ts index 37a3297cc52..8e19dcd5498 100644 --- a/src/phases/post-game-over-phase.ts +++ b/src/phases/post-game-over-phase.ts @@ -1,7 +1,6 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; import type { EndCardPhase } from "./end-card-phase"; -import { TitlePhase } from "./title-phase"; export class PostGameOverPhase extends Phase { public readonly phaseName = "PostGameOverPhase"; @@ -28,7 +27,7 @@ export class PostGameOverPhase extends Phase { return globalScene.reset(true); } globalScene.reset(); - globalScene.phaseManager.unshiftPhase(new TitlePhase()); + globalScene.phaseManager.unshiftNew("TitlePhase"); this.end(); }); }); diff --git a/src/phases/quiet-form-change-phase.ts b/src/phases/quiet-form-change-phase.ts index 363b026831f..6b65c2a5140 100644 --- a/src/phases/quiet-form-change-phase.ts +++ b/src/phases/quiet-form-change-phase.ts @@ -9,7 +9,6 @@ import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { BattlePhase } from "./battle-phase"; import type { MovePhase } from "./move-phase"; -import { PokemonHealPhase } from "./pokemon-heal-phase"; import { applyAbAttrs, ClearTerrainAbAttr, @@ -159,8 +158,15 @@ export class QuietFormChangePhase extends BattlePhase { this.pokemon.findAndRemoveTags(t => t.tagType === BattlerTagType.AUTOTOMIZED); if (globalScene?.currentBattle.battleSpec === BattleSpec.FINAL_BOSS && this.pokemon.isEnemy()) { globalScene.playBgm(); - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase(this.pokemon.getBattlerIndex(), this.pokemon.getMaxHp(), null, false, false, false, true), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + this.pokemon.getBattlerIndex(), + this.pokemon.getMaxHp(), + null, + false, + false, + false, + true, ); this.pokemon.findAndRemoveTags(() => true); this.pokemon.bossSegments = 5; diff --git a/src/phases/revival-blessing-phase.ts b/src/phases/revival-blessing-phase.ts index 2db73103a88..e3e69f7ef25 100644 --- a/src/phases/revival-blessing-phase.ts +++ b/src/phases/revival-blessing-phase.ts @@ -6,8 +6,6 @@ import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import { toDmgValue, isNullOrUndefined } from "#app/utils/common"; import { BattlePhase } from "#app/phases/battle-phase"; -import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; -import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; import type { PlayerPokemon } from "#app/field/pokemon"; /** @@ -51,16 +49,26 @@ export class RevivalBlessingPhase extends BattlePhase { ) { if (slotIndex <= 1) { // Revived ally pokemon - globalScene.phaseManager.unshiftPhase( - new SwitchSummonPhase(SwitchType.SWITCH, pokemon.getFieldIndex(), slotIndex, false, true), + globalScene.phaseManager.unshiftNew( + "SwitchSummonPhase", + SwitchType.SWITCH, + pokemon.getFieldIndex(), + slotIndex, + false, + true, ); - globalScene.phaseManager.unshiftPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.unshiftNew("ToggleDoublePositionPhase", true); } else if (allyPokemon.isFainted()) { // Revived party pokemon, and ally pokemon is fainted - globalScene.phaseManager.unshiftPhase( - new SwitchSummonPhase(SwitchType.SWITCH, allyPokemon.getFieldIndex(), slotIndex, false, true), + globalScene.phaseManager.unshiftNew( + "SwitchSummonPhase", + SwitchType.SWITCH, + allyPokemon.getFieldIndex(), + slotIndex, + false, + true, ); - globalScene.phaseManager.unshiftPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.unshiftNew("ToggleDoublePositionPhase", true); } } } diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index 633bf20d34c..e8b4946b6d1 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -6,8 +6,6 @@ import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler" import { UiMode } from "#enums/ui-mode"; import { BattlePhase } from "./battle-phase"; import { randSeedInt } from "#app/utils/common"; -import { PartyHealPhase } from "./party-heal-phase"; -import { SwitchBiomePhase } from "./switch-biome-phase"; export class SelectBiomePhase extends BattlePhase { public readonly phaseName = "SelectBiomePhase"; @@ -22,9 +20,9 @@ export class SelectBiomePhase extends BattlePhase { const setNextBiome = (nextBiome: BiomeId) => { if (nextWaveIndex % 10 === 1) { globalScene.applyModifiers(MoneyInterestModifier, true); - globalScene.phaseManager.unshiftPhase(new PartyHealPhase(false)); + globalScene.phaseManager.unshiftNew("PartyHealPhase", false); } - globalScene.phaseManager.unshiftPhase(new SwitchBiomePhase(nextBiome)); + globalScene.phaseManager.unshiftNew("SwitchBiomePhase", nextBiome); this.end(); }; diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts index 5ac3b9e6d76..4d790e70e1b 100644 --- a/src/phases/select-modifier-phase.ts +++ b/src/phases/select-modifier-phase.ts @@ -123,11 +123,10 @@ export class SelectModifierPhase extends BattlePhase { return false; } globalScene.reroll = true; - globalScene.phaseManager.unshiftPhase( - new SelectModifierPhase( - this.rerollCount + 1, - this.typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as ModifierTier[], - ), + globalScene.phaseManager.unshiftNew( + "SelectModifierPhase", + this.rerollCount + 1, + this.typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as ModifierTier[], ); globalScene.ui.clearText(); globalScene.ui.setMode(UiMode.MESSAGE).then(() => super.end()); @@ -419,7 +418,8 @@ export class SelectModifierPhase extends BattlePhase { } copy(): SelectModifierPhase { - return new SelectModifierPhase( + return globalScene.phaseManager.create( + "SelectModifierPhase", this.rerollCount, this.modifierTiers, { diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts index 2b60fcaf054..88d4fe06a9b 100644 --- a/src/phases/select-starter-phase.ts +++ b/src/phases/select-starter-phase.ts @@ -6,7 +6,6 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { overrideHeldItems, overrideModifiers } from "#app/modifier/modifier"; import Overrides from "#app/overrides"; import { Phase } from "#app/phase"; -import { TitlePhase } from "#app/phases/title-phase"; import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler"; import type { Starter } from "#app/ui/starter-select-ui-handler"; import { UiMode } from "#enums/ui-mode"; @@ -26,7 +25,7 @@ export class SelectStarterPhase extends Phase { globalScene.ui.setMode(UiMode.SAVE_SLOT, SaveSlotUiMode.SAVE, (slotId: number) => { if (slotId === -1) { globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.pushPhase(new TitlePhase()); + globalScene.phaseManager.pushNew("TitlePhase"); return this.end(); } globalScene.sessionSlotId = slotId; diff --git a/src/phases/select-target-phase.ts b/src/phases/select-target-phase.ts index 515f7ed98ca..fcbd3aeb679 100644 --- a/src/phases/select-target-phase.ts +++ b/src/phases/select-target-phase.ts @@ -2,7 +2,6 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#app/battle"; import { Command } from "#app/ui/command-ui-handler"; import { UiMode } from "#enums/ui-mode"; -import { CommandPhase } from "./command-phase"; import { PokemonPhase } from "./pokemon-phase"; import i18next from "#app/plugins/i18n"; import { allMoves } from "#app/data/data-lists"; @@ -33,7 +32,7 @@ export class SelectTargetPhase extends PokemonPhase { } if (targets.length < 1) { globalScene.currentBattle.turnCommands[this.fieldIndex] = null; - globalScene.phaseManager.unshiftPhase(new CommandPhase(this.fieldIndex)); + globalScene.phaseManager.unshiftNew("CommandPhase", this.fieldIndex); } else { turnCommand!.targets = targets; //TODO: is the bang correct here? } diff --git a/src/phases/show-ability-phase.ts b/src/phases/show-ability-phase.ts index e4be6124784..af295b72622 100644 --- a/src/phases/show-ability-phase.ts +++ b/src/phases/show-ability-phase.ts @@ -2,7 +2,6 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#app/battle"; import { PokemonPhase } from "./pokemon-phase"; import { getPokemonNameWithAffix } from "#app/messages"; -import { HideAbilityPhase } from "#app/phases/hide-ability-phase"; export class ShowAbilityPhase extends PokemonPhase { public readonly phaseName = "ShowAbilityPhase"; @@ -36,8 +35,8 @@ export class ShowAbilityPhase extends PokemonPhase { // If the bar is already out, hide it before showing the new one if (globalScene.abilityBar.isVisible()) { - globalScene.phaseManager.unshiftPhase(new HideAbilityPhase()); - globalScene.phaseManager.unshiftPhase(new ShowAbilityPhase(this.battlerIndex, this.passive)); + globalScene.phaseManager.unshiftNew("HideAbilityPhase"); + globalScene.phaseManager.unshiftNew("ShowAbilityPhase", this.battlerIndex, this.passive); return this.end(); } diff --git a/src/phases/show-party-exp-bar-phase.ts b/src/phases/show-party-exp-bar-phase.ts index 765bd498f66..4849526b639 100644 --- a/src/phases/show-party-exp-bar-phase.ts +++ b/src/phases/show-party-exp-bar-phase.ts @@ -3,8 +3,6 @@ import { ExpGainsSpeed } from "#app/enums/exp-gains-speed"; import { ExpNotification } from "#app/enums/exp-notification"; import { ExpBoosterModifier } from "#app/modifier/modifier"; import { NumberHolder } from "#app/utils/common"; -import { HidePartyExpBarPhase } from "./hide-party-exp-bar-phase"; -import { LevelUpPhase } from "./level-up-phase"; import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase { @@ -29,9 +27,9 @@ export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase { pokemon.addExp(exp.value); const newLevel = pokemon.level; if (newLevel > lastLevel) { - globalScene.phaseManager.unshiftPhase(new LevelUpPhase(this.partyMemberIndex, lastLevel, newLevel)); + globalScene.phaseManager.unshiftNew("LevelUpPhase", this.partyMemberIndex, lastLevel, newLevel); } - globalScene.phaseManager.unshiftPhase(new HidePartyExpBarPhase()); + globalScene.phaseManager.unshiftNew("HidePartyExpBarPhase"); pokemon.updateInfo(); if (globalScene.expParty === ExpNotification.SKIP) { diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index 49f3952ef01..ad2eeae1c48 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -72,18 +72,17 @@ export class StatStageChangePhase extends PokemonPhase { if (this.stats.length > 1) { for (let i = 0; i < this.stats.length; i++) { const stat = [this.stats[i]]; - globalScene.phaseManager.unshiftPhase( - new StatStageChangePhase( - this.battlerIndex, - this.selfTarget, - stat, - this.stages, - this.showMessage, - this.ignoreAbilities, - this.canBeCopied, - this.onChange, - this.comingFromMirrorArmorUser, - ), + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + this.battlerIndex, + this.selfTarget, + stat, + this.stages, + this.showMessage, + this.ignoreAbilities, + this.canBeCopied, + this.onChange, + this.comingFromMirrorArmorUser, ); } return this.end(); diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index 15f42e76a5a..921466dfead 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -9,9 +9,6 @@ import { FieldPosition } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import i18next from "i18next"; import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase"; -import { PostSummonPhase } from "./post-summon-phase"; -import { GameOverPhase } from "./game-over-phase"; -import { ShinySparklePhase } from "./shiny-sparkle-phase"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import { applyPreSummonAbAttrs, PreSummonAbAttr } from "#app/data/abilities/ability"; import { globalScene } from "#app/global-scene"; @@ -58,7 +55,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { console.error("Party Details:\n", party); console.error("All available Pokemon were fainted or illegal!"); globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.unshiftNew("GameOverPhase"); this.end(); return; } @@ -275,7 +272,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { const pokemon = this.getPokemon(); if (pokemon.isShiny(true)) { - globalScene.phaseManager.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex())); + globalScene.phaseManager.unshiftNew("ShinySparklePhase", pokemon.getBattlerIndex()); } pokemon.resetTurnData(); @@ -291,7 +288,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { } queuePostSummon(): void { - globalScene.phaseManager.pushPhase(new PostSummonPhase(this.getPokemon().getBattlerIndex())); + globalScene.phaseManager.pushNew("PostSummonPhase", this.getPokemon().getBattlerIndex()); } end() { diff --git a/src/phases/switch-phase.ts b/src/phases/switch-phase.ts index 5f2ae55900e..8d18e29e6a6 100644 --- a/src/phases/switch-phase.ts +++ b/src/phases/switch-phase.ts @@ -3,7 +3,6 @@ import PartyUiHandler, { PartyOption, PartyUiMode } from "#app/ui/party-ui-handl import { UiMode } from "#enums/ui-mode"; import { SwitchType } from "#enums/switch-type"; import { BattlePhase } from "./battle-phase"; -import { SwitchSummonPhase } from "./switch-summon-phase"; /** * Opens the party selector UI and transitions into a {@linkcode SwitchSummonPhase} @@ -80,9 +79,7 @@ export class SwitchPhase extends BattlePhase { p => p.is("PostSummonPhase") && p.player && p.fieldIndex === this.fieldIndex, ); const switchType = option === PartyOption.PASS_BATON ? SwitchType.BATON_PASS : this.switchType; - globalScene.phaseManager.unshiftPhase( - new SwitchSummonPhase(switchType, fieldIndex, slotIndex, this.doReturn), - ); + globalScene.phaseManager.unshiftNew("SwitchSummonPhase", switchType, fieldIndex, slotIndex, this.doReturn); } globalScene.ui.setMode(UiMode.MESSAGE).then(() => super.end()); }, diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index de8b9f3a5d9..103af3db275 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -16,7 +16,6 @@ import { getPokemonNameWithAffix } from "#app/messages"; import { SwitchEffectTransferModifier } from "#app/modifier/modifier"; import { Command } from "#app/ui/command-ui-handler"; import i18next from "i18next"; -import { PostSummonPhase } from "./post-summon-phase"; import { SummonPhase } from "./summon-phase"; import { SubstituteTag } from "#app/data/battler-tags"; import { SwitchType } from "#enums/switch-type"; @@ -265,6 +264,6 @@ export class SwitchSummonPhase extends SummonPhase { } queuePostSummon(): void { - globalScene.phaseManager.unshiftPhase(new PostSummonPhase(this.getPokemon().getBattlerIndex())); + globalScene.phaseManager.unshiftNew("PostSummonPhase", this.getPokemon().getBattlerIndex()); } } diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index fb980d87359..37ce294f237 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -20,11 +20,6 @@ import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler"; import { UiMode } from "#enums/ui-mode"; import { isLocal, isLocalServerConnected, isNullOrUndefined } from "#app/utils/common"; import i18next from "i18next"; -import { CheckSwitchPhase } from "./check-switch-phase"; -import { EncounterPhase } from "./encounter-phase"; -import { SelectChallengePhase } from "./select-challenge-phase"; -import { SelectStarterPhase } from "./select-starter-phase"; -import { SummonPhase } from "./summon-phase"; import { globalScene } from "#app/global-scene"; import Overrides from "#app/overrides"; @@ -125,7 +120,7 @@ export class TitlePhase extends Phase { label: i18next.t("menu:cancel"), handler: () => { globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.pushPhase(new TitlePhase()); + globalScene.phaseManager.pushNew("TitlePhase"); super.end(); return true; }, @@ -200,7 +195,7 @@ export class TitlePhase extends Phase { globalScene.ui.setMode(UiMode.SAVE_SLOT, SaveSlotUiMode.SAVE, (slotId: number) => { globalScene.phaseManager.clearPhaseQueue(); if (slotId === -1) { - globalScene.phaseManager.pushPhase(new TitlePhase()); + globalScene.phaseManager.pushNew("TitlePhase"); return super.end(); } globalScene.sessionSlotId = slotId; @@ -304,23 +299,23 @@ export class TitlePhase extends Phase { globalScene.arena.preloadBgm(); globalScene.gameMode = getGameMode(this.gameMode); if (this.gameMode === GameModes.CHALLENGE) { - globalScene.phaseManager.pushPhase(new SelectChallengePhase()); + globalScene.phaseManager.pushNew("SelectChallengePhase"); } else { - globalScene.phaseManager.pushPhase(new SelectStarterPhase()); + globalScene.phaseManager.pushNew("SelectStarterPhase"); } globalScene.newArena(globalScene.gameMode.getStartingBiome()); } else { globalScene.playBgm(); } - globalScene.phaseManager.pushPhase(new EncounterPhase(this.loaded)); + globalScene.phaseManager.pushNew("EncounterPhase", this.loaded); if (this.loaded) { const availablePartyMembers = globalScene.getPokemonAllowedInBattle().length; - globalScene.phaseManager.pushPhase(new SummonPhase(0, true, true)); + globalScene.phaseManager.pushNew("SummonPhase", 0, true, true); if (globalScene.currentBattle.double && availablePartyMembers > 1) { - globalScene.phaseManager.pushPhase(new SummonPhase(1, true, true)); + globalScene.phaseManager.pushNew("SummonPhase", 1, true, true); } if ( @@ -329,9 +324,9 @@ export class TitlePhase extends Phase { ) { const minPartySize = globalScene.currentBattle.double ? 2 : 1; if (availablePartyMembers > minPartySize) { - globalScene.phaseManager.pushPhase(new CheckSwitchPhase(0, globalScene.currentBattle.double)); + globalScene.phaseManager.pushNew("CheckSwitchPhase", 0, globalScene.currentBattle.double); if (globalScene.currentBattle.double) { - globalScene.phaseManager.pushPhase(new CheckSwitchPhase(1, globalScene.currentBattle.double)); + globalScene.phaseManager.pushNew("CheckSwitchPhase", 1, globalScene.currentBattle.double); } } } diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts index 5b7b26d52fb..5d35dd5d375 100644 --- a/src/phases/trainer-victory-phase.ts +++ b/src/phases/trainer-victory-phase.ts @@ -5,8 +5,6 @@ import { vouchers } from "#app/system/voucher"; import i18next from "i18next"; import { randSeedItem } from "#app/utils/common"; import { BattlePhase } from "./battle-phase"; -import { ModifierRewardPhase } from "./modifier-reward-phase"; -import { MoneyRewardPhase } from "./money-reward-phase"; import { TrainerSlot } from "#enums/trainer-slot"; import { globalScene } from "#app/global-scene"; import { BiomeId } from "#enums/biome-id"; @@ -20,13 +18,11 @@ export class TrainerVictoryPhase extends BattlePhase { globalScene.playBgm(globalScene.currentBattle.trainer?.config.victoryBgm); - globalScene.phaseManager.unshiftPhase( - new MoneyRewardPhase(globalScene.currentBattle.trainer?.config.moneyMultiplier!), - ); // TODO: is this bang correct? + globalScene.phaseManager.unshiftNew("MoneyRewardPhase", globalScene.currentBattle.trainer?.config.moneyMultiplier!); // TODO: is this bang correct? const modifierRewardFuncs = globalScene.currentBattle.trainer?.config.modifierRewardFuncs!; // TODO: is this bang correct? for (const modifierRewardFunc of modifierRewardFuncs) { - globalScene.phaseManager.unshiftPhase(new ModifierRewardPhase(modifierRewardFunc)); + globalScene.phaseManager.unshiftNew("ModifierRewardPhase", modifierRewardFunc); } const trainerType = globalScene.currentBattle.trainer?.config.trainerType!; // TODO: is this bang correct? @@ -37,23 +33,21 @@ export class TrainerVictoryPhase extends BattlePhase { globalScene.currentBattle.trainer?.config.isBoss ) { if (timedEventManager.getUpgradeUnlockedVouchers()) { - globalScene.phaseManager.unshiftPhase( - new ModifierRewardPhase( - [ - modifierTypes.VOUCHER_PLUS, - modifierTypes.VOUCHER_PLUS, - modifierTypes.VOUCHER_PLUS, - modifierTypes.VOUCHER_PREMIUM, - ][vouchers[TrainerType[trainerType]].voucherType], - ), + globalScene.phaseManager.unshiftNew( + "ModifierRewardPhase", + [ + modifierTypes.VOUCHER_PLUS, + modifierTypes.VOUCHER_PLUS, + modifierTypes.VOUCHER_PLUS, + modifierTypes.VOUCHER_PREMIUM, + ][vouchers[TrainerType[trainerType]].voucherType], ); } else { - globalScene.phaseManager.unshiftPhase( - new ModifierRewardPhase( - [modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][ - vouchers[TrainerType[trainerType]].voucherType - ], - ), + globalScene.phaseManager.unshiftNew( + "ModifierRewardPhase", + [modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][ + vouchers[TrainerType[trainerType]].voucherType + ], ); } } diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index 832f60043ce..85590d667d6 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -14,7 +14,6 @@ import { } from "#app/modifier/modifier"; import i18next from "i18next"; import { FieldPhase } from "./field-phase"; -import { PokemonHealPhase } from "./pokemon-heal-phase"; import { globalScene } from "#app/global-scene"; export class TurnEndPhase extends FieldPhase { @@ -34,15 +33,14 @@ export class TurnEndPhase extends FieldPhase { globalScene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon); if (globalScene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) { - globalScene.phaseManager.unshiftPhase( - new PokemonHealPhase( - pokemon.getBattlerIndex(), - Math.max(pokemon.getMaxHp() >> 4, 1), - i18next.t("battle:turnEndHpRestore", { - pokemonName: getPokemonNameWithAffix(pokemon), - }), - true, - ), + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + pokemon.getBattlerIndex(), + Math.max(pokemon.getMaxHp() >> 4, 1), + i18next.t("battle:turnEndHpRestore", { + pokemonName: getPokemonNameWithAffix(pokemon), + }), + true, ); } diff --git a/src/phases/turn-init-phase.ts b/src/phases/turn-init-phase.ts index 61ec5fd8a71..e9d6f60af5e 100644 --- a/src/phases/turn-init-phase.ts +++ b/src/phases/turn-init-phase.ts @@ -6,12 +6,7 @@ import { import { TurnInitEvent } from "#app/events/battle-scene"; import type { PlayerPokemon } from "#app/field/pokemon"; import i18next from "i18next"; -import { CommandPhase } from "./command-phase"; -import { EnemyCommandPhase } from "./enemy-command-phase"; import { FieldPhase } from "./field-phase"; -import { GameOverPhase } from "./game-over-phase"; -import { ToggleDoublePositionPhase } from "./toggle-double-position-phase"; -import { TurnStartPhase } from "./turn-start-phase"; import { globalScene } from "#app/global-scene"; export class TurnInitPhase extends FieldPhase { @@ -33,7 +28,7 @@ export class TurnInitPhase extends FieldPhase { if (!allowedPokemon.length) { // If there are no longer any legal pokemon in the party, game over. globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.unshiftPhase(new GameOverPhase()); + globalScene.phaseManager.unshiftNew("GameOverPhase"); } else if ( allowedPokemon.length >= globalScene.currentBattle.getBattlerCount() || (globalScene.currentBattle.double && !allowedPokemon[0].isActive(true)) @@ -46,7 +41,7 @@ export class TurnInitPhase extends FieldPhase { p.leaveField(); } if (allowedPokemon.length === 1 && globalScene.currentBattle.double) { - globalScene.phaseManager.unshiftPhase(new ToggleDoublePositionPhase(true)); + globalScene.phaseManager.unshiftNew("ToggleDoublePositionPhase", true); } } }); @@ -69,13 +64,15 @@ export class TurnInitPhase extends FieldPhase { pokemon.resetTurnData(); - globalScene.phaseManager.pushPhase( - pokemon.isPlayer() ? new CommandPhase(i) : new EnemyCommandPhase(i - BattlerIndex.ENEMY), - ); + if (pokemon.isPlayer()) { + globalScene.phaseManager.pushNew("CommandPhase", i); + } else { + globalScene.phaseManager.pushNew("EnemyCommandPhase", i - BattlerIndex.ENEMY); + } } }); - globalScene.phaseManager.pushPhase(new TurnStartPhase()); + globalScene.phaseManager.pushNew("TurnStartPhase"); this.end(); } diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index c07feac0888..557b67b6091 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -8,21 +8,11 @@ import { PokemonMove } from "#app/field/pokemon"; import { BypassSpeedChanceModifier } from "#app/modifier/modifier"; import { Command } from "#app/ui/command-ui-handler"; import { randSeedShuffle, BooleanHolder } from "#app/utils/common"; -import { AttemptCapturePhase } from "./attempt-capture-phase"; -import { AttemptRunPhase } from "./attempt-run-phase"; -import { BerryPhase } from "./berry-phase"; import { FieldPhase } from "./field-phase"; -import { MoveHeaderPhase } from "./move-header-phase"; -import { MovePhase } from "./move-phase"; -import { SwitchSummonPhase } from "./switch-summon-phase"; -import { TurnEndPhase } from "./turn-end-phase"; -import { WeatherEffectPhase } from "./weather-effect-phase"; -import { CheckStatusEffectPhase } from "#app/phases/check-status-effect-phase"; import { BattlerIndex } from "#app/battle"; import { TrickRoomTag } from "#app/data/arena-tag"; import { SwitchType } from "#enums/switch-type"; import { globalScene } from "#app/global-scene"; -import { TeraPhase } from "./tera-phase"; export class TurnStartPhase extends FieldPhase { public readonly phaseName = "TurnStartPhase"; @@ -153,10 +143,12 @@ export class TurnStartPhase extends FieldPhase { switch (preTurnCommand?.command) { case Command.TERA: - globalScene.phaseManager.pushPhase(new TeraPhase(pokemon)); + globalScene.phaseManager.pushNew("TeraPhase", pokemon); } } + const phaseManager = globalScene.phaseManager; + for (const o of moveOrder) { const pokemon = field[o]; const turnCommand = globalScene.currentBattle.turnCommands[o]; @@ -167,88 +159,94 @@ export class TurnStartPhase extends FieldPhase { switch (turnCommand?.command) { case Command.FIGHT: - const queuedMove = turnCommand.move; - pokemon.turnData.order = orderIndex++; - if (!queuedMove) { - continue; - } - const move = - pokemon.getMoveset().find(m => m.moveId === queuedMove.move && m.ppUsed < m.getMovePp()) || - new PokemonMove(queuedMove.move); - if (move.getMove().hasAttr(MoveHeaderAttr)) { - globalScene.phaseManager.unshiftPhase(new MoveHeaderPhase(pokemon, move)); - } - if (pokemon.isPlayer()) { - if (turnCommand.cursor === -1) { - globalScene.phaseManager.pushPhase( - new MovePhase(pokemon, turnCommand.targets || turnCommand.move!.targets, move), - ); //TODO: is the bang correct here? - } else { - const playerPhase = new MovePhase( - pokemon, - turnCommand.targets || turnCommand.move!.targets, - move, - false, - queuedMove.ignorePP, - ); //TODO: is the bang correct here? - globalScene.phaseManager.pushPhase(playerPhase); + { + const queuedMove = turnCommand.move; + pokemon.turnData.order = orderIndex++; + if (!queuedMove) { + continue; } - } else { - globalScene.phaseManager.pushPhase( - new MovePhase( + const move = + pokemon.getMoveset().find(m => m.moveId === queuedMove.move && m.ppUsed < m.getMovePp()) || + new PokemonMove(queuedMove.move); + if (move.getMove().hasAttr(MoveHeaderAttr)) { + phaseManager.unshiftNew("MoveHeaderPhase", pokemon, move); + } + if (pokemon.isPlayer()) { + if (turnCommand.cursor === -1) { + phaseManager.pushNew("MovePhase", pokemon, turnCommand.targets || turnCommand.move!.targets, move); + } else { + phaseManager.pushNew( + "MovePhase", + pokemon, + turnCommand.targets || turnCommand.move!.targets, // TODO: is the bang correct here? + move, + false, + queuedMove.ignorePP, + ); + } + } else { + phaseManager.pushNew( + "MovePhase", pokemon, turnCommand.targets || turnCommand.move!.targets, move, false, queuedMove.ignorePP, - ), - ); //TODO: is the bang correct here? + ); + } } break; case Command.BALL: - globalScene.phaseManager.unshiftPhase( - new AttemptCapturePhase(turnCommand.targets![0] % 2, turnCommand.cursor!), - ); //TODO: is the bang correct here? + phaseManager.unshiftNew("AttemptCapturePhase", turnCommand.targets![0] % 2, turnCommand.cursor!); //TODO: is the bang correct here? break; case Command.POKEMON: - const switchType = turnCommand.args?.[0] ? SwitchType.BATON_PASS : SwitchType.SWITCH; - globalScene.phaseManager.unshiftPhase( - new SwitchSummonPhase(switchType, pokemon.getFieldIndex(), turnCommand.cursor!, true, pokemon.isPlayer()), - ); + { + const switchType = turnCommand.args?.[0] ? SwitchType.BATON_PASS : SwitchType.SWITCH; + phaseManager.unshiftNew( + "SwitchSummonPhase", + switchType, + pokemon.getFieldIndex(), + turnCommand.cursor!, + true, + pokemon.isPlayer(), + ); + } break; case Command.RUN: - let runningPokemon = pokemon; - if (globalScene.currentBattle.double) { - const playerActivePokemon = field.filter(pokemon => { - if (pokemon) { - return pokemon.isPlayer() && pokemon.isActive(); + { + let runningPokemon = pokemon; + if (globalScene.currentBattle.double) { + const playerActivePokemon = field.filter(pokemon => { + if (pokemon) { + return pokemon.isPlayer() && pokemon.isActive(); + } + return; + }); + // 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]; + // check if either active pokemon has the ability "Run Away" + const hasRunAway = playerActivePokemon.find(p => p.hasAbility(AbilityId.RUN_AWAY)); + runningPokemon = hasRunAway !== undefined ? hasRunAway : fasterPokemon; } - return; - }); - // 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]; - // check if either active pokemon has the ability "Run Away" - const hasRunAway = playerActivePokemon.find(p => p.hasAbility(AbilityId.RUN_AWAY)); - runningPokemon = hasRunAway !== undefined ? hasRunAway : fasterPokemon; } + phaseManager.unshiftNew("AttemptRunPhase", runningPokemon.getFieldIndex()); } - globalScene.phaseManager.unshiftPhase(new AttemptRunPhase(runningPokemon.getFieldIndex())); break; } } - globalScene.phaseManager.pushPhase(new WeatherEffectPhase()); - globalScene.phaseManager.pushPhase(new BerryPhase()); + phaseManager.pushNew("WeatherEffectPhase"); + phaseManager.pushNew("BerryPhase"); /** Add a new phase to check who should be taking status damage */ - globalScene.phaseManager.pushPhase(new CheckStatusEffectPhase(moveOrder)); + phaseManager.pushNew("CheckStatusEffectPhase", moveOrder); - globalScene.phaseManager.pushPhase(new TurnEndPhase()); + phaseManager.pushNew("TurnEndPhase"); /** * this.end() will call shiftPhase(), which dumps everything from PrependQueue (aka everything that is unshifted()) to the front diff --git a/src/phases/unavailable-phase.ts b/src/phases/unavailable-phase.ts index a6fc4a1be61..8b5bb5f7508 100644 --- a/src/phases/unavailable-phase.ts +++ b/src/phases/unavailable-phase.ts @@ -1,13 +1,12 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; import { UiMode } from "#enums/ui-mode"; -import { LoginPhase } from "./login-phase"; export class UnavailablePhase extends Phase { public readonly phaseName = "UnavailablePhase"; start(): void { globalScene.ui.setMode(UiMode.UNAVAILABLE, () => { - globalScene.phaseManager.unshiftPhase(new LoginPhase(true)); + globalScene.phaseManager.unshiftNew("LoginPhase", true); this.end(); }); } diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index 0a08e010720..ca24e474cde 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -3,19 +3,10 @@ import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; import { BattleType } from "#enums/battle-type"; import type { CustomModifierSettings } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { BattleEndPhase } from "./battle-end-phase"; -import { NewBattlePhase } from "./new-battle-phase"; import { PokemonPhase } from "./pokemon-phase"; -import { AddEnemyBuffModifierPhase } from "./add-enemy-buff-modifier-phase"; -import { EggLapsePhase } from "./egg-lapse-phase"; -import { GameOverPhase } from "./game-over-phase"; -import { ModifierRewardPhase } from "./modifier-reward-phase"; -import { SelectModifierPhase } from "./select-modifier-phase"; -import { TrainerVictoryPhase } from "./trainer-victory-phase"; import { handleMysteryEncounterVictory } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { globalScene } from "#app/global-scene"; import { timedEventManager } from "#app/global-event-manager"; -import { SelectBiomePhase } from "./select-biome-phase"; export class VictoryPhase extends PokemonPhase { public readonly phaseName = "VictoryPhase"; @@ -51,12 +42,12 @@ export class VictoryPhase extends PokemonPhase { .getEnemyParty() .find(p => (globalScene.currentBattle.battleType === BattleType.WILD ? p.isOnField() : !p?.isFainted(true))) ) { - globalScene.phaseManager.pushPhase(new BattleEndPhase(true)); + globalScene.phaseManager.pushNew("BattleEndPhase", true); if (globalScene.currentBattle.battleType === BattleType.TRAINER) { - globalScene.phaseManager.pushPhase(new TrainerVictoryPhase()); + globalScene.phaseManager.pushNew("TrainerVictoryPhase"); } if (globalScene.gameMode.isEndless || !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) { - globalScene.phaseManager.pushPhase(new EggLapsePhase()); + globalScene.phaseManager.pushNew("EggLapsePhase"); if (globalScene.gameMode.isClassic) { switch (globalScene.currentBattle.waveIndex) { case ClassicFixedBossWaves.RIVAL_1: @@ -64,68 +55,67 @@ export class VictoryPhase extends PokemonPhase { // Get event modifiers for this wave timedEventManager .getFixedBattleEventRewards(globalScene.currentBattle.waveIndex) - .map(r => globalScene.phaseManager.pushPhase(new ModifierRewardPhase(modifierTypes[r]))); + .map(r => globalScene.phaseManager.pushNew("ModifierRewardPhase", modifierTypes[r])); break; case ClassicFixedBossWaves.EVIL_BOSS_2: // Should get Lock Capsule on 165 before shop phase so it can be used in the rewards shop - globalScene.phaseManager.pushPhase(new ModifierRewardPhase(modifierTypes.LOCK_CAPSULE)); + globalScene.phaseManager.pushNew("ModifierRewardPhase", modifierTypes.LOCK_CAPSULE); break; } } if (globalScene.currentBattle.waveIndex % 10) { - globalScene.phaseManager.pushPhase( - new SelectModifierPhase(undefined, undefined, this.getFixedBattleCustomModifiers()), + globalScene.phaseManager.pushNew( + "SelectModifierPhase", + undefined, + undefined, + this.getFixedBattleCustomModifiers(), ); } else if (globalScene.gameMode.isDaily) { - globalScene.phaseManager.pushPhase(new ModifierRewardPhase(modifierTypes.EXP_CHARM)); + globalScene.phaseManager.pushNew("ModifierRewardPhase", modifierTypes.EXP_CHARM); if ( globalScene.currentBattle.waveIndex > 10 && !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex) ) { - globalScene.phaseManager.pushPhase(new ModifierRewardPhase(modifierTypes.GOLDEN_POKEBALL)); + globalScene.phaseManager.pushNew("ModifierRewardPhase", modifierTypes.GOLDEN_POKEBALL); } } else { const superExpWave = !globalScene.gameMode.isEndless ? (globalScene.offsetGym ? 0 : 20) : 10; if (globalScene.gameMode.isEndless && globalScene.currentBattle.waveIndex === 10) { - globalScene.phaseManager.pushPhase(new ModifierRewardPhase(modifierTypes.EXP_SHARE)); + globalScene.phaseManager.pushNew("ModifierRewardPhase", modifierTypes.EXP_SHARE); } if ( globalScene.currentBattle.waveIndex <= 750 && (globalScene.currentBattle.waveIndex <= 500 || globalScene.currentBattle.waveIndex % 30 === superExpWave) ) { - globalScene.phaseManager.pushPhase( - new ModifierRewardPhase( - globalScene.currentBattle.waveIndex % 30 !== superExpWave || globalScene.currentBattle.waveIndex > 250 - ? modifierTypes.EXP_CHARM - : modifierTypes.SUPER_EXP_CHARM, - ), + globalScene.phaseManager.pushNew( + "ModifierRewardPhase", + globalScene.currentBattle.waveIndex % 30 !== superExpWave || globalScene.currentBattle.waveIndex > 250 + ? modifierTypes.EXP_CHARM + : modifierTypes.SUPER_EXP_CHARM, ); } if (globalScene.currentBattle.waveIndex <= 150 && !(globalScene.currentBattle.waveIndex % 50)) { - globalScene.phaseManager.pushPhase(new ModifierRewardPhase(modifierTypes.GOLDEN_POKEBALL)); + globalScene.phaseManager.pushNew("ModifierRewardPhase", modifierTypes.GOLDEN_POKEBALL); } if (globalScene.gameMode.isEndless && !(globalScene.currentBattle.waveIndex % 50)) { - globalScene.phaseManager.pushPhase( - new ModifierRewardPhase( - !(globalScene.currentBattle.waveIndex % 250) - ? modifierTypes.VOUCHER_PREMIUM - : modifierTypes.VOUCHER_PLUS, - ), + globalScene.phaseManager.pushNew( + "ModifierRewardPhase", + !(globalScene.currentBattle.waveIndex % 250) ? modifierTypes.VOUCHER_PREMIUM : modifierTypes.VOUCHER_PLUS, ); - globalScene.phaseManager.pushPhase(new AddEnemyBuffModifierPhase()); + globalScene.phaseManager.pushNew("AddEnemyBuffModifierPhase"); } } if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { - globalScene.phaseManager.pushPhase(new SelectBiomePhase()); + globalScene.phaseManager.pushNew("SelectBiomePhase"); } - globalScene.phaseManager.pushPhase(new NewBattlePhase()); + globalScene.phaseManager.pushNew("NewBattlePhase"); } else { globalScene.currentBattle.battleType = BattleType.CLEAR; globalScene.score += globalScene.gameMode.getClearScoreBonus(); globalScene.updateScoreText(); - globalScene.phaseManager.pushPhase(new GameOverPhase(true)); + globalScene.phaseManager.pushNew("GameOverPhase", true); } } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 9a24194b8e9..2949ecd51cf 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -49,7 +49,6 @@ import { SpeciesId } from "#enums/species-id"; import { applyChallenges, ChallengeType } from "#app/data/challenge"; import { WeatherType } from "#enums/weather-type"; import { TerrainType } from "#app/data/terrain"; -import { ReloadSessionPhase } from "#app/phases/reload-session-phase"; import { RUN_HISTORY_LIMIT } from "#app/ui/run-history-ui-handler"; import { applySessionVersionMigration, @@ -427,7 +426,7 @@ export class GameData { if (error) { if (error.startsWith("session out of date")) { globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.unshiftPhase(new ReloadSessionPhase()); + globalScene.phaseManager.unshiftNew("ReloadSessionPhase"); } console.error(error); return resolve(false); @@ -747,7 +746,7 @@ export class GameData { if (systemData) { globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.unshiftPhase(new ReloadSessionPhase(JSON.stringify(systemData))); + globalScene.phaseManager.unshiftNew("ReloadSessionPhase", JSON.stringify(systemData)); this.clearLocalData(); return false; } @@ -1249,7 +1248,7 @@ export class GameData { if (error) { if (error.startsWith("session out of date")) { globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.unshiftPhase(new ReloadSessionPhase()); + globalScene.phaseManager.unshiftNew("ReloadSessionPhase"); } console.error(error); resolve(false); @@ -1321,7 +1320,7 @@ export class GameData { } else { if (jsonResponse?.error?.startsWith("session out of date")) { globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.unshiftPhase(new ReloadSessionPhase()); + globalScene.phaseManager.unshiftNew("ReloadSessionPhase"); } console.error(jsonResponse); @@ -1459,7 +1458,7 @@ export class GameData { if (error) { if (error.startsWith("session out of date")) { globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.unshiftPhase(new ReloadSessionPhase()); + globalScene.phaseManager.unshiftNew("ReloadSessionPhase"); } console.error(error); return resolve(false); diff --git a/src/ui/challenges-select-ui-handler.ts b/src/ui/challenges-select-ui-handler.ts index d2c2dbc6c0d..b02bf4abaef 100644 --- a/src/ui/challenges-select-ui-handler.ts +++ b/src/ui/challenges-select-ui-handler.ts @@ -9,8 +9,6 @@ import { getLocalizedSpriteKey } from "#app/utils/common"; import { Challenges } from "#app/enums/challenges"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { Color, ShadowColor } from "#app/enums/color"; -import { SelectStarterPhase } from "#app/phases/select-starter-phase"; -import { TitlePhase } from "#app/phases/title-phase"; import { globalScene } from "#app/global-scene"; /** @@ -384,14 +382,14 @@ export default class GameChallengesUiHandler extends UiHandler { this.updateChallengeArrows(this.startCursor.visible); } else { globalScene.phaseManager.clearPhaseQueue(); - globalScene.phaseManager.pushPhase(new TitlePhase()); + globalScene.phaseManager.pushNew("TitlePhase"); globalScene.phaseManager.getCurrentPhase()?.end(); } success = true; } else if (button === Button.SUBMIT || button === Button.ACTION) { if (this.hasSelectedChallenge) { if (this.startCursor.visible) { - globalScene.phaseManager.unshiftPhase(new SelectStarterPhase()); + globalScene.phaseManager.unshiftNew("SelectStarterPhase"); globalScene.phaseManager.getCurrentPhase()?.end(); } else { this.startCursor.setVisible(true); diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 973805e4ca1..47226de3354 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -51,9 +51,6 @@ import { StarterContainer } from "#app/ui/starter-container"; import { FilterBar } from "#app/ui/filter-bar"; import { DropDownColumn } from "#enums/drop-down-column"; import { ScrollBar } from "#app/ui/scroll-bar"; -import { SelectChallengePhase } from "#app/phases/select-challenge-phase"; -import { EncounterPhase } from "#app/phases/encounter-phase"; -import { TitlePhase } from "#app/phases/title-phase"; import { AbilityId } from "#enums/ability-id"; import { getPassiveCandyCount, @@ -4307,10 +4304,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler { ui.setMode(UiMode.STARTER_SELECT); globalScene.phaseManager.clearPhaseQueue(); if (globalScene.gameMode.isChallenge) { - globalScene.phaseManager.pushPhase(new SelectChallengePhase()); - globalScene.phaseManager.pushPhase(new EncounterPhase()); + globalScene.phaseManager.pushNew("SelectChallengePhase"); + globalScene.phaseManager.pushNew("EncounterPhase"); } else { - globalScene.phaseManager.pushPhase(new TitlePhase()); + globalScene.phaseManager.pushNew("TitlePhase"); } this.clearText(); globalScene.phaseManager.getCurrentPhase()?.end(); diff --git a/test/testUtils/gameManager.ts b/test/testUtils/gameManager.ts index edf0301447d..437c8d9f083 100644 --- a/test/testUtils/gameManager.ts +++ b/test/testUtils/gameManager.ts @@ -105,8 +105,8 @@ export default class GameManager { // Must be run after phase interceptor has been initialized. - this.scene.phaseManager.pushPhase(new LoginPhase()); - this.scene.phaseManager.pushPhase(new TitlePhase()); + this.scene.phaseManager.pushNew("LoginPhase"); + this.scene.phaseManager.pushNew("TitlePhase"); this.scene.phaseManager.shiftPhase(); this.gameWrapper.scene = this.scene;