From 70ba974348dda7f650f078677a8ede2cb7befbc8 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Tue, 1 Apr 2025 15:58:25 -0700 Subject: [PATCH 01/52] [Balance] Remove accuracy cap from Wide Lens --- src/modifier/modifier.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 7c9207bbea5..80f14ba22ce 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -2734,7 +2734,7 @@ export class PokemonMoveAccuracyBoosterModifier extends PokemonHeldItemModifier * @returns always `true` */ override apply(_pokemon: Pokemon, moveAccuracy: NumberHolder): boolean { - moveAccuracy.value = Math.min(moveAccuracy.value + this.accuracyAmount * this.getStackCount(), 100); + moveAccuracy.value = moveAccuracy.value + this.accuracyAmount * this.getStackCount(); return true; } From 420c2e37c21d123e1fb88bb64e68070305efd7c5 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Fri, 4 Apr 2025 17:13:21 -0500 Subject: [PATCH 02/52] [GitHub] Add path filters to avoid unnecessarily re-running tests (#5497) * Add path filters to avoid unnecessarily re-running tests * Apply suggestions from kev's review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update .github/workflows/tests.yml Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Don't ignore image files for tests --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- .github/workflows/tests.yml | 48 +++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 167a108e58c..ccc8604ff7e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,18 +5,58 @@ on: # but only for the main branch push: branches: - - main # Trigger on push events to the main branch + - main # Trigger on push events to the main branch - beta # Trigger on push events to the beta branch + # go upvote https://github.com/actions/runner/issues/1182 and yell at microsoft until they fix this or ditch yml for workflows + paths: + # src and test files + - "src/**" + - "test/**" + - "public/**" + # Workflows that can impact tests + - ".github/workflows/test*.yml" + # top-level files + - "package*.json" + - ".nvrmc" # Updates to node version can break tests + - "vite.*.ts" # vite.config.ts, vite.vitest.config.ts, vitest.workspace.ts + - "tsconfig*.json" # tsconfig.json tweaking can impact compilation + - "global.d.ts" + - ".env.*" + # Blanket negations for files that cannot impact tests + - "!**/*.py" # No .py files + - "!**/*.sh" # No .sh files + - "!**/*.md" # No .md files + - "!**/.git*" # .gitkeep and family + pull_request: branches: - - main # Trigger on pull request events targeting the main branch + - main # Trigger on pull request events targeting the main branch - beta # Trigger on pull request events targeting the beta branch + paths: # go upvote https://github.com/actions/runner/issues/1182 and yell at microsoft because until then we have to duplicate this + # src and test files + - "src/**" + - "test/**" + - "public/**" + # Workflows that can impact tests + - ".github/workflows/test*.yml" + # top-level files + - "package*.json" + - ".nvrmc" # Updates to node version can break tests + - "vite*" # vite.config.ts, vite.vitest.config.ts, vitest.workspace.ts + - "tsconfig*.json" # tsconfig.json tweaking can impact compilation + - "global.d.ts" + - ".env.*" + # Blanket negations for files that cannot impact tests + - "!**/*.py" # No .py files + - "!**/*.sh" # No .sh files + - "!**/*.md" # No .md files + - "!**/.git*" # .gitkeep and family merge_group: types: [checks_requested] jobs: run-tests: - name: Run Tests + name: Run Tests strategy: matrix: shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] @@ -24,4 +64,4 @@ jobs: with: project: main shard: ${{ matrix.shard }} - totalShards: 10 \ No newline at end of file + totalShards: 10 From 9e4162d4299466d15a04185f6e86eb0de0189671 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Fri, 4 Apr 2025 19:40:25 -0500 Subject: [PATCH 03/52] [Bug] Fix super luck implementation (#5625) * Fix super luck implementation * Use numberholder instead of booleanholder * Update tsdoc for getCritStage --- src/data/ability.ts | 14 ++++++++-- src/field/pokemon.ts | 14 +++------- test/abilities/super_luck.test.ts | 43 +++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 test/abilities/super_luck.test.ts diff --git a/src/data/ability.ts b/src/data/ability.ts index eea24c791b0..790bff2290d 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -3499,8 +3499,18 @@ export class BonusCritAbAttr extends AbAttr { constructor() { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.BooleanHolder).value = true; + + /** + * 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 args Args[0] is a number holder containing the crit stage. + */ + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: [Utils.NumberHolder, ...any]): void { + (args[0] as Utils.NumberHolder).value += 1; } } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index f89319a6e30..f3e758e4efd 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1340,8 +1340,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Retrieves the critical-hit stage considering the move used and the Pokemon - * who used it. + * Calculate the critical-hit stage of a move used against this pokemon by + * the given source + * * @param source the {@linkcode Pokemon} who using the move * @param move the {@linkcode Move} being used * @returns the final critical-hit stage value @@ -1360,14 +1361,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { source.isPlayer(), critStage, ); - const bonusCrit = new Utils.BooleanHolder(false); - //@ts-ignore - if (applyAbAttrs(BonusCritAbAttr, source, null, false, bonusCrit)) { - // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus. - if (bonusCrit.value) { - critStage.value += 1; - } - } + applyAbAttrs(BonusCritAbAttr, source, null, false, critStage) const critBoostTag = source.getTag(CritBoostTag); if (critBoostTag) { if (critBoostTag instanceof DragonCheerTag) { diff --git a/test/abilities/super_luck.test.ts b/test/abilities/super_luck.test.ts new file mode 100644 index 00000000000..bc9524de801 --- /dev/null +++ b/test/abilities/super_luck.test.ts @@ -0,0 +1,43 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Abilities - Super Luck", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([Moves.TACKLE]) + .ability(Abilities.SUPER_LUCK) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should increase the crit stage of a user by 1", async () => { + await game.classicMode.startBattle([Species.MAGIKARP]); + const enemy = game.scene.getEnemyPokemon()!; + const fn = vi.spyOn(enemy, "getCritStage"); + game.move.select(Moves.TACKLE); + await game.phaseInterceptor.to("BerryPhase"); + expect(fn).toHaveReturnedWith(1); + fn.mockRestore(); + }); +}); From 5318d717b3035f0e047533961d6a3e22505693dd Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Fri, 4 Apr 2025 19:43:46 -0500 Subject: [PATCH 04/52] [Refactor] [Docs] Minor refactor of `move.checkFlags` into `move.doesFlagEffectApply` (#5264) * Refactor Move.checkFlags * Improve jsdoc clarity * Fix improper recursive call for the IGNORE_PROTECT check * Fix improper placement of followUp check * Get rid of unnecssary break * Fix last import * Remove latent checkFlag call in move-effect-phase * Remedy perish body oversight * Apply kev's suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/ability.ts | 21 ++++++++++----------- src/data/moves/move.ts | 32 ++++++++++++++++++++++++++------ src/phases/move-effect-phase.ts | 5 +++-- src/phases/move-phase.ts | 2 +- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 790bff2290d..a7107ce2e9d 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -998,7 +998,7 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { 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.randSeedInt(this.effects.length)]; - return move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status + return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && !attacker.status && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !move.hitsSubstitute(attacker, pokemon) && attacker.canSetStatus(effect, true, false, pokemon); } @@ -1038,7 +1038,7 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance + return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && pokemon.randSeedInt(100) < this.chance && !move.hitsSubstitute(attacker, pokemon) && attacker.canAddTag(this.tagType); } @@ -1085,7 +1085,7 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return !simulated && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) + return !simulated && move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !move.hitsSubstitute(attacker, pokemon); } @@ -1118,8 +1118,7 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !move.hitsSubstitute(attacker, pokemon)) - && !attacker.getTag(BattlerTagType.PERISH_SONG); + 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 { @@ -1163,7 +1162,7 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) + return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && attacker.getAbility().isSwappable && !move.hitsSubstitute(attacker, pokemon); } @@ -1189,7 +1188,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && attacker.getAbility().isSuppressable + return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && attacker.getAbility().isSuppressable && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr) && !move.hitsSubstitute(attacker, pokemon); } @@ -1220,7 +1219,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { 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.hitsSubstitute(attacker, pokemon) - && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance); + && move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance); } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { @@ -1925,7 +1924,7 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { super.canApplyPostAttack(pokemon, passive, simulated, attacker, move, hitResult, args) && !(pokemon !== attacker && move.hitsSubstitute(attacker, pokemon)) && (simulated || !attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker - && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) + && (!this.contactRequired || move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon})) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) ) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; return simulated || attacker.canSetStatus(effect, true, false, pokemon); @@ -1964,7 +1963,7 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { /**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.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && + (!this.contactRequired || move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon})) && pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status; } @@ -4761,7 +4760,7 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { } override canApplyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): boolean { - const diedToDirectDamage = move !== undefined && attacker !== undefined && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon); + const diedToDirectDamage = move !== undefined && attacker !== undefined && move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}); const cancelled = new Utils.BooleanHolder(false); globalScene.getField(true).map(p => applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled, simulated)); if (!diedToDirectDamage || cancelled.value || attacker!.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 2624fe6cda9..421314b1945 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -346,7 +346,7 @@ export default class Move implements Localizable { * @param target The {@linkcode Pokemon} targeted by this move * @returns `true` if the move can bypass the target's Substitute; `false` otherwise. */ - hitsSubstitute(user: Pokemon, target: Pokemon | null): boolean { + hitsSubstitute(user: Pokemon, target?: Pokemon): boolean { if ([ MoveTarget.USER, MoveTarget.USER_SIDE, MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES ].includes(this.moveTarget) || !target?.getTag(BattlerTagType.SUBSTITUTE)) { return false; @@ -618,12 +618,30 @@ export default class Move implements Localizable { /** * Checks if the move flag applies to the pokemon(s) using/receiving the move + * + * This method will take the `user`'s ability into account when reporting flags, e.g. + * calling this method for {@linkcode MoveFlags.MAKES_CONTACT | MAKES_CONTACT} + * will return `false` if the user has a {@linkcode Abilities.LONG_REACH} that is not being suppressed. + * + * **Note:** This method only checks if the move should have effectively have the flag applied to its use. + * It does *not* check whether the flag will trigger related effects. + * For example using this method to check {@linkcode MoveFlags.WIND_MOVE} + * will not consider {@linkcode Abilities.WIND_RIDER | Wind Rider }. + * + * To simply check whether the move has a flag, use {@linkcode hasFlag}. * @param flag {@linkcode MoveFlags} MoveFlag to check on user and/or target * @param user {@linkcode Pokemon} the Pokemon using the move * @param target {@linkcode Pokemon} the Pokemon receiving the move + * @param isFollowUp (defaults to `false`) `true` if the move was used as a follow up * @returns boolean + * @see {@linkcode hasFlag} */ - checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon | null): boolean { + doesFlagEffectApply({ flag, user, target, isFollowUp = false }: { + flag: MoveFlags; + user: Pokemon; + target?: Pokemon; + isFollowUp?: boolean; + }): boolean { // special cases below, eg: if the move flag is MAKES_CONTACT, and the user pokemon has an ability that ignores contact (like "Long Reach"), then overrides and move does not make contact switch (flag) { case MoveFlags.MAKES_CONTACT: @@ -633,16 +651,18 @@ export default class Move implements Localizable { break; case MoveFlags.IGNORE_ABILITIES: if (user.hasAbilityWithAttr(MoveAbilityBypassAbAttr)) { - const abilityEffectsIgnored = new Utils.BooleanHolder(false); + const abilityEffectsIgnored = new Utils.BooleanHolder(false); applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, false, this); if (abilityEffectsIgnored.value) { return true; } + // Sunsteel strike, Moongeist beam, and photon geyser will not ignore abilities if invoked + // by another move, such as via metronome. } - break; + return this.hasFlag(MoveFlags.IGNORE_ABILITIES) && !isFollowUp; case MoveFlags.IGNORE_PROTECT: if (user.hasAbilityWithAttr(IgnoreProtectOnContactAbAttr) - && this.checkFlag(MoveFlags.MAKES_CONTACT, user, null)) { + && this.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user })) { return true; } break; @@ -1214,7 +1234,7 @@ export class MoveEffectAttr extends MoveAttr { canApply(user: Pokemon, target: Pokemon, move: Move, args?: any[]) { return !! (this.selfTarget ? user.hp && !user.getTag(BattlerTagType.FRENZY) : target.hp) && (this.selfTarget || !target.getTag(BattlerTagType.PROTECTED) || - move.checkFlag(MoveFlags.IGNORE_PROTECT, user, target)); + move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_PROTECT, user, target })); } /** Applies move effects so long as they are able based on {@linkcode canApply} */ diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index bd1c9caad96..7cc389651dd 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -289,7 +289,8 @@ export class MoveEffectPhase extends PokemonPhase { /** Is the target protected by Protect, etc. or a relevant conditional protection effect? */ const isProtected = ![MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES].includes(this.move.getMove().moveTarget) && - (bypassIgnoreProtect.value || !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target)) && + (bypassIgnoreProtect.value || + !this.move.getMove().doesFlagEffectApply({ flag: MoveFlags.IGNORE_PROTECT, user, target })) && (hasConditionalProtectApplied.value || (!target.findTags(t => t instanceof DamageProtectedTag).length && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))) || @@ -307,7 +308,7 @@ export class MoveEffectPhase extends PokemonPhase { /** Is the target's magic bounce ability not ignored and able to reflect this move? */ const canMagicBounce = !isReflecting && - !move.checkFlag(MoveFlags.IGNORE_ABILITIES, user, target) && + !move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user, target }) && target.hasAbilityWithAttr(ReflectStatusMoveAbAttr); const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index cab02174605..33e772eb2ea 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -168,7 +168,7 @@ export class MovePhase extends BattlePhase { // Check move to see if arena.ignoreAbilities should be true. if (!this.followUp || this.reflected) { - if (this.move.getMove().checkFlag(MoveFlags.IGNORE_ABILITIES, this.pokemon, null)) { + if (this.move.getMove().doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user: this.pokemon, isFollowUp: this.followUp })) { globalScene.arena.setIgnoreAbilities(true, this.pokemon.getBattlerIndex()); } } From 1e6ceb55812fe00dde450ecdbc31d8876e5c4066 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sat, 5 Apr 2025 20:10:52 -0700 Subject: [PATCH 05/52] [Misc] Clean up various phases (part 1) (#4797) * Clean up various phases Remove redundant code, utilize default parameters, clean up some leftover `strict-null` `TODO`s, replace `integer` with `number` * Replace `* as Utils` imports with named imports * Apply Biome --- src/battle-scene.ts | 2 - src/phases/add-enemy-buff-modifier-phase.ts | 4 -- src/phases/attempt-run-phase.ts | 10 +-- src/phases/battle-end-phase.ts | 16 +++-- src/phases/battle-phase.ts | 12 ++-- src/phases/berry-phase.ts | 6 +- src/phases/common-anim-phase.ts | 11 +++- src/phases/damage-anim-phase.ts | 9 ++- src/phases/egg-hatch-phase.ts | 73 ++++++++++----------- src/phases/encounter-phase.ts | 4 +- src/phases/evolution-phase.ts | 28 ++++---- src/phases/exp-phase.ts | 4 +- src/phases/form-change-phase.ts | 8 +-- src/phases/game-over-phase.ts | 4 +- src/phases/login-phase.ts | 16 ++--- src/phases/message-phase.ts | 12 ++-- src/phases/money-reward-phase.ts | 4 +- src/phases/move-anim-test-phase.ts | 50 -------------- src/phases/move-end-phase.ts | 2 +- src/phases/move-phase.ts | 3 +- src/phases/mystery-encounter-phases.ts | 5 +- src/phases/new-biome-encounter-phase.ts | 4 -- src/phases/next-encounter-phase.ts | 4 -- src/phases/party-heal-phase.ts | 4 +- src/phases/pokemon-anim-phase.ts | 10 +-- src/phases/pokemon-heal-phase.ts | 6 +- src/phases/pokemon-phase.ts | 11 ++-- src/phases/post-game-over-phase.ts | 4 +- src/phases/post-turn-status-effect-phase.ts | 6 +- src/phases/reload-session-phase.ts | 8 +-- src/phases/scan-ivs-phase.ts | 4 +- src/phases/select-biome-phase.ts | 12 ++-- src/phases/select-challenge-phase.ts | 4 -- src/phases/select-gender-phase.ts | 4 -- src/phases/select-modifier-phase.ts | 3 +- src/phases/select-starter-phase.ts | 4 -- src/phases/select-target-phase.ts | 1 + src/phases/shiny-sparkle-phase.ts | 1 + src/phases/show-party-exp-bar-phase.ts | 4 +- src/phases/show-trainer-phase.ts | 4 -- src/phases/summon-missing-phase.ts | 4 -- src/phases/switch-summon-phase.ts | 10 +-- src/phases/test-message-phase.ts | 7 -- src/phases/title-phase.ts | 14 ++-- src/phases/trainer-message-test-phase.ts | 47 ------------- src/phases/trainer-victory-phase.ts | 8 +-- src/phases/turn-end-phase.ts | 4 -- src/phases/turn-init-phase.ts | 4 -- src/phases/turn-start-phase.ts | 14 ++-- src/phases/unavailable-phase.ts | 4 -- src/phases/weather-effect-phase.ts | 13 ++-- 51 files changed, 178 insertions(+), 332 deletions(-) delete mode 100644 src/phases/move-anim-test-phase.ts delete mode 100644 src/phases/test-message-phase.ts delete mode 100644 src/phases/trainer-message-test-phase.ts diff --git a/src/battle-scene.ts b/src/battle-scene.ts index a759cbb84c2..f676ba63306 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1526,8 +1526,6 @@ export default class BattleScene extends SceneBase { this.currentBattle.mysteryEncounterType = mysteryEncounterType; } - //this.pushPhase(new TrainerMessageTestPhase(this, TrainerType.RIVAL, TrainerType.RIVAL_2, TrainerType.RIVAL_3, TrainerType.RIVAL_4, TrainerType.RIVAL_5, TrainerType.RIVAL_6)); - if (!waveIndex && lastBattle) { const isWaveIndexMultipleOfTen = !(lastBattle.waveIndex % 10); const isEndlessOrDaily = this.gameMode.hasShortBiomes || this.gameMode.isDaily; diff --git a/src/phases/add-enemy-buff-modifier-phase.ts b/src/phases/add-enemy-buff-modifier-phase.ts index 7d91e64382a..16ed78e6d0d 100644 --- a/src/phases/add-enemy-buff-modifier-phase.ts +++ b/src/phases/add-enemy-buff-modifier-phase.ts @@ -9,10 +9,6 @@ import { Phase } from "#app/phase"; import { globalScene } from "#app/global-scene"; export class AddEnemyBuffModifierPhase extends Phase { - constructor() { - super(); - } - start() { super.start(); diff --git a/src/phases/attempt-run-phase.ts b/src/phases/attempt-run-phase.ts index dab5b8789da..e5691f5fb8e 100644 --- a/src/phases/attempt-run-phase.ts +++ b/src/phases/attempt-run-phase.ts @@ -1,10 +1,10 @@ import { applyAbAttrs, applyPreLeaveFieldAbAttrs, PreLeaveFieldAbAttr, RunSuccessAbAttr } from "#app/data/ability"; -import { Stat } from "#app/enums/stat"; -import { StatusEffect } from "#app/enums/status-effect"; +import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; import type { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import i18next from "i18next"; -import * as Utils from "#app/utils"; +import { NumberHolder } from "#app/utils"; import { BattleEndPhase } from "./battle-end-phase"; import { NewBattlePhase } from "./new-battle-phase"; import { PokemonPhase } from "./pokemon-phase"; @@ -22,7 +22,7 @@ export class AttemptRunPhase extends PokemonPhase { const playerPokemon = this.getPokemon(); - const escapeChance = new Utils.NumberHolder(0); + const escapeChance = new NumberHolder(0); this.attemptRunAway(playerField, enemyField, escapeChance); @@ -63,7 +63,7 @@ export class AttemptRunPhase extends PokemonPhase { this.end(); } - attemptRunAway(playerField: PlayerPokemon[], enemyField: EnemyPokemon[], escapeChance: Utils.NumberHolder) { + attemptRunAway(playerField: PlayerPokemon[], enemyField: EnemyPokemon[], escapeChance: NumberHolder) { /** Sum of the speed of all enemy pokemon on the field */ const enemySpeed = enemyField.reduce( (total: number, enemyPokemon: Pokemon) => total + enemyPokemon.getStat(Stat.SPD), diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index e6a0c66548e..ff17b17ab8b 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -26,13 +26,15 @@ export class BattleEndPhase extends BattlePhase { return true; }); // `phaseQueuePrepend` is private, so we have to use this inefficient loop. - while (globalScene.tryRemoveUnshiftedPhase(phase => { - if (phase instanceof BattleEndPhase) { - this.isVictory ||= phase.isVictory; - return true; - } - return false; - })) {} + while ( + globalScene.tryRemoveUnshiftedPhase(phase => { + if (phase instanceof BattleEndPhase) { + this.isVictory ||= phase.isVictory; + return true; + } + return false; + }) + ) {} globalScene.gameData.gameStats.battles++; if ( diff --git a/src/phases/battle-phase.ts b/src/phases/battle-phase.ts index 72bcc85bc62..d70b3909639 100644 --- a/src/phases/battle-phase.ts +++ b/src/phases/battle-phase.ts @@ -3,13 +3,13 @@ import { TrainerSlot } from "#enums/trainer-slot"; import { Phase } from "#app/phase"; export class BattlePhase extends Phase { - constructor() { - super(); - } - showEnemyTrainer(trainerSlot: TrainerSlot = TrainerSlot.NONE): void { - const sprites = globalScene.currentBattle.trainer?.getSprites()!; // TODO: is this bang correct? - const tintSprites = globalScene.currentBattle.trainer?.getTintSprites()!; // TODO: is this bang correct? + if (!globalScene.currentBattle.trainer) { + console.warn("Enemy trainer is missing!"); + return; + } + const sprites = globalScene.currentBattle.trainer.getSprites(); + const tintSprites = globalScene.currentBattle.trainer.getTintSprites(); for (let i = 0; i < sprites.length; i++) { const visible = !trainerSlot || !i === (trainerSlot === TrainerSlot.TRAINER) || sprites.length < 2; [sprites[i], tintSprites[i]].map(sprite => { diff --git a/src/phases/berry-phase.ts b/src/phases/berry-phase.ts index 0048f8cd2f2..e5614739903 100644 --- a/src/phases/berry-phase.ts +++ b/src/phases/berry-phase.ts @@ -4,7 +4,7 @@ import { BerryUsedEvent } from "#app/events/battle-scene"; import { getPokemonNameWithAffix } from "#app/messages"; import { BerryModifier } from "#app/modifier/modifier"; import i18next from "i18next"; -import * as Utils from "#app/utils"; +import { BooleanHolder } from "#app/utils"; import { FieldPhase } from "./field-phase"; import { CommonAnimPhase } from "./common-anim-phase"; import { globalScene } from "#app/global-scene"; @@ -20,7 +20,7 @@ export class BerryPhase extends FieldPhase { }, pokemon.isPlayer()); if (hasUsableBerry) { - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); pokemon.getOpponents().map(opp => applyAbAttrs(PreventBerryUseAbAttr, opp, cancelled)); if (cancelled.value) { @@ -44,7 +44,7 @@ export class BerryPhase extends FieldPhase { globalScene.updateModifiers(pokemon.isPlayer()); - applyAbAttrs(HealFromBerryUseAbAttr, pokemon, new Utils.BooleanHolder(false)); + applyAbAttrs(HealFromBerryUseAbAttr, pokemon, new BooleanHolder(false)); } } }); diff --git a/src/phases/common-anim-phase.ts b/src/phases/common-anim-phase.ts index d32e93ea6aa..5be5e112389 100644 --- a/src/phases/common-anim-phase.ts +++ b/src/phases/common-anim-phase.ts @@ -6,13 +6,18 @@ import { PokemonPhase } from "./pokemon-phase"; export class CommonAnimPhase extends PokemonPhase { private anim: CommonAnim | null; - private targetIndex: number | undefined; + private targetIndex?: BattlerIndex; private playOnEmptyField: boolean; - constructor(battlerIndex?: BattlerIndex, targetIndex?: BattlerIndex, anim?: CommonAnim, playOnEmptyField = false) { + constructor( + battlerIndex?: BattlerIndex, + targetIndex?: BattlerIndex, + anim: CommonAnim | null = null, + playOnEmptyField = false, + ) { super(battlerIndex); - this.anim = anim!; // TODO: is this bang correct? + this.anim = anim; this.targetIndex = targetIndex; this.playOnEmptyField = playOnEmptyField; } diff --git a/src/phases/damage-anim-phase.ts b/src/phases/damage-anim-phase.ts index 703cd3d160e..696a2e55b6f 100644 --- a/src/phases/damage-anim-phase.ts +++ b/src/phases/damage-anim-phase.ts @@ -10,11 +10,16 @@ export class DamageAnimPhase extends PokemonPhase { private damageResult: DamageResult; private critical: boolean; - constructor(battlerIndex: BattlerIndex, amount: number, damageResult?: DamageResult, critical = false) { + constructor( + battlerIndex: BattlerIndex, + amount: number, + damageResult: DamageResult = HitResult.EFFECTIVE, + critical = false, + ) { super(battlerIndex); this.amount = amount; - this.damageResult = damageResult || HitResult.EFFECTIVE; + this.damageResult = damageResult; this.critical = critical; } diff --git a/src/phases/egg-hatch-phase.ts b/src/phases/egg-hatch-phase.ts index 49a408e8699..07eeeb0f8ae 100644 --- a/src/phases/egg-hatch-phase.ts +++ b/src/phases/egg-hatch-phase.ts @@ -11,7 +11,7 @@ import PokemonInfoContainer from "#app/ui/pokemon-info-container"; import { Mode } from "#app/ui/ui"; import i18next from "i18next"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import * as Utils from "#app/utils"; +import { fixedInt, getFrameMs, randInt } from "#app/utils"; import type { EggLapsePhase } from "./egg-lapse-phase"; import type { EggHatchData } from "#app/data/egg-hatch-data"; import { doShinySparkleAnim } from "#app/field/anims"; @@ -306,17 +306,17 @@ export class EggHatchPhase extends Phase { this.canSkip = false; this.hatched = true; if (this.evolutionBgm) { - SoundFade.fadeOut(globalScene, this.evolutionBgm, Utils.fixedInt(100)); + SoundFade.fadeOut(globalScene, this.evolutionBgm, fixedInt(100)); } for (let e = 0; e < 5; e++) { - globalScene.time.delayedCall(Utils.fixedInt(375 * e), () => + globalScene.time.delayedCall(fixedInt(375 * e), () => globalScene.playSound("se/egg_hatch", { volume: 1 - e * 0.2 }), ); } this.eggLightraysOverlay.setVisible(true); this.eggLightraysOverlay.play("egg_lightrays"); globalScene.tweens.add({ - duration: Utils.fixedInt(125), + duration: fixedInt(125), targets: this.eggHatchOverlay, alpha: 1, ease: "Cubic.easeIn", @@ -325,7 +325,7 @@ export class EggHatchPhase extends Phase { this.canSkip = true; }, }); - globalScene.time.delayedCall(Utils.fixedInt(1500), () => { + globalScene.time.delayedCall(fixedInt(1500), () => { this.canSkip = false; if (!this.skipped) { this.doReveal(); @@ -363,46 +363,43 @@ export class EggHatchPhase extends Phase { this.pokemonSprite.setPipelineData("shiny", this.pokemon.shiny); this.pokemonSprite.setPipelineData("variant", this.pokemon.variant); this.pokemonSprite.setVisible(true); - globalScene.time.delayedCall(Utils.fixedInt(250), () => { + globalScene.time.delayedCall(fixedInt(250), () => { this.eggsToHatchCount--; this.eggHatchHandler.eventTarget.dispatchEvent(new EggCountChangedEvent(this.eggsToHatchCount)); this.pokemon.cry(); if (isShiny) { - globalScene.time.delayedCall(Utils.fixedInt(500), () => { + globalScene.time.delayedCall(fixedInt(500), () => { doShinySparkleAnim(this.pokemonShinySparkle, this.pokemon.variant); }); } - globalScene.time.delayedCall( - Utils.fixedInt(!this.skipped ? (!isShiny ? 1250 : 1750) : !isShiny ? 250 : 750), - () => { - this.infoContainer.show(this.pokemon, false, this.skipped ? 2 : 1); + globalScene.time.delayedCall(fixedInt(!this.skipped ? (!isShiny ? 1250 : 1750) : !isShiny ? 250 : 750), () => { + this.infoContainer.show(this.pokemon, false, this.skipped ? 2 : 1); - globalScene.playSoundWithoutBgm("evolution_fanfare"); + globalScene.playSoundWithoutBgm("evolution_fanfare"); - globalScene.ui.showText( - i18next.t("egg:hatchFromTheEgg", { - pokemonName: this.pokemon.species.getExpandedSpeciesName(), - }), - null, - () => { - globalScene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs); - globalScene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => { - globalScene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(value => { - this.eggHatchData.setEggMoveUnlocked(value); - globalScene.ui.showText("", 0); - this.end(); - }); + globalScene.ui.showText( + i18next.t("egg:hatchFromTheEgg", { + pokemonName: this.pokemon.species.getExpandedSpeciesName(), + }), + null, + () => { + globalScene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs); + globalScene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => { + globalScene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(value => { + this.eggHatchData.setEggMoveUnlocked(value); + globalScene.ui.showText("", 0); + this.end(); }); - }, - null, - true, - 3000, - ); - }, - ); + }); + }, + null, + true, + 3000, + ); + }); }); globalScene.tweens.add({ - duration: Utils.fixedInt(this.skipped ? 500 : 3000), + duration: fixedInt(this.skipped ? 500 : 3000), targets: this.eggHatchOverlay, alpha: 0, ease: "Cubic.easeOut", @@ -427,9 +424,9 @@ export class EggHatchPhase extends Phase { doSpray(intensity: number, offsetY?: number) { globalScene.tweens.addCounter({ repeat: intensity, - duration: Utils.getFrameMs(1), + duration: getFrameMs(1), onRepeat: () => { - this.doSprayParticle(Utils.randInt(8), offsetY || 0); + this.doSprayParticle(randInt(8), offsetY || 0); }, }); } @@ -448,12 +445,12 @@ export class EggHatchPhase extends Phase { let f = 0; let yOffset = 0; - const speed = 3 - Utils.randInt(8); - const amp = 24 + Utils.randInt(32); + const speed = 3 - randInt(8); + const amp = 24 + randInt(32); const particleTimer = globalScene.tweens.addCounter({ repeat: -1, - duration: Utils.getFrameMs(1), + duration: getFrameMs(1), onRepeat: () => { updateParticle(); }, diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index ad2bf689e38..9e5edf3e1d9 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -43,10 +43,10 @@ import { getNatureName } from "#app/data/nature"; export class EncounterPhase extends BattlePhase { private loaded: boolean; - constructor(loaded?: boolean) { + constructor(loaded = false) { super(); - this.loaded = !!loaded; + this.loaded = loaded; } start() { diff --git a/src/phases/evolution-phase.ts b/src/phases/evolution-phase.ts index bb283fa8139..203c7542eff 100644 --- a/src/phases/evolution-phase.ts +++ b/src/phases/evolution-phase.ts @@ -5,7 +5,7 @@ import { globalScene } from "#app/global-scene"; import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; import { FusionSpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; import type EvolutionSceneHandler from "#app/ui/evolution-scene-handler"; -import * as Utils from "#app/utils"; +import { fixedInt, getFrameMs, randInt } from "#app/utils"; import { Mode } from "#app/ui/ui"; import { cos, sin } from "#app/field/anims"; import type { PlayerPokemon } from "#app/field/pokemon"; @@ -332,9 +332,9 @@ export class EvolutionPhase extends Phase { () => this.end(), null, true, - Utils.fixedInt(4000), + fixedInt(4000), ); - globalScene.time.delayedCall(Utils.fixedInt(4250), () => globalScene.playBgm()); + globalScene.time.delayedCall(fixedInt(4250), () => globalScene.playBgm()); }); }); }; @@ -392,7 +392,7 @@ export class EvolutionPhase extends Phase { globalScene.tweens.addCounter({ repeat: 64, - duration: Utils.getFrameMs(1), + duration: getFrameMs(1), onRepeat: () => { if (f < 64) { if (!(f & 7)) { @@ -411,7 +411,7 @@ export class EvolutionPhase extends Phase { globalScene.tweens.addCounter({ repeat: 96, - duration: Utils.getFrameMs(1), + duration: getFrameMs(1), onRepeat: () => { if (f < 96) { if (f < 6) { @@ -461,7 +461,7 @@ export class EvolutionPhase extends Phase { globalScene.tweens.addCounter({ repeat: 48, - duration: Utils.getFrameMs(1), + duration: getFrameMs(1), onRepeat: () => { if (!f) { for (let i = 0; i < 16; i++) { @@ -482,14 +482,14 @@ export class EvolutionPhase extends Phase { globalScene.tweens.addCounter({ repeat: 48, - duration: Utils.getFrameMs(1), + duration: getFrameMs(1), onRepeat: () => { if (!f) { for (let i = 0; i < 8; i++) { this.doSprayParticle(i); } } else if (f < 50) { - this.doSprayParticle(Utils.randInt(8)); + this.doSprayParticle(randInt(8)); } f++; }, @@ -506,7 +506,7 @@ export class EvolutionPhase extends Phase { const particleTimer = globalScene.tweens.addCounter({ repeat: -1, - duration: Utils.getFrameMs(1), + duration: getFrameMs(1), onRepeat: () => { updateParticle(); }, @@ -543,7 +543,7 @@ export class EvolutionPhase extends Phase { const particleTimer = globalScene.tweens.addCounter({ repeat: -1, - duration: Utils.getFrameMs(1), + duration: getFrameMs(1), onRepeat: () => { updateParticle(); }, @@ -575,7 +575,7 @@ export class EvolutionPhase extends Phase { const particleTimer = globalScene.tweens.addCounter({ repeat: -1, - duration: Utils.getFrameMs(1), + duration: getFrameMs(1), onRepeat: () => { updateParticle(); }, @@ -605,12 +605,12 @@ export class EvolutionPhase extends Phase { let f = 0; let yOffset = 0; - const speed = 3 - Utils.randInt(8); - const amp = 48 + Utils.randInt(64); + const speed = 3 - randInt(8); + const amp = 48 + randInt(64); const particleTimer = globalScene.tweens.addCounter({ repeat: -1, - duration: Utils.getFrameMs(1), + duration: getFrameMs(1), onRepeat: () => { updateParticle(); }, diff --git a/src/phases/exp-phase.ts b/src/phases/exp-phase.ts index 092482d4c18..b7d62c92bcf 100644 --- a/src/phases/exp-phase.ts +++ b/src/phases/exp-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; import { ExpBoosterModifier } from "#app/modifier/modifier"; import i18next from "i18next"; -import * as Utils from "#app/utils"; +import { NumberHolder } from "#app/utils"; import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; import { LevelUpPhase } from "./level-up-phase"; @@ -19,7 +19,7 @@ export class ExpPhase extends PlayerPartyMemberPokemonPhase { super.start(); const pokemon = this.getPokemon(); - const exp = new Utils.NumberHolder(this.expValue); + const exp = new NumberHolder(this.expValue); globalScene.applyModifiers(ExpBoosterModifier, true, exp); exp.value = Math.floor(exp.value); globalScene.ui.showText( diff --git a/src/phases/form-change-phase.ts b/src/phases/form-change-phase.ts index e0ec4e87600..bf94284b117 100644 --- a/src/phases/form-change-phase.ts +++ b/src/phases/form-change-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import * as Utils from "../utils"; +import { fixedInt } from "#app/utils"; import { achvs } from "../system/achv"; import type { SpeciesFormChange } from "../data/pokemon-forms"; import { getSpeciesFormChangeMessage } from "../data/pokemon-forms"; @@ -9,7 +9,7 @@ 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 "#app/enums/battler-tag-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { SpeciesFormKey } from "#enums/species-form-key"; export class FormChangePhase extends EvolutionPhase { @@ -151,9 +151,9 @@ export class FormChangePhase extends EvolutionPhase { () => this.end(), null, true, - Utils.fixedInt(delay), + fixedInt(delay), ); - globalScene.time.delayedCall(Utils.fixedInt(delay + 250), () => + globalScene.time.delayedCall(fixedInt(delay + 250), () => globalScene.playBgm(), ); }); diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index f105b625cc8..1ccdc9c7106 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -20,7 +20,7 @@ import { UnlockPhase } from "#app/phases/unlock-phase"; import { achvs, ChallengeAchv } from "#app/system/achv"; import { Unlockables } from "#app/system/unlockables"; import { Mode } from "#app/ui/ui"; -import * as Utils from "#app/utils"; +import { isLocal, isLocalServerConnected } from "#app/utils"; import { PlayerGender } from "#enums/player-gender"; import { TrainerType } from "#enums/trainer-type"; import i18next from "i18next"; @@ -219,7 +219,7 @@ export class GameOverPhase extends BattlePhase { /* Added a local check to see if the game is running offline If Online, execute apiFetch as intended If Offline, execute offlineNewClear() only for victory, a localStorage implementation of newClear daily run checks */ - if (!Utils.isLocal || Utils.isLocalServerConnected) { + if (!isLocal || isLocalServerConnected) { pokerogueApi.savedata.session .newclear({ slot: globalScene.sessionSlotId, diff --git a/src/phases/login-phase.ts b/src/phases/login-phase.ts index 5cce6ca0298..846482ff726 100644 --- a/src/phases/login-phase.ts +++ b/src/phases/login-phase.ts @@ -5,26 +5,26 @@ import { Phase } from "#app/phase"; import { handleTutorial, Tutorial } from "#app/tutorial"; import { Mode } from "#app/ui/ui"; import i18next, { t } from "i18next"; -import * as Utils from "#app/utils"; +import { getCookie, sessionIdKey, executeIf, removeCookie } from "#app/utils"; import { SelectGenderPhase } from "./select-gender-phase"; import { UnavailablePhase } from "./unavailable-phase"; export class LoginPhase extends Phase { private showText: boolean; - constructor(showText?: boolean) { + constructor(showText = true) { super(); - this.showText = showText === undefined || !!showText; + this.showText = showText; } start(): void { super.start(); - const hasSession = !!Utils.getCookie(Utils.sessionIdKey); + const hasSession = !!getCookie(sessionIdKey); globalScene.ui.setMode(Mode.LOADING, { buttonActions: [] }); - Utils.executeIf(bypassLogin || hasSession, updateUserInfo).then(response => { + executeIf(bypassLogin || hasSession, updateUserInfo).then(response => { const success = response ? response[0] : false; const statusCode = response ? response[1] : null; if (!success) { @@ -38,7 +38,7 @@ export class LoginPhase extends Phase { const loadData = () => { updateUserInfo().then(success => { if (!success[0]) { - Utils.removeCookie(Utils.sessionIdKey); + removeCookie(sessionIdKey); globalScene.reset(true, true); return; } @@ -60,7 +60,7 @@ export class LoginPhase extends Phase { globalScene.ui.playSelect(); updateUserInfo().then(success => { if (!success[0]) { - Utils.removeCookie(Utils.sessionIdKey); + removeCookie(sessionIdKey); globalScene.reset(true, true); return; } @@ -89,7 +89,7 @@ export class LoginPhase extends Phase { ], }); } else if (statusCode === 401) { - Utils.removeCookie(Utils.sessionIdKey); + removeCookie(sessionIdKey); globalScene.reset(true, true); } else { globalScene.unshiftPhase(new UnavailablePhase()); diff --git a/src/phases/message-phase.ts b/src/phases/message-phase.ts index cff7249fcfa..f6777579857 100644 --- a/src/phases/message-phase.ts +++ b/src/phases/message-phase.ts @@ -3,9 +3,9 @@ import { Phase } from "#app/phase"; export class MessagePhase extends Phase { private text: string; - private callbackDelay: number | null; - private prompt: boolean | null; - private promptDelay: number | null; + private callbackDelay?: number | null; + private prompt?: boolean | null; + private promptDelay?: number | null; private speaker?: string; constructor( @@ -18,9 +18,9 @@ export class MessagePhase extends Phase { super(); this.text = text; - this.callbackDelay = callbackDelay!; // TODO: is this bang correct? - this.prompt = prompt!; // TODO: is this bang correct? - this.promptDelay = promptDelay!; // TODO: is this bang correct? + this.callbackDelay = callbackDelay; + this.prompt = prompt; + this.promptDelay = promptDelay; this.speaker = speaker; } diff --git a/src/phases/money-reward-phase.ts b/src/phases/money-reward-phase.ts index 56f46d25f77..ae8dc90616d 100644 --- a/src/phases/money-reward-phase.ts +++ b/src/phases/money-reward-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import { MoneyMultiplierModifier } from "#app/modifier/modifier"; import i18next from "i18next"; -import * as Utils from "#app/utils"; +import { NumberHolder } from "#app/utils"; import { BattlePhase } from "./battle-phase"; export class MoneyRewardPhase extends BattlePhase { @@ -15,7 +15,7 @@ export class MoneyRewardPhase extends BattlePhase { } start() { - const moneyAmount = new Utils.NumberHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier)); + const moneyAmount = new NumberHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier)); globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount); diff --git a/src/phases/move-anim-test-phase.ts b/src/phases/move-anim-test-phase.ts deleted file mode 100644 index e8b7c0c8fa7..00000000000 --- a/src/phases/move-anim-test-phase.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { globalScene } from "#app/global-scene"; -import { initMoveAnim, loadMoveAnimAssets, MoveAnim } from "#app/data/battle-anims"; -import { allMoves, SelfStatusMove } from "#app/data/moves/move"; -import { Moves } from "#app/enums/moves"; -import * as Utils from "#app/utils"; -import { BattlePhase } from "./battle-phase"; - -export class MoveAnimTestPhase extends BattlePhase { - private moveQueue: Moves[]; - - constructor(moveQueue?: Moves[]) { - super(); - - this.moveQueue = moveQueue || Utils.getEnumValues(Moves).slice(1); - } - - start() { - const moveQueue = this.moveQueue.slice(0); - this.playMoveAnim(moveQueue, true); - } - - playMoveAnim(moveQueue: Moves[], player: boolean) { - const moveId = player ? moveQueue[0] : moveQueue.shift(); - if (moveId === undefined) { - this.playMoveAnim(this.moveQueue.slice(0), true); - return; - } - if (player) { - console.log(Moves[moveId]); - } - - initMoveAnim(moveId).then(() => { - loadMoveAnimAssets([moveId], true).then(() => { - const user = player ? globalScene.getPlayerPokemon()! : globalScene.getEnemyPokemon()!; - const target = - player !== allMoves[moveId] instanceof SelfStatusMove - ? globalScene.getEnemyPokemon()! - : globalScene.getPlayerPokemon()!; - new MoveAnim(moveId, user, target.getBattlerIndex()).play(allMoves[moveId].hitsSubstitute(user, target), () => { - // TODO: are the bangs correct here? - if (player) { - this.playMoveAnim(moveQueue, false); - } else { - this.playMoveAnim(moveQueue, true); - } - }); - }); - }); - } -} diff --git a/src/phases/move-end-phase.ts b/src/phases/move-end-phase.ts index f3a40ce69bd..53856956401 100644 --- a/src/phases/move-end-phase.ts +++ b/src/phases/move-end-phase.ts @@ -5,7 +5,7 @@ import type { BattlerIndex } from "#app/battle"; export class MoveEndPhase extends PokemonPhase { private wasFollowUp: boolean; - constructor(battlerIndex: BattlerIndex, wasFollowUp: boolean = false) { + constructor(battlerIndex: BattlerIndex, wasFollowUp = false) { super(battlerIndex); this.wasFollowUp = wasFollowUp; } diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 33e772eb2ea..82b73f681a0 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -227,7 +227,7 @@ export class MovePhase extends BattlePhase { (!this.pokemon.randSeedInt(4) || Overrides.STATUS_ACTIVATION_OVERRIDE === true) && Overrides.STATUS_ACTIVATION_OVERRIDE !== false; break; - case StatusEffect.SLEEP: + case StatusEffect.SLEEP: { applyMoveAttrs(BypassSleepAttr, this.pokemon, null, this.move.getMove()); const turnsRemaining = new NumberHolder(this.pokemon.status.sleepTurnsRemaining ?? 0); applyAbAttrs( @@ -242,6 +242,7 @@ export class MovePhase extends BattlePhase { healed = this.pokemon.status.sleepTurnsRemaining <= 0; activated = !healed && !this.pokemon.getTag(BattlerTagType.BYPASS_SLEEP); break; + } case StatusEffect.FREEZE: healed = !!this.move diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index eb187617e69..f42290ff872 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -26,8 +26,7 @@ import { TrainerSlot } from "#enums/trainer-slot"; import { IvScannerModifier } from "../modifier/modifier"; import { Phase } from "../phase"; import { Mode } from "../ui/ui"; -import * as Utils from "../utils"; -import { isNullOrUndefined } from "../utils"; +import { isNullOrUndefined, randSeedItem } from "#app/utils"; /** * Will handle (in order): @@ -387,7 +386,7 @@ export class MysteryEncounterBattlePhase extends Phase { const trainer = globalScene.currentBattle.trainer; let message: string; globalScene.executeWithSeedOffset( - () => (message = Utils.randSeedItem(encounterMessages)), + () => (message = randSeedItem(encounterMessages)), globalScene.currentBattle.mysteryEncounter?.getSeedOffset(), ); message = message!; // tell TS compiler it's defined now diff --git a/src/phases/new-biome-encounter-phase.ts b/src/phases/new-biome-encounter-phase.ts index bb1fe54fe9f..3449a562c4a 100644 --- a/src/phases/new-biome-encounter-phase.ts +++ b/src/phases/new-biome-encounter-phase.ts @@ -4,10 +4,6 @@ import { getRandomWeatherType } from "#app/data/weather"; import { NextEncounterPhase } from "./next-encounter-phase"; export class NewBiomeEncounterPhase extends NextEncounterPhase { - constructor() { - super(); - } - doEncounter(): void { globalScene.playBgm(undefined, true); diff --git a/src/phases/next-encounter-phase.ts b/src/phases/next-encounter-phase.ts index e53f775f083..e5e61312c3b 100644 --- a/src/phases/next-encounter-phase.ts +++ b/src/phases/next-encounter-phase.ts @@ -2,10 +2,6 @@ import { globalScene } from "#app/global-scene"; import { EncounterPhase } from "./encounter-phase"; export class NextEncounterPhase extends EncounterPhase { - constructor() { - super(); - } - start() { super.start(); } diff --git a/src/phases/party-heal-phase.ts b/src/phases/party-heal-phase.ts index a9b24309e24..137af9f3a2d 100644 --- a/src/phases/party-heal-phase.ts +++ b/src/phases/party-heal-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import * as Utils from "#app/utils"; +import { fixedInt } from "#app/utils"; import { BattlePhase } from "./battle-phase"; export class PartyHealPhase extends BattlePhase { @@ -28,7 +28,7 @@ export class PartyHealPhase extends BattlePhase { pokemon.updateInfo(true); } const healSong = globalScene.playSoundWithoutBgm("heal"); - globalScene.time.delayedCall(Utils.fixedInt(healSong.totalDuration * 1000), () => { + globalScene.time.delayedCall(fixedInt(healSong.totalDuration * 1000), () => { healSong.destroy(); if (this.resumeBgm && bgmPlaying) { globalScene.playBgm(); diff --git a/src/phases/pokemon-anim-phase.ts b/src/phases/pokemon-anim-phase.ts index b9c91508b5a..f0693a52aaa 100644 --- a/src/phases/pokemon-anim-phase.ts +++ b/src/phases/pokemon-anim-phase.ts @@ -8,18 +8,18 @@ import { Species } from "#enums/species"; export class PokemonAnimPhase extends BattlePhase { /** The type of animation to play in this phase */ - private key: PokemonAnimType; + protected key: PokemonAnimType; /** The Pokemon to which this animation applies */ - private pokemon: Pokemon; + protected pokemon: Pokemon; /** Any other field sprites affected by this animation */ - private fieldAssets: Phaser.GameObjects.Sprite[]; + protected fieldAssets: Phaser.GameObjects.Sprite[]; - constructor(key: PokemonAnimType, pokemon: Pokemon, fieldAssets?: Phaser.GameObjects.Sprite[]) { + constructor(key: PokemonAnimType, pokemon: Pokemon, fieldAssets: Phaser.GameObjects.Sprite[] = []) { super(); this.key = key; this.pokemon = pokemon; - this.fieldAssets = fieldAssets ?? []; + this.fieldAssets = fieldAssets; } start(): void { diff --git a/src/phases/pokemon-heal-phase.ts b/src/phases/pokemon-heal-phase.ts index ecfe99389eb..651c625b23a 100644 --- a/src/phases/pokemon-heal-phase.ts +++ b/src/phases/pokemon-heal-phase.ts @@ -8,7 +8,7 @@ import { getPokemonNameWithAffix } from "#app/messages"; import { HealingBoosterModifier } from "#app/modifier/modifier"; import { HealAchv } from "#app/system/achv"; import i18next from "i18next"; -import * as Utils from "#app/utils"; +import { NumberHolder } from "#app/utils"; import { CommonAnimPhase } from "./common-anim-phase"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import type { HealBlockTag } from "#app/data/battler-tags"; @@ -72,11 +72,11 @@ export class PokemonHealPhase extends CommonAnimPhase { return super.end(); } if (healOrDamage) { - const hpRestoreMultiplier = new Utils.NumberHolder(1); + const hpRestoreMultiplier = new NumberHolder(1); if (!this.revive) { globalScene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier); } - const healAmount = new Utils.NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value)); + const healAmount = new NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value)); if (healAmount.value < 0) { pokemon.damageAndUpdate(healAmount.value * -1, { result: HitResult.INDIRECT }); healAmount.value = 0; diff --git a/src/phases/pokemon-phase.ts b/src/phases/pokemon-phase.ts index 3ca5f09f953..8c30512cdc4 100644 --- a/src/phases/pokemon-phase.ts +++ b/src/phases/pokemon-phase.ts @@ -11,11 +11,14 @@ export abstract class PokemonPhase extends FieldPhase { constructor(battlerIndex?: BattlerIndex | number) { super(); - if (battlerIndex === undefined) { - battlerIndex = globalScene + battlerIndex = + battlerIndex ?? + globalScene .getField() - .find(p => p?.isActive())! - .getBattlerIndex(); // TODO: is the bang correct here? + .find(p => p?.isActive())! // TODO: is the bang correct here? + .getBattlerIndex(); + if (battlerIndex === undefined) { + console.warn("There are no Pokemon on the field!"); // TODO: figure out a suitable fallback behavior } this.battlerIndex = battlerIndex; diff --git a/src/phases/post-game-over-phase.ts b/src/phases/post-game-over-phase.ts index f86ec8496e0..753251e992f 100644 --- a/src/phases/post-game-over-phase.ts +++ b/src/phases/post-game-over-phase.ts @@ -4,12 +4,12 @@ import type { EndCardPhase } from "./end-card-phase"; import { TitlePhase } from "./title-phase"; export class PostGameOverPhase extends Phase { - private endCardPhase: EndCardPhase | null; + private endCardPhase?: EndCardPhase; constructor(endCardPhase?: EndCardPhase) { super(); - this.endCardPhase = endCardPhase!; // TODO: is this bang correct? + this.endCardPhase = endCardPhase; } start() { diff --git a/src/phases/post-turn-status-effect-phase.ts b/src/phases/post-turn-status-effect-phase.ts index f6341666e7e..619ef22d01e 100644 --- a/src/phases/post-turn-status-effect-phase.ts +++ b/src/phases/post-turn-status-effect-phase.ts @@ -13,7 +13,7 @@ import { getStatusEffectActivationText } from "#app/data/status-effect"; import { BattleSpec } from "#app/enums/battle-spec"; import { StatusEffect } from "#app/enums/status-effect"; import { getPokemonNameWithAffix } from "#app/messages"; -import * as Utils from "#app/utils"; +import { BooleanHolder, NumberHolder } from "#app/utils"; import { PokemonPhase } from "./pokemon-phase"; export class PostTurnStatusEffectPhase extends PokemonPhase { @@ -26,7 +26,7 @@ export class PostTurnStatusEffectPhase extends PokemonPhase { const pokemon = this.getPokemon(); if (pokemon?.isActive(true) && pokemon.status && pokemon.status.isPostTurn() && !pokemon.switchOutStatus) { pokemon.status.incrementTurn(); - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); applyAbAttrs(BlockStatusDamageAbAttr, pokemon, cancelled); @@ -34,7 +34,7 @@ export class PostTurnStatusEffectPhase extends PokemonPhase { globalScene.queueMessage( getStatusEffectActivationText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)), ); - const damage = new Utils.NumberHolder(0); + const damage = new NumberHolder(0); switch (pokemon.status.effect) { case StatusEffect.POISON: damage.value = Math.max(pokemon.getMaxHp() >> 3, 1); diff --git a/src/phases/reload-session-phase.ts b/src/phases/reload-session-phase.ts index 3a4a4e0e3a5..a7ac0002b03 100644 --- a/src/phases/reload-session-phase.ts +++ b/src/phases/reload-session-phase.ts @@ -1,15 +1,15 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; import { Mode } from "#app/ui/ui"; -import * as Utils from "#app/utils"; +import { fixedInt } from "#app/utils"; export class ReloadSessionPhase extends Phase { - private systemDataStr: string | null; + private systemDataStr?: string; constructor(systemDataStr?: string) { super(); - this.systemDataStr = systemDataStr ?? null; + this.systemDataStr = systemDataStr; } start(): void { @@ -18,7 +18,7 @@ export class ReloadSessionPhase extends Phase { let delayElapsed = false; let loaded = false; - globalScene.time.delayedCall(Utils.fixedInt(1500), () => { + globalScene.time.delayedCall(fixedInt(1500), () => { if (loaded) { this.end(); } else { diff --git a/src/phases/scan-ivs-phase.ts b/src/phases/scan-ivs-phase.ts index 2a2d68591ca..aaeeb7f84f8 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 { + // biome-ignore lint/complexity/noUselessConstructor: This changes `battlerIndex` to be required constructor(battlerIndex: BattlerIndex) { super(battlerIndex); } @@ -24,7 +25,8 @@ export class ScanIvsPhase extends PokemonPhase { const uiTheme = globalScene.uiTheme; // Assuming uiTheme is accessible for (let e = 0; e < enemyField.length; e++) { enemyIvs = enemyField[e].ivs; - const currentIvs = globalScene.gameData.dexData[enemyField[e].species.getRootSpeciesId()].ivs; // we are using getRootSpeciesId() here because we want to check against the baby form, not the mid form if it exists + // we are using getRootSpeciesId() here because we want to check against the baby form, not the mid form if it exists + const currentIvs = globalScene.gameData.dexData[enemyField[e].species.getRootSpeciesId()].ivs; statsContainer = enemyField[e].getBattleInfo().getStatsValueContainer().list as Phaser.GameObjects.Sprite[]; statsContainerLabels = statsContainer.filter(m => m.name.indexOf("icon_stat_label") >= 0); for (let s = 0; s < statsContainerLabels.length; s++) { diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index 6a11967832a..2d67cb87405 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -5,15 +5,11 @@ import { MoneyInterestModifier, MapModifier } from "#app/modifier/modifier"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { Mode } from "#app/ui/ui"; import { BattlePhase } from "./battle-phase"; -import * as Utils from "#app/utils"; +import { randSeedInt } from "#app/utils"; import { PartyHealPhase } from "./party-heal-phase"; import { SwitchBiomePhase } from "./switch-biome-phase"; export class SelectBiomePhase extends BattlePhase { - constructor() { - super(); - } - start() { super.start(); @@ -40,7 +36,7 @@ export class SelectBiomePhase extends BattlePhase { let biomes: Biome[] = []; globalScene.executeWithSeedOffset(() => { biomes = (biomeLinks[currentBiome] as (Biome | [Biome, number])[]) - .filter(b => !Array.isArray(b) || !Utils.randSeedInt(b[1])) + .filter(b => !Array.isArray(b) || !randSeedInt(b[1])) .map(b => (!Array.isArray(b) ? b : b[0])); }, globalScene.currentBattle.waveIndex); if (biomes.length > 1 && globalScene.findModifier(m => m instanceof MapModifier)) { @@ -51,7 +47,7 @@ export class SelectBiomePhase extends BattlePhase { ? [biomeLinks[currentBiome] as Biome] : (biomeLinks[currentBiome] as (Biome | [Biome, number])[]) ) - .filter((b, _i) => !Array.isArray(b) || !Utils.randSeedInt(b[1])) + .filter(b => !Array.isArray(b) || !randSeedInt(b[1])) .map(b => (Array.isArray(b) ? b[0] : b)); }, globalScene.currentBattle.waveIndex); const biomeSelectItems = biomeChoices.map(b => { @@ -70,7 +66,7 @@ export class SelectBiomePhase extends BattlePhase { delay: 1000, }); } else { - setNextBiome(biomes[Utils.randSeedInt(biomes.length)]); + setNextBiome(biomes[randSeedInt(biomes.length)]); } } else if (biomeLinks.hasOwnProperty(currentBiome)) { setNextBiome(biomeLinks[currentBiome] as Biome); diff --git a/src/phases/select-challenge-phase.ts b/src/phases/select-challenge-phase.ts index 2a6797d3556..5e6f20f93ee 100644 --- a/src/phases/select-challenge-phase.ts +++ b/src/phases/select-challenge-phase.ts @@ -3,10 +3,6 @@ import { Phase } from "#app/phase"; import { Mode } from "#app/ui/ui"; export class SelectChallengePhase extends Phase { - constructor() { - super(); - } - start() { super.start(); diff --git a/src/phases/select-gender-phase.ts b/src/phases/select-gender-phase.ts index 1c86536de53..4da60b38aa1 100644 --- a/src/phases/select-gender-phase.ts +++ b/src/phases/select-gender-phase.ts @@ -6,10 +6,6 @@ import { Mode } from "#app/ui/ui"; import i18next from "i18next"; export class SelectGenderPhase extends Phase { - constructor() { - super(); - } - start(): void { super.start(); diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts index 11d448876d3..27ab7e374a2 100644 --- a/src/phases/select-modifier-phase.ts +++ b/src/phases/select-modifier-phase.ts @@ -26,7 +26,6 @@ import { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler"; import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler"; import { Mode } from "#app/ui/ui"; import i18next from "i18next"; -import * as Utils from "#app/utils"; import { BattlePhase } from "./battle-phase"; import Overrides from "#app/overrides"; import type { CustomModifierSettings } from "#app/modifier/modifier-type"; @@ -67,7 +66,7 @@ export class SelectModifierPhase extends BattlePhase { if (!this.isCopy) { regenerateModifierPoolThresholds(party, this.getPoolType(), this.rerollCount); } - const modifierCount = new Utils.NumberHolder(3); + const modifierCount = new NumberHolder(3); if (this.isPlayer()) { globalScene.applyModifiers(ExtraModifierModifier, true, modifierCount); globalScene.applyModifiers(TempExtraModifierModifier, true, modifierCount); diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts index b3ebe6731c9..c6ded6be7af 100644 --- a/src/phases/select-starter-phase.ts +++ b/src/phases/select-starter-phase.ts @@ -15,10 +15,6 @@ import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import * as Utils from "../utils"; export class SelectStarterPhase extends Phase { - constructor() { - super(); - } - start() { super.start(); diff --git a/src/phases/select-target-phase.ts b/src/phases/select-target-phase.ts index 2042d0a3fcf..035eaff41fa 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/moves/move"; export class SelectTargetPhase extends PokemonPhase { + // 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 2540d98fb79..87a7db29cf6 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 { + // biome-ignore lint/complexity/noUselessConstructor: This makes `battlerIndex` required constructor(battlerIndex: BattlerIndex) { super(battlerIndex); } diff --git a/src/phases/show-party-exp-bar-phase.ts b/src/phases/show-party-exp-bar-phase.ts index 568b8b615c8..139f4efcc49 100644 --- a/src/phases/show-party-exp-bar-phase.ts +++ b/src/phases/show-party-exp-bar-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import { ExpGainsSpeed } from "#app/enums/exp-gains-speed"; import { ExpNotification } from "#app/enums/exp-notification"; import { ExpBoosterModifier } from "#app/modifier/modifier"; -import * as Utils from "#app/utils"; +import { NumberHolder } from "#app/utils"; import { HidePartyExpBarPhase } from "./hide-party-exp-bar-phase"; import { LevelUpPhase } from "./level-up-phase"; import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; @@ -20,7 +20,7 @@ export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase { super.start(); const pokemon = this.getPokemon(); - const exp = new Utils.NumberHolder(this.expValue); + const exp = new NumberHolder(this.expValue); globalScene.applyModifiers(ExpBoosterModifier, true, exp); exp.value = Math.floor(exp.value); diff --git a/src/phases/show-trainer-phase.ts b/src/phases/show-trainer-phase.ts index 740c11f5c5d..b6c1e345c70 100644 --- a/src/phases/show-trainer-phase.ts +++ b/src/phases/show-trainer-phase.ts @@ -3,10 +3,6 @@ import { PlayerGender } from "#app/enums/player-gender"; import { BattlePhase } from "./battle-phase"; export class ShowTrainerPhase extends BattlePhase { - constructor() { - super(); - } - start() { super.start(); diff --git a/src/phases/summon-missing-phase.ts b/src/phases/summon-missing-phase.ts index 32bc7495dce..a692455ce47 100644 --- a/src/phases/summon-missing-phase.ts +++ b/src/phases/summon-missing-phase.ts @@ -4,10 +4,6 @@ import { SummonPhase } from "./summon-phase"; import { globalScene } from "#app/global-scene"; export class SummonMissingPhase extends SummonPhase { - constructor(fieldIndex: number) { - super(fieldIndex); - } - preSummon(): void { globalScene.ui.showText( i18next.t("battle:sendOutPokemon", { diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index 16868bf9bc0..e0903ada275 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -23,11 +23,11 @@ export class SwitchSummonPhase extends SummonPhase { /** * Constructor for creating a new SwitchSummonPhase - * @param switchType the type of switch behavior - * @param fieldIndex integer representing position on the battle field - * @param slotIndex integer for the index of pokemon (in party of 6) to switch into - * @param doReturn boolean whether to render "comeback" dialogue - * @param player boolean if the switch is from the player + * @param switchType - The type of switch behavior + * @param fieldIndex - Position on the battle field + * @param slotIndex - The index of pokemon (in party of 6) to switch into + * @param doReturn - Whether to render "comeback" dialogue + * @param player - (Optional) `true` if the switch is from the player */ constructor(switchType: SwitchType, fieldIndex: number, slotIndex: number, doReturn: boolean, player?: boolean) { super(fieldIndex, player !== undefined ? player : true); diff --git a/src/phases/test-message-phase.ts b/src/phases/test-message-phase.ts deleted file mode 100644 index d5e74efd490..00000000000 --- a/src/phases/test-message-phase.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { MessagePhase } from "./message-phase"; - -export class TestMessagePhase extends MessagePhase { - constructor(message: string) { - super(message, null, true); - } -} diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index dc455a0a62a..108366d4774 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -18,7 +18,7 @@ import { vouchers } from "#app/system/voucher"; import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler"; import { Mode } from "#app/ui/ui"; -import * as Utils from "#app/utils"; +import { isLocal, isLocalServerConnected, isNullOrUndefined } from "#app/utils"; import i18next from "i18next"; import { CheckSwitchPhase } from "./check-switch-phase"; import { EncounterPhase } from "./encounter-phase"; @@ -29,16 +29,10 @@ import { globalScene } from "#app/global-scene"; import Overrides from "#app/overrides"; export class TitlePhase extends Phase { - private loaded: boolean; + private loaded = false; private lastSessionData: SessionSaveData; public gameMode: GameModes; - constructor() { - super(); - - this.loaded = false; - } - start(): void { super.start(); @@ -282,7 +276,7 @@ export class TitlePhase extends Phase { }; // If Online, calls seed fetch from db to generate daily run. If Offline, generates a daily run based on current date. - if (!Utils.isLocal || Utils.isLocalServerConnected) { + if (!isLocal || isLocalServerConnected) { fetchDailyRunSeed() .then(seed => { if (seed) { @@ -296,7 +290,7 @@ export class TitlePhase extends Phase { }); } else { let seed: string = btoa(new Date().toISOString().substring(0, 10)); - if (!Utils.isNullOrUndefined(Overrides.DAILY_RUN_SEED_OVERRIDE)) { + if (!isNullOrUndefined(Overrides.DAILY_RUN_SEED_OVERRIDE)) { seed = Overrides.DAILY_RUN_SEED_OVERRIDE; } generateDaily(seed); diff --git a/src/phases/trainer-message-test-phase.ts b/src/phases/trainer-message-test-phase.ts deleted file mode 100644 index 23c2c86361c..00000000000 --- a/src/phases/trainer-message-test-phase.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { globalScene } from "#app/global-scene"; -import { trainerConfigs } from "#app/data/trainers/trainer-config"; -import type { TrainerType } from "#app/enums/trainer-type"; -import { BattlePhase } from "./battle-phase"; -import { TestMessagePhase } from "./test-message-phase"; - -export class TrainerMessageTestPhase extends BattlePhase { - private trainerTypes: TrainerType[]; - - constructor(...trainerTypes: TrainerType[]) { - super(); - - this.trainerTypes = trainerTypes; - } - - start() { - super.start(); - - const testMessages: string[] = []; - - for (const t of Object.keys(trainerConfigs)) { - const type = Number.parseInt(t); - if (this.trainerTypes.length && !this.trainerTypes.find(tt => tt === (type as TrainerType))) { - continue; - } - const config = trainerConfigs[type]; - [ - config.encounterMessages, - config.femaleEncounterMessages, - config.victoryMessages, - config.femaleVictoryMessages, - config.defeatMessages, - config.femaleDefeatMessages, - ].map(messages => { - if (messages?.length) { - testMessages.push(...messages); - } - }); - } - - for (const message of testMessages) { - globalScene.pushPhase(new TestMessagePhase(message)); - } - - this.end(); - } -} diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts index a024885121f..637ddea8b56 100644 --- a/src/phases/trainer-victory-phase.ts +++ b/src/phases/trainer-victory-phase.ts @@ -3,7 +3,7 @@ import { TrainerType } from "#app/enums/trainer-type"; import { modifierTypes } from "#app/modifier/modifier-type"; import { vouchers } from "#app/system/voucher"; import i18next from "i18next"; -import * as Utils from "#app/utils"; +import { randSeedItem } from "#app/utils"; import { BattlePhase } from "./battle-phase"; import { ModifierRewardPhase } from "./modifier-reward-phase"; import { MoneyRewardPhase } from "./money-reward-phase"; @@ -14,10 +14,6 @@ import { achvs } from "#app/system/achv"; import { timedEventManager } from "#app/global-event-manager"; export class TrainerVictoryPhase extends BattlePhase { - constructor() { - super(); - } - start() { globalScene.disableMenu = true; @@ -82,7 +78,7 @@ export class TrainerVictoryPhase extends BattlePhase { const victoryMessages = globalScene.currentBattle.trainer?.getVictoryMessages()!; // TODO: is this bang correct? let message: string; globalScene.executeWithSeedOffset( - () => (message = Utils.randSeedItem(victoryMessages)), + () => (message = randSeedItem(victoryMessages)), globalScene.currentBattle.waveIndex, ); message = message!; // tell TS compiler it's defined now diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index ddfc0955508..9b84ea05e58 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -18,10 +18,6 @@ import { PokemonHealPhase } from "./pokemon-heal-phase"; import { globalScene } from "#app/global-scene"; export class TurnEndPhase extends FieldPhase { - constructor() { - super(); - } - start() { super.start(); diff --git a/src/phases/turn-init-phase.ts b/src/phases/turn-init-phase.ts index 3104b65eb3f..0c110024af7 100644 --- a/src/phases/turn-init-phase.ts +++ b/src/phases/turn-init-phase.ts @@ -15,10 +15,6 @@ import { TurnStartPhase } from "./turn-start-phase"; import { globalScene } from "#app/global-scene"; export class TurnInitPhase extends FieldPhase { - constructor() { - super(); - } - start() { super.start(); diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index 34dd7df3e89..d5b4160fe1b 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -6,7 +6,7 @@ import type Pokemon from "#app/field/pokemon"; import { PokemonMove } from "#app/field/pokemon"; import { BypassSpeedChanceModifier } from "#app/modifier/modifier"; import { Command } from "#app/ui/command-ui-handler"; -import * as Utils from "#app/utils"; +import { randSeedShuffle, BooleanHolder } from "#app/utils"; import { AttemptCapturePhase } from "./attempt-capture-phase"; import { AttemptRunPhase } from "./attempt-run-phase"; import { BerryPhase } from "./berry-phase"; @@ -24,10 +24,6 @@ import { globalScene } from "#app/global-scene"; import { TeraPhase } from "./tera-phase"; export class TurnStartPhase extends FieldPhase { - constructor() { - super(); - } - /** * 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. @@ -43,14 +39,14 @@ export class TurnStartPhase extends FieldPhase { // was varying based on how long since you last reloaded globalScene.executeWithSeedOffset( () => { - orderedTargets = Utils.randSeedShuffle(orderedTargets); + orderedTargets = randSeedShuffle(orderedTargets); }, globalScene.currentBattle.turn, globalScene.waveSeed, ); // Next, a check for Trick Room is applied to determine sort order. - const speedReversed = new Utils.BooleanHolder(false); + const speedReversed = new BooleanHolder(false); globalScene.arena.applyTags(TrickRoomTag, false, speedReversed); // Adjust the sort function based on whether Trick Room is active. @@ -80,8 +76,8 @@ export class TurnStartPhase extends FieldPhase { .getField(true) .filter(p => p.summonData) .map(p => { - const bypassSpeed = new Utils.BooleanHolder(false); - const canCheckHeldItems = new Utils.BooleanHolder(true); + const bypassSpeed = new BooleanHolder(false); + const canCheckHeldItems = new BooleanHolder(true); applyAbAttrs(BypassSpeedChanceAbAttr, p, null, false, bypassSpeed); applyAbAttrs(PreventBypassSpeedChanceAbAttr, p, null, false, bypassSpeed, canCheckHeldItems); if (canCheckHeldItems.value) { diff --git a/src/phases/unavailable-phase.ts b/src/phases/unavailable-phase.ts index c0b5d4224c5..33042739971 100644 --- a/src/phases/unavailable-phase.ts +++ b/src/phases/unavailable-phase.ts @@ -4,10 +4,6 @@ import { Mode } from "#app/ui/ui"; import { LoginPhase } from "./login-phase"; export class UnavailablePhase extends Phase { - constructor() { - super(); - } - start(): void { globalScene.ui.setMode(Mode.UNAVAILABLE, () => { globalScene.unshiftPhase(new LoginPhase(true)); diff --git a/src/phases/weather-effect-phase.ts b/src/phases/weather-effect-phase.ts index d7a1f193029..5284c9fba85 100644 --- a/src/phases/weather-effect-phase.ts +++ b/src/phases/weather-effect-phase.ts @@ -15,7 +15,7 @@ import { BattlerTagType } from "#app/enums/battler-tag-type"; import { WeatherType } from "#app/enums/weather-type"; import type Pokemon from "#app/field/pokemon"; import { HitResult } from "#app/field/pokemon"; -import * as Utils from "#app/utils"; +import { BooleanHolder, toDmgValue } from "#app/utils"; import { CommonAnimPhase } from "./common-anim-phase"; export class WeatherEffectPhase extends CommonAnimPhase { @@ -35,14 +35,13 @@ export class WeatherEffectPhase extends CommonAnimPhase { this.weather = globalScene?.arena?.weather; if (!this.weather) { - this.end(); - return; + return this.end(); } this.setAnimation(CommonAnim.SUNNY + (this.weather.weatherType - 1)); if (this.weather.isDamaging()) { - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); this.executeForAll((pokemon: Pokemon) => applyPreWeatherEffectAbAttrs(SuppressWeatherEffectAbAttr, pokemon, this.weather, cancelled), @@ -50,7 +49,7 @@ export class WeatherEffectPhase extends CommonAnimPhase { if (!cancelled.value) { const inflictDamage = (pokemon: Pokemon) => { - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); applyPreWeatherEffectAbAttrs(PreWeatherDamageAbAttr, pokemon, this.weather, cancelled); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); @@ -63,9 +62,9 @@ export class WeatherEffectPhase extends CommonAnimPhase { return; } - const damage = Utils.toDmgValue(pokemon.getMaxHp() / 16); + const damage = toDmgValue(pokemon.getMaxHp() / 16); - globalScene.queueMessage(getWeatherDamageMessage(this.weather?.weatherType!, pokemon)!); // TODO: are those bangs correct? + globalScene.queueMessage(getWeatherDamageMessage(this.weather!.weatherType, pokemon) ?? ""); pokemon.damageAndUpdate(damage, { result: HitResult.INDIRECT, ignoreSegments: true }); }; From 0479b9dfcc951969fec368f938ac3419673536e6 Mon Sep 17 00:00:00 2001 From: Diogo Cruz Diniz <50275496+didas72@users.noreply.github.com> Date: Mon, 7 Apr 2025 14:50:52 +0100 Subject: [PATCH 06/52] Fix #2735: Hazard moves incorrectly require targets (#5635) * Fix #2735: Hazard moves incorrectly require targets Hazard moves should no longer require targets to successfully execute * Apply suggestions from code review made by NightKev Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/battle-anims.ts | 3 +- src/data/moves/move.ts | 4 +- src/phases/move-effect-phase.ts | 415 ++++++++++++++++---------------- src/phases/move-phase.ts | 6 +- test/moves/spikes.test.ts | 14 ++ 5 files changed, 236 insertions(+), 206 deletions(-) diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 341976b388d..511c80bee72 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -1428,7 +1428,8 @@ export class MoveAnim extends BattleAnim { public move: Moves; constructor(move: Moves, user: Pokemon, target: BattlerIndex, playOnEmptyField = false) { - super(user, globalScene.getField()[target], playOnEmptyField); + // Set target to the user pokemon if no target is found to avoid crashes + super(user, globalScene.getField()[target] ?? user, playOnEmptyField); this.move = move; } diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 421314b1945..7a820d984d0 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -5928,7 +5928,7 @@ export class AddArenaTagAttr extends MoveEffectAttr { } if ((move.chance < 0 || move.chance === 100 || user.randSeedInt(100) < move.chance) && user.getLastXMoves(1)[0]?.result === MoveResult.SUCCESS) { - const side = (this.selfSideTarget ? user : target).isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + const side = ((this.selfSideTarget ? user : target).isPlayer() !== (move.hasAttr(AddArenaTrapTagAttr) && target === user)) ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; globalScene.arena.addTag(this.tagType, this.turnCount, move.id, user.id, side); return true; } @@ -5977,7 +5977,7 @@ export class RemoveArenaTagsAttr extends MoveEffectAttr { export class AddArenaTrapTagAttr extends AddArenaTagAttr { getCondition(): MoveConditionFunc { return (user, target, move) => { - const side = (this.selfSideTarget ? user : target).isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + const side = (this.selfSideTarget !== user.isPlayer()) ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER; const tag = globalScene.arena.getTagOnSide(this.tagType, side) as ArenaTrapTag; if (!tag) { return true; diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index 7cc389651dd..6c46f7ff8c0 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -26,6 +26,7 @@ import { } from "#app/data/battler-tags"; import type { MoveAttr } from "#app/data/moves/move"; import { + AddArenaTrapTagAttr, applyFilteredMoveAttrs, applyMoveAttrs, AttackMove, @@ -209,12 +210,12 @@ export class MoveEffectPhase extends PokemonPhase { targets.some(t => t.hasAbilityWithAttr(ReflectStatusMoveAbAttr) || !!t.getTag(BattlerTagType.MAGIC_COAT)); /** - * If no targets are left for the move to hit (FAIL), or the invoked move is non-reflectable, single-target + * If no targets are left for the move to hit and it is not a hazard move (FAIL), or the invoked move is non-reflectable, single-target * (and not random target) and failed the hit check against its target (MISS), log the move * as FAILed or MISSed (depending on the conditions above) and end this phase. */ if ( - !hasActiveTargets || + (!hasActiveTargets && !move.hasAttr(AddArenaTrapTagAttr)) || (!mayBounce && !move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && @@ -239,18 +240,28 @@ export class MoveEffectPhase extends PokemonPhase { return this.end(); } - const playOnEmptyField = globalScene.currentBattle?.mysteryEncounter?.hasBattleAnimationsWithoutTargets ?? false; - // Move animation only needs one target - new MoveAnim(move.id as Moves, user, this.getFirstTarget()!.getBattlerIndex(), playOnEmptyField).play( - move.hitsSubstitute(user, this.getFirstTarget()!), - () => { - /** Has the move successfully hit a target (for damage) yet? */ - let hasHit = false; + const playOnEmptyField = + (globalScene.currentBattle?.mysteryEncounter?.hasBattleAnimationsWithoutTargets ?? false) || + (!hasActiveTargets && move.hasAttr(AddArenaTrapTagAttr)); + // Move animation only needs one target. The attacker is used as a fallback. + new MoveAnim( + move.id as Moves, + user, + this.getFirstTarget()?.getBattlerIndex() ?? BattlerIndex.ATTACKER, + playOnEmptyField, + ).play(move.hitsSubstitute(user, this.getFirstTarget()!), () => { + /** Has the move successfully hit a target (for damage) yet? */ + let hasHit = false; - // Prevent ENEMY_SIDE targeted moves from occurring twice in double battles - // and check which target will magic bounce. - const trueTargets: Pokemon[] = - move.moveTarget !== MoveTarget.ENEMY_SIDE + // Prevent ENEMY_SIDE targeted moves from occurring twice in double battles + // and check which target will magic bounce. + // In the event that the move is a hazard move, there may be no target and the move should still succeed. + // In this case, the user is used as the "target" to prevent a crash. + // This should not affect normal execution of the move otherwise. + const trueTargets: Pokemon[] = + !hasActiveTargets && move.hasAttr(AddArenaTrapTagAttr) + ? [user] + : move.moveTarget !== MoveTarget.ENEMY_SIDE ? targets : (() => { const magicCoatTargets = targets.filter( @@ -264,27 +275,27 @@ export class MoveEffectPhase extends PokemonPhase { return [magicCoatTargets[0]]; })(); - const queuedPhases: Phase[] = []; - for (const target of trueTargets) { - /** The {@linkcode ArenaTagSide} to which the target belongs */ - const targetSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; - /** Has the invoked move been cancelled by conditional protection (e.g Quick Guard)? */ - const hasConditionalProtectApplied = new BooleanHolder(false); - /** Does the applied conditional protection bypass Protect-ignoring effects? */ - const bypassIgnoreProtect = new BooleanHolder(false); - /** If the move is not targeting a Pokemon on the user's side, try to apply conditional protection effects */ - if (!this.move.getMove().isAllyTarget()) { - globalScene.arena.applyTagsForSide( - ConditionalProtectTag, - targetSide, - false, - hasConditionalProtectApplied, - user, - target, - move.id, - bypassIgnoreProtect, - ); - } + const queuedPhases: Phase[] = []; + for (const target of trueTargets) { + /** The {@linkcode ArenaTagSide} to which the target belongs */ + const targetSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + /** Has the invoked move been cancelled by conditional protection (e.g Quick Guard)? */ + const hasConditionalProtectApplied = new BooleanHolder(false); + /** Does the applied conditional protection bypass Protect-ignoring effects? */ + const bypassIgnoreProtect = new BooleanHolder(false); + /** If the move is not targeting a Pokemon on the user's side, try to apply conditional protection effects */ + if (!this.move.getMove().isAllyTarget()) { + globalScene.arena.applyTagsForSide( + ConditionalProtectTag, + targetSide, + false, + hasConditionalProtectApplied, + user, + target, + move.id, + bypassIgnoreProtect, + ); + } /** Is the target protected by Protect, etc. or a relevant conditional protection effect? */ const isProtected = @@ -297,13 +308,13 @@ export class MoveEffectPhase extends PokemonPhase { (this.move.getMove().category !== MoveCategory.STATUS && target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType)))); - /** Is the target hidden by the effects of its Commander ability? */ - const isCommanding = - globalScene.currentBattle.double && - target.getAlly()?.getTag(BattlerTagType.COMMANDED)?.getSourcePokemon() === target; + /** Is the target hidden by the effects of its Commander ability? */ + const isCommanding = + globalScene.currentBattle.double && + target.getAlly()?.getTag(BattlerTagType.COMMANDED)?.getSourcePokemon() === target; - /** Is the target reflecting status moves from the magic coat move? */ - const isReflecting = !!target.getTag(BattlerTagType.MAGIC_COAT); + /** Is the target reflecting status moves from the magic coat move? */ + const isReflecting = !!target.getTag(BattlerTagType.MAGIC_COAT); /** Is the target's magic bounce ability not ignored and able to reflect this move? */ const canMagicBounce = @@ -311,16 +322,16 @@ export class MoveEffectPhase extends PokemonPhase { !move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user, target }) && target.hasAbilityWithAttr(ReflectStatusMoveAbAttr); - const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); + const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); - /** Is the target reflecting the effect, not protected, and not in an semi-invulnerable state?*/ - const willBounce = - !isProtected && - !this.reflected && - !isCommanding && - move.hasFlag(MoveFlags.REFLECTABLE) && - (isReflecting || canMagicBounce) && - !semiInvulnerableTag; + /** Is the target reflecting the effect, not protected, and not in an semi-invulnerable state?*/ + const willBounce = + !isProtected && + !this.reflected && + !isCommanding && + move.hasFlag(MoveFlags.REFLECTABLE) && + (isReflecting || canMagicBounce) && + !semiInvulnerableTag; // If the move will bounce, then queue the bounce and move on to the next target if (!target.switchOutStatus && willBounce) { @@ -338,171 +349,171 @@ export class MoveEffectPhase extends PokemonPhase { queuedPhases.push(new HideAbilityPhase()); } - queuedPhases.push( - new MovePhase(target, newTargets, new PokemonMove(move.id, 0, 0, true), true, true, true), + queuedPhases.push(new MovePhase(target, newTargets, new PokemonMove(move.id, 0, 0, true), true, true, true)); + continue; + } + + /** Is the pokemon immune due to an ablility, and also not in a semi invulnerable state? */ + const isImmune = + target.hasAbilityWithAttr(TypeImmunityAbAttr) && + target.getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move) && + !semiInvulnerableTag; + + /** + * If the move missed a target, stop all future hits against that target + * and move on to the next target (if there is one). + */ + if ( + target.switchOutStatus || + isCommanding || + (!isImmune && + !isProtected && + !targetHitChecks[target.getBattlerIndex()] && + !move.hasAttr(AddArenaTrapTagAttr)) + ) { + this.stopMultiHit(target); + if (!target.switchOutStatus) { + globalScene.queueMessage( + i18next.t("battle:attackMissed", { + pokemonNameWithAffix: getPokemonNameWithAffix(target), + }), ); - continue; } - - /** Is the pokemon immune due to an ablility, and also not in a semi invulnerable state? */ - const isImmune = - target.hasAbilityWithAttr(TypeImmunityAbAttr) && - target.getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move) && - !semiInvulnerableTag; - - /** - * If the move missed a target, stop all future hits against that target - * and move on to the next target (if there is one). - */ - if ( - target.switchOutStatus || - isCommanding || - (!isImmune && !isProtected && !targetHitChecks[target.getBattlerIndex()]) - ) { - this.stopMultiHit(target); - if (!target.switchOutStatus) { - globalScene.queueMessage( - i18next.t("battle:attackMissed", { - pokemonNameWithAffix: getPokemonNameWithAffix(target), - }), - ); - } - if (moveHistoryEntry.result === MoveResult.PENDING) { - moveHistoryEntry.result = MoveResult.MISS; - } - user.pushMoveHistory(moveHistoryEntry); - applyMoveAttrs(MissEffectAttr, user, null, move); - continue; - } - - /** Does this phase represent the invoked move's first strike? */ - const firstHit = user.turnData.hitsLeft === user.turnData.hitCount; - - // Only log the move's result on the first strike - if (firstHit) { - user.pushMoveHistory(moveHistoryEntry); - } - - /** - * Since all fail/miss checks have applied, the move is considered successfully applied. - * It's worth noting that if the move has no effect or is protected against, this assignment - * is overwritten and the move is logged as a FAIL. - */ - moveHistoryEntry.result = MoveResult.SUCCESS; - - /** - * Stores the result of applying the invoked move to the target. - * If the target is protected, the result is always `NO_EFFECT`. - * Otherwise, the hit result is based on type effectiveness, immunities, - * and other factors that may negate the attack or status application. - * - * Internally, the call to {@linkcode Pokemon.apply} is where damage is calculated - * (for attack moves) and the target's HP is updated. However, this isn't - * made visible to the user until the resulting {@linkcode DamagePhase} - * is invoked. - */ - const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT; - - /** Does {@linkcode hitResult} indicate that damage was dealt to the target? */ - const dealsDamage = [ - HitResult.EFFECTIVE, - HitResult.SUPER_EFFECTIVE, - HitResult.NOT_VERY_EFFECTIVE, - HitResult.ONE_HIT_KO, - ].includes(hitResult); - - /** Is this target the first one hit by the move on its current strike? */ - const firstTarget = dealsDamage && !hasHit; - if (firstTarget) { - hasHit = true; - } - - /** - * If the move has no effect on the target (i.e. the target is protected or immune), - * change the logged move result to FAIL. - */ - if (hitResult === HitResult.NO_EFFECT) { - moveHistoryEntry.result = MoveResult.FAIL; - } - - /** Does this phase represent the invoked move's last strike? */ - const lastHit = user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive(); - - /** - * If the user can change forms by using the invoked move, - * it only changes forms after the move's last hit - * (see Relic Song's interaction with Parental Bond when used by Meloetta). - */ - if (lastHit) { - globalScene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger); - /** - * Multi-Lens, Multi Hit move and Parental Bond check for PostDamageAbAttr - * other damage source are calculated in damageAndUpdate in pokemon.ts - */ - if (user.turnData.hitCount > 1) { - applyPostDamageAbAttrs(PostDamageAbAttr, target, 0, target.hasPassive(), false, [], user); - } - } - - applyFilteredMoveAttrs( - (attr: MoveAttr) => - attr instanceof MoveEffectAttr && - attr.trigger === MoveEffectTrigger.PRE_APPLY && - (!attr.firstHitOnly || firstHit) && - (!attr.lastHitOnly || lastHit) && - hitResult !== HitResult.NO_EFFECT, - user, - target, - move, - ); - - if (hitResult !== HitResult.FAIL) { - this.applySelfTargetEffects(user, target, firstHit, lastHit); - - if (hitResult !== HitResult.NO_EFFECT) { - this.applyPostApplyEffects(user, target, firstHit, lastHit); - this.applyHeldItemFlinchCheck(user, target, dealsDamage); - this.applySuccessfulAttackEffects(user, target, firstHit, lastHit, !!isProtected, hitResult, firstTarget); - } else { - applyMoveAttrs(NoEffectAttr, user, null, move); - } + if (moveHistoryEntry.result === MoveResult.PENDING) { + moveHistoryEntry.result = MoveResult.MISS; } + user.pushMoveHistory(moveHistoryEntry); + applyMoveAttrs(MissEffectAttr, user, null, move); + continue; } - // Apply queued phases - if (queuedPhases.length) { - globalScene.appendToPhase(queuedPhases, MoveEndPhase); - } - // Apply the move's POST_TARGET effects on the move's last hit, after all targeted effects have resolved - if (user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive()) { - applyFilteredMoveAttrs( - (attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET, - user, - null, - move, - ); + /** Does this phase represent the invoked move's first strike? */ + const firstHit = user.turnData.hitsLeft === user.turnData.hitCount; + + // Only log the move's result on the first strike + if (firstHit) { + user.pushMoveHistory(moveHistoryEntry); } /** - * Remove the target's substitute (if it exists and has expired) - * after all targeted effects have applied. - * This prevents blocked effects from applying until after this hit resolves. + * Since all fail/miss checks have applied, the move is considered successfully applied. + * It's worth noting that if the move has no effect or is protected against, this assignment + * is overwritten and the move is logged as a FAIL. */ - targets.forEach(target => { - const substitute = target.getTag(SubstituteTag); - if (substitute && substitute.hp <= 0) { - target.lapseTag(BattlerTagType.SUBSTITUTE); - } - }); + moveHistoryEntry.result = MoveResult.SUCCESS; - const moveType = user.getMoveType(move, true); - if (move.category !== MoveCategory.STATUS && !user.stellarTypesBoosted.includes(moveType)) { - user.stellarTypesBoosted.push(moveType); + /** + * Stores the result of applying the invoked move to the target. + * If the target is protected, the result is always `NO_EFFECT`. + * Otherwise, the hit result is based on type effectiveness, immunities, + * and other factors that may negate the attack or status application. + * + * Internally, the call to {@linkcode Pokemon.apply} is where damage is calculated + * (for attack moves) and the target's HP is updated. However, this isn't + * made visible to the user until the resulting {@linkcode DamagePhase} + * is invoked. + */ + const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT; + + /** Does {@linkcode hitResult} indicate that damage was dealt to the target? */ + const dealsDamage = [ + HitResult.EFFECTIVE, + HitResult.SUPER_EFFECTIVE, + HitResult.NOT_VERY_EFFECTIVE, + HitResult.ONE_HIT_KO, + ].includes(hitResult); + + /** Is this target the first one hit by the move on its current strike? */ + const firstTarget = dealsDamage && !hasHit; + if (firstTarget) { + hasHit = true; } - this.end(); - }, - ); + /** + * If the move has no effect on the target (i.e. the target is protected or immune), + * change the logged move result to FAIL. + */ + if (hitResult === HitResult.NO_EFFECT) { + moveHistoryEntry.result = MoveResult.FAIL; + } + + /** Does this phase represent the invoked move's last strike? */ + const lastHit = user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive(); + + /** + * If the user can change forms by using the invoked move, + * it only changes forms after the move's last hit + * (see Relic Song's interaction with Parental Bond when used by Meloetta). + */ + if (lastHit) { + globalScene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger); + /** + * Multi-Lens, Multi Hit move and Parental Bond check for PostDamageAbAttr + * other damage source are calculated in damageAndUpdate in pokemon.ts + */ + if (user.turnData.hitCount > 1) { + applyPostDamageAbAttrs(PostDamageAbAttr, target, 0, target.hasPassive(), false, [], user); + } + } + + applyFilteredMoveAttrs( + (attr: MoveAttr) => + attr instanceof MoveEffectAttr && + attr.trigger === MoveEffectTrigger.PRE_APPLY && + (!attr.firstHitOnly || firstHit) && + (!attr.lastHitOnly || lastHit) && + hitResult !== HitResult.NO_EFFECT, + user, + target, + move, + ); + + if (hitResult !== HitResult.FAIL) { + this.applySelfTargetEffects(user, target, firstHit, lastHit); + + if (hitResult !== HitResult.NO_EFFECT) { + this.applyPostApplyEffects(user, target, firstHit, lastHit); + this.applyHeldItemFlinchCheck(user, target, dealsDamage); + this.applySuccessfulAttackEffects(user, target, firstHit, lastHit, !!isProtected, hitResult, firstTarget); + } else { + applyMoveAttrs(NoEffectAttr, user, null, move); + } + } + } + + // Apply queued phases + if (queuedPhases.length) { + globalScene.appendToPhase(queuedPhases, MoveEndPhase); + } + // Apply the move's POST_TARGET effects on the move's last hit, after all targeted effects have resolved + if (user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive()) { + applyFilteredMoveAttrs( + (attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET, + user, + null, + move, + ); + } + + /** + * Remove the target's substitute (if it exists and has expired) + * after all targeted effects have applied. + * This prevents blocked effects from applying until after this hit resolves. + */ + targets.forEach(target => { + const substitute = target.getTag(SubstituteTag); + if (substitute && substitute.hp <= 0) { + target.lapseTag(BattlerTagType.SUBSTITUTE); + } + }); + + const moveType = user.getMoveType(move, true); + if (move.category !== MoveCategory.STATUS && !user.stellarTypesBoosted.includes(moveType)) { + user.stellarTypesBoosted.push(moveType); + } + + this.end(); + }); } public override end(): void { diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 82b73f681a0..5232dfee8ba 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -15,6 +15,7 @@ import type { DelayedAttackTag } from "#app/data/arena-tag"; import { CommonAnim } from "#app/data/battle-anims"; import { BattlerTagLapseType, CenterOfAttentionTag } from "#app/data/battler-tags"; import { + AddArenaTrapTagAttr, allMoves, applyMoveAttrs, BypassRedirectAttr, @@ -201,7 +202,10 @@ export class MovePhase extends BattlePhase { const targets = this.getActiveTargetPokemon(); const moveQueue = this.pokemon.getMoveQueue(); - if (targets.length === 0 || (moveQueue.length && moveQueue[0].move === Moves.NONE)) { + if ( + (targets.length === 0 && !this.move.getMove().hasAttr(AddArenaTrapTagAttr)) || + (moveQueue.length && moveQueue[0].move === Moves.NONE) + ) { this.showMoveText(); this.showFailedText(); this.cancel(); diff --git a/test/moves/spikes.test.ts b/test/moves/spikes.test.ts index 9bf0e5e1437..76af15777bb 100644 --- a/test/moves/spikes.test.ts +++ b/test/moves/spikes.test.ts @@ -4,6 +4,7 @@ import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; describe("Moves - Spikes", () => { let phaserGame: Phaser.Game; @@ -77,4 +78,17 @@ describe("Moves - Spikes", () => { const enemy = game.scene.getEnemyParty()[0]; expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); }, 20000); + + it("should work when all targets fainted", async () => { + game.override.enemySpecies(Species.DIGLETT); + game.override.battleType("double"); + game.override.startingLevel(50); + await game.classicMode.startBattle([Species.RAYQUAZA, Species.ROWLET]); + + game.move.select(Moves.EARTHQUAKE); + game.move.select(Moves.SPIKES, 1); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(game.scene.arena.getTagOnSide(ArenaTrapTag, ArenaTagSide.ENEMY)).toBeDefined(); + }, 20000); }); From 1b79d1f832b2163b265beb4bff2ec0ae45a0fb28 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Mon, 7 Apr 2025 15:53:35 -0700 Subject: [PATCH 07/52] [Refactor] Re-implement save migration system (#5634) --- package-lock.json | 7 + package.json | 1 + src/@types/SessionSaveMigrator.ts | 6 + src/@types/SettingsSaveMigrator.ts | 5 + src/@types/SystemSaveMigrator.ts | 6 + .../version_migration/version_converter.ts | 273 +++++++----------- .../version_migration/versions/v1_0_4.ts | 104 ++++--- .../version_migration/versions/v1_1_0.ts | 5 - .../version_migration/versions/v1_7_0.ts | 36 ++- .../version_migration/versions/v1_8_3.ts | 22 +- 10 files changed, 227 insertions(+), 238 deletions(-) create mode 100644 src/@types/SessionSaveMigrator.ts create mode 100644 src/@types/SettingsSaveMigrator.ts create mode 100644 src/@types/SystemSaveMigrator.ts delete mode 100644 src/system/version_migration/versions/v1_1_0.ts diff --git a/package-lock.json b/package-lock.json index 33e9dd08104..6b880370f0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "hasInstallScript": true, "dependencies": { "@material/material-color-utilities": "^0.2.7", + "compare-versions": "^6.1.1", "crypto-js": "^4.2.0", "i18next": "^24.2.2", "i18next-browser-languagedetector": "^8.0.4", @@ -3605,6 +3606,12 @@ "node": ">=18" } }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", diff --git a/package.json b/package.json index 6b1c73db158..c84e926fc35 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ }, "dependencies": { "@material/material-color-utilities": "^0.2.7", + "compare-versions": "^6.1.1", "crypto-js": "^4.2.0", "i18next": "^24.2.2", "i18next-browser-languagedetector": "^8.0.4", diff --git a/src/@types/SessionSaveMigrator.ts b/src/@types/SessionSaveMigrator.ts new file mode 100644 index 00000000000..c4b0ad8dda4 --- /dev/null +++ b/src/@types/SessionSaveMigrator.ts @@ -0,0 +1,6 @@ +import type { SessionSaveData } from "#app/system/game-data"; + +export interface SessionSaveMigrator { + version: string; + migrate: (data: SessionSaveData) => void; +} diff --git a/src/@types/SettingsSaveMigrator.ts b/src/@types/SettingsSaveMigrator.ts new file mode 100644 index 00000000000..aae3df7cc60 --- /dev/null +++ b/src/@types/SettingsSaveMigrator.ts @@ -0,0 +1,5 @@ +export interface SettingsSaveMigrator { + version: string; + // biome-ignore lint/complexity/noBannedTypes: TODO - refactor settings + migrate: (data: Object) => void; +} diff --git a/src/@types/SystemSaveMigrator.ts b/src/@types/SystemSaveMigrator.ts new file mode 100644 index 00000000000..a22b5f6c93d --- /dev/null +++ b/src/@types/SystemSaveMigrator.ts @@ -0,0 +1,6 @@ +import type { SystemSaveData } from "#app/system/game-data"; + +export interface SystemSaveMigrator { + version: string; + migrate: (data: SystemSaveData) => void; +} diff --git a/src/system/version_migration/version_converter.ts b/src/system/version_migration/version_converter.ts index 074f60c2c5d..4b712609819 100644 --- a/src/system/version_migration/version_converter.ts +++ b/src/system/version_migration/version_converter.ts @@ -1,19 +1,104 @@ -import type { SessionSaveData, SystemSaveData } from "../game-data"; +import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator"; +import type { SettingsSaveMigrator } from "#app/@types/SettingsSaveMigrator"; +import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator"; +import type { SessionSaveData, SystemSaveData } from "#app/system/game-data"; +import { compareVersions } from "compare-versions"; import { version } from "../../../package.json"; +/* +// template for save migrator creation +// versions/vA_B_C.ts + +// The version for each migrator should match the filename, ie: `vA_B_C.ts` -> `version: "A.B.C" +// This is the target version (aka the version we're ending up on after the migrators are run) + +// The name for each migrator should match its purpose. For example, if you're fixing +// the ability index of a pokemon, it might be called `migratePokemonAbilityIndex` + +const systemMigratorA: SystemSaveMigrator = { + version: "A.B.C", + migrate: (data: SystemSaveData): void => { + // migration code goes here + }, +}; + +export const systemMigrators: Readonly = [systemMigratorA] as const; + +const sessionMigratorA: SessionSaveMigrator = { + version: "A.B.C", + migrate: (data: SessionSaveData): void => { + // migration code goes here + }, +}; + +export const sessionMigrators: Readonly = [sessionMigratorA] as const; + +const settingsMigratorA: SettingsSaveMigrator = { + version: "A.B.C", + // biome-ignore lint/complexity/noBannedTypes: TODO - refactor settings + migrate: (data: Object): void => { + // migration code goes here + }, +}; + +export const settingsMigrators: Readonly = [settingsMigratorA] as const; +*/ + +// --- vA.B.C PATCHES --- // +// import * as vA_B_C from "./versions/vA_B_C"; + // --- v1.0.4 (and below) PATCHES --- // import * as v1_0_4 from "./versions/v1_0_4"; -// --- v1.1.0 PATCHES --- // -import * as v1_1_0 from "./versions/v1_1_0"; - // --- v1.7.0 PATCHES --- // import * as v1_7_0 from "./versions/v1_7_0"; // --- v1.8.3 PATCHES --- // import * as v1_8_3 from "./versions/v1_8_3"; -const LATEST_VERSION = version.split(".").map(value => Number.parseInt(value)); +/** Current game version */ +const LATEST_VERSION = version; + +type SaveMigrator = SystemSaveMigrator | SessionSaveMigrator | SettingsSaveMigrator; + +// biome-ignore lint/complexity/noBannedTypes: TODO - refactor settings +type SaveData = SystemSaveData | SessionSaveData | Object; + +// To add a new set of migrators, create a new `.push()` line like so: +// `systemMigrators.push(...vA_B_C.systemMigrators);` + +/** All system save migrators */ +const systemMigrators: SystemSaveMigrator[] = []; +systemMigrators.push(...v1_0_4.systemMigrators); +systemMigrators.push(...v1_7_0.systemMigrators); +systemMigrators.push(...v1_8_3.systemMigrators); + +/** All session save migrators */ +const sessionMigrators: SessionSaveMigrator[] = []; +sessionMigrators.push(...v1_0_4.sessionMigrators); +sessionMigrators.push(...v1_7_0.sessionMigrators); + +/** All settings migrators */ +const settingsMigrators: SettingsSaveMigrator[] = []; +settingsMigrators.push(...v1_0_4.settingsMigrators); + +/** Sorts migrators by their stated version, ensuring they are applied in order from oldest to newest */ +const sortMigrators = (migrators: SaveMigrator[]): void => { + migrators.sort((a, b) => compareVersions(a.version, b.version)); +}; + +sortMigrators(systemMigrators); +sortMigrators(sessionMigrators); +sortMigrators(settingsMigrators); + +const applyMigrators = (migrators: readonly SaveMigrator[], data: SaveData, saveVersion: string) => { + for (const migrator of migrators) { + const isMigratorVersionHigher = compareVersions(saveVersion, migrator.version) === -1; + if (isMigratorVersionHigher) { + migrator.migrate(data as any); + } + } +}; /** * Converts incoming {@linkcode SystemSaveData} that has a version below the @@ -26,12 +111,12 @@ const LATEST_VERSION = version.split(".").map(value => Number.parseInt(value)); * @see {@link SystemVersionConverter} */ export function applySystemVersionMigration(data: SystemSaveData) { - const curVersion = data.gameVersion.split(".").map(value => Number.parseInt(value)); + const prevVersion = data.gameVersion; + const isCurrentVersionHigher = compareVersions(prevVersion, LATEST_VERSION) === -1; - if (!curVersion.every((value, index) => value === LATEST_VERSION[index])) { - const converter = new SystemVersionConverter(); - converter.applyStaticPreprocessors(data); - converter.applyMigration(data, curVersion); + if (isCurrentVersionHigher) { + applyMigrators(systemMigrators, data, prevVersion); + console.log(`System data successfully migrated to v${LATEST_VERSION}!`); } } @@ -46,12 +131,15 @@ export function applySystemVersionMigration(data: SystemSaveData) { * @see {@link SessionVersionConverter} */ export function applySessionVersionMigration(data: SessionSaveData) { - const curVersion = data.gameVersion.split(".").map(value => Number.parseInt(value)); + const prevVersion = data.gameVersion; + const isCurrentVersionHigher = compareVersions(prevVersion, LATEST_VERSION) === -1; - if (!curVersion.every((value, index) => value === LATEST_VERSION[index])) { - const converter = new SessionVersionConverter(); - converter.applyStaticPreprocessors(data); - converter.applyMigration(data, curVersion); + if (isCurrentVersionHigher) { + // Always sanitize money as a safeguard + data.money = Math.floor(data.money); + + applyMigrators(sessionMigrators, data, prevVersion); + console.log(`Session data successfully migrated to v${LATEST_VERSION}!`); } } @@ -65,156 +153,13 @@ export function applySessionVersionMigration(data: SessionSaveData) { * @param data Settings data object * @see {@link SettingsVersionConverter} */ +// biome-ignore lint/complexity/noBannedTypes: TODO - refactor settings export function applySettingsVersionMigration(data: Object) { - const gameVersion: string = data.hasOwnProperty("gameVersion") ? data["gameVersion"] : "1.0.0"; - const curVersion = gameVersion.split(".").map(value => Number.parseInt(value)); + const prevVersion: string = data.hasOwnProperty("gameVersion") ? data["gameVersion"] : "1.0.0"; + const isCurrentVersionHigher = compareVersions(prevVersion, LATEST_VERSION) === -1; - if (!curVersion.every((value, index) => value === LATEST_VERSION[index])) { - const converter = new SettingsVersionConverter(); - converter.applyStaticPreprocessors(data); - converter.applyMigration(data, curVersion); - } -} - -/** - * Abstract class encapsulating the logic for migrating data from a given version up to - * the current version listed in `package.json`. - * - * Note that, for any version converter, the corresponding `applyMigration` - * function would only need to be changed once when the first migration for a - * given version is introduced. Similarly, a version file (within the `versions` - * folder) would only need to be created for a version once with the appropriate - * array nomenclature. - */ -abstract class VersionConverter { - /** - * Iterates through an array of designated migration functions that are each - * called one by one to transform the data. - * @param data The data to be operated on - * @param migrationArr An array of functions that will transform the incoming data - */ - callMigrators(data: any, migrationArr: readonly any[]) { - for (const migrate of migrationArr) { - migrate(data); - } - } - - /** - * Applies any version-agnostic data sanitation as defined within the function - * body. - * @param data The data to be operated on - */ - applyStaticPreprocessors(_data: any): void {} - - /** - * Uses the current version the incoming data to determine the starting point - * of the migration which will cascade up to the latest version, calling the - * necessary migration functions in the process. - * @param data The data to be operated on - * @param curVersion [0] Current major version - * [1] Current minor version - * [2] Current patch version - */ - abstract applyMigration(data: any, curVersion: number[]): void; -} - -/** - * Class encapsulating the logic for migrating {@linkcode SessionSaveData} from - * a given version up to the current version listed in `package.json`. - * @extends VersionConverter - */ -class SessionVersionConverter extends VersionConverter { - override applyStaticPreprocessors(data: SessionSaveData): void { - // Always sanitize money as a safeguard - data.money = Math.floor(data.money); - } - - override applyMigration(data: SessionSaveData, curVersion: number[]): void { - const [curMajor, curMinor, curPatch] = curVersion; - - if (curMajor === 1) { - if (curMinor === 0) { - if (curPatch <= 5) { - console.log("Applying v1.0.4 session data migration!"); - this.callMigrators(data, v1_0_4.sessionMigrators); - } - } - if (curMinor <= 1) { - console.log("Applying v1.1.0 session data migration!"); - this.callMigrators(data, v1_1_0.sessionMigrators); - } - if (curMinor < 7) { - console.log("Applying v1.7.0 session data migration!"); - this.callMigrators(data, v1_7_0.sessionMigrators); - } - } - - console.log(`Session data successfully migrated to v${version}!`); - } -} - -/** - * Class encapsulating the logic for migrating {@linkcode SystemSaveData} from - * a given version up to the current version listed in `package.json`. - * @extends VersionConverter - */ -class SystemVersionConverter extends VersionConverter { - override applyMigration(data: SystemSaveData, curVersion: number[]): void { - const [curMajor, curMinor, curPatch] = curVersion; - - if (curMajor === 1) { - if (curMinor === 0) { - if (curPatch <= 4) { - console.log("Applying v1.0.4 system data migraton!"); - this.callMigrators(data, v1_0_4.systemMigrators); - } - } - if (curMinor <= 1) { - console.log("Applying v1.1.0 system data migraton!"); - this.callMigrators(data, v1_1_0.systemMigrators); - } - if (curMinor < 7) { - console.log("Applying v1.7.0 system data migration!"); - this.callMigrators(data, v1_7_0.systemMigrators); - } - if (curMinor === 8) { - if (curPatch <= 2) { - console.log("Applying v1.8.3 system data migration!"); - this.callMigrators(data, v1_8_3.systemMigrators); - } - } - } - - console.log(`System data successfully migrated to v${version}!`); - } -} - -/** - * Class encapsulating the logic for migrating settings data from - * a given version up to the current version listed in `package.json`. - * @extends VersionConverter - */ -class SettingsVersionConverter extends VersionConverter { - override applyMigration(data: Object, curVersion: number[]): void { - const [curMajor, curMinor, curPatch] = curVersion; - - if (curMajor === 1) { - if (curMinor === 0) { - if (curPatch <= 4) { - console.log("Applying v1.0.4 settings data migraton!"); - this.callMigrators(data, v1_0_4.settingsMigrators); - } - } - if (curMinor <= 1) { - console.log("Applying v1.1.0 settings data migraton!"); - this.callMigrators(data, v1_1_0.settingsMigrators); - } - if (curMinor < 7) { - console.log("Applying v1.7.0 settings data migration!"); - this.callMigrators(data, v1_7_0.settingsMigrators); - } - } - - console.log(`Settings data successfully migrated to v${version}!`); + if (isCurrentVersionHigher) { + applyMigrators(settingsMigrators, data, prevVersion); + console.log(`Settings successfully migrated to v${LATEST_VERSION}!`); } } diff --git a/src/system/version_migration/versions/v1_0_4.ts b/src/system/version_migration/versions/v1_0_4.ts index 16bd9db9915..2139352b783 100644 --- a/src/system/version_migration/versions/v1_0_4.ts +++ b/src/system/version_migration/versions/v1_0_4.ts @@ -4,15 +4,18 @@ import { AbilityAttr, defaultStarterSpecies, DexAttr } from "#app/system/game-da import { allSpecies } from "#app/data/pokemon-species"; import { CustomPokemonData } from "#app/data/custom-pokemon-data"; import { isNullOrUndefined } from "#app/utils"; +import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator"; +import type { SettingsSaveMigrator } from "#app/@types/SettingsSaveMigrator"; +import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator"; -export const systemMigrators = [ - /** - * Migrate ability starter data if empty for caught species. - * @param data {@linkcode SystemSaveData} - */ - function migrateAbilityData(data: SystemSaveData) { +/** + * Migrate ability starter data if empty for caught species. + * @param data - {@linkcode SystemSaveData} + */ +const migrateAbilityData: SystemSaveMigrator = { + version: "1.0.4", + migrate: (data: SystemSaveData): void => { if (data.starterData && data.dexData) { - // biome-ignore lint/complexity/noForEach: Object.keys(data.starterData).forEach(sd => { if (data.dexData[sd]?.caughtAttr && data.starterData[sd] && !data.starterData[sd].abilityAttr) { data.starterData[sd].abilityAttr = 1; @@ -20,12 +23,15 @@ export const systemMigrators = [ }); } }, +}; - /** - * Populate legendary Pokémon statistics if they are missing. - * @param data {@linkcode SystemSaveData} - */ - function fixLegendaryStats(data: SystemSaveData) { +/** + * Populate legendary Pokémon statistics if they are missing. + * @param data - {@linkcode SystemSaveData} + */ +const fixLegendaryStats: SystemSaveMigrator = { + version: "1.0.4", + migrate: (data: SystemSaveData): void => { if ( data.gameStats && data.gameStats.legendaryPokemonCaught !== undefined && @@ -34,7 +40,6 @@ export const systemMigrators = [ data.gameStats.subLegendaryPokemonSeen = 0; data.gameStats.subLegendaryPokemonCaught = 0; data.gameStats.subLegendaryPokemonHatched = 0; - // biome-ignore lint/complexity/noForEach: allSpecies .filter(s => s.subLegendary) .forEach(s => { @@ -66,12 +71,15 @@ export const systemMigrators = [ ); } }, +}; - /** - * Unlock all starters' first ability and female gender option. - * @param data {@linkcode SystemSaveData} - */ - function fixStarterData(data: SystemSaveData) { +/** + * Unlock all starters' first ability and female gender option. + * @param data - {@linkcode SystemSaveData} + */ +const fixStarterData: SystemSaveMigrator = { + version: "1.0.4", + migrate: (data: SystemSaveData): void => { if (!isNullOrUndefined(data.starterData)) { for (const starterId of defaultStarterSpecies) { if (data.starterData[starterId]?.abilityAttr) { @@ -83,17 +91,22 @@ export const systemMigrators = [ } } }, +}; + +export const systemMigrators: Readonly = [ + migrateAbilityData, + fixLegendaryStats, + fixStarterData, ] as const; -export const settingsMigrators = [ - /** - * Migrate from "REROLL_TARGET" property to {@linkcode - * SettingKeys.Shop_Cursor_Target}. - * @param data the `settings` object - */ - - // biome-ignore lint/complexity/noBannedTypes: TODO: fix the type to not be object... - function fixRerollTarget(data: Object) { +/** + * Migrate from `REROLL_TARGET` property to {@linkcode SettingKeys.Shop_Cursor_Target} + * @param data - The `settings` object + */ +const fixRerollTarget: SettingsSaveMigrator = { + version: "1.0.4", + // biome-ignore lint/complexity/noBannedTypes: TODO - refactor settings + migrate: (data: Object): void => { if (data.hasOwnProperty("REROLL_TARGET") && !data.hasOwnProperty(SettingKeys.Shop_Cursor_Target)) { data[SettingKeys.Shop_Cursor_Target] = data["REROLL_TARGET"]; // biome-ignore lint/performance/noDelete: intentional @@ -101,16 +114,20 @@ export const settingsMigrators = [ localStorage.setItem("settings", JSON.stringify(data)); } }, -] as const; +}; -export const sessionMigrators = [ - /** - * Converts old lapsing modifiers (battle items, lures, and Dire Hit) and - * other miscellaneous modifiers (vitamins, White Herb) to any new class - * names and/or change in reload arguments. - * @param data {@linkcode SessionSaveData} - */ - function migrateModifiers(data: SessionSaveData) { +export const settingsMigrators: Readonly = [fixRerollTarget] as const; + +/** + * Converts old lapsing modifiers (battle items, lures, and Dire Hit) and + * other miscellaneous modifiers (vitamins, White Herb) to any new class + * names and/or change in reload arguments. + * @param data - {@linkcode SessionSaveData} + */ +const migrateModifiers: SessionSaveMigrator = { + version: "1.0.4", + // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: necessary? + migrate: (data: SessionSaveData): void => { for (const m of data.modifiers) { if (m.className === "PokemonBaseStatModifier") { m.className = "BaseStatModifier"; @@ -163,12 +180,11 @@ export const sessionMigrators = [ } } }, - /** - * Converts old Pokemon natureOverride and mysteryEncounterData - * to use the new conjoined {@linkcode Pokemon.customPokemonData} structure instead. - * @param data {@linkcode SessionSaveData} - */ - function migrateCustomPokemonDataAndNatureOverrides(data: SessionSaveData) { +}; + +const migrateCustomPokemonData: SessionSaveMigrator = { + version: "1.0.4", + migrate: (data: SessionSaveData): void => { // Fix Pokemon nature overrides and custom data migration for (const pokemon of data.party) { if (pokemon["mysteryEncounterPokemonData"]) { @@ -186,4 +202,6 @@ export const sessionMigrators = [ } } }, -] as const; +}; + +export const sessionMigrators: Readonly = [migrateModifiers, migrateCustomPokemonData] as const; diff --git a/src/system/version_migration/versions/v1_1_0.ts b/src/system/version_migration/versions/v1_1_0.ts deleted file mode 100644 index 5d6247aeaa2..00000000000 --- a/src/system/version_migration/versions/v1_1_0.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const systemMigrators = [] as const; - -export const settingsMigrators = [] as const; - -export const sessionMigrators = [] as const; diff --git a/src/system/version_migration/versions/v1_7_0.ts b/src/system/version_migration/versions/v1_7_0.ts index 167cd974e56..a1213ccf64c 100644 --- a/src/system/version_migration/versions/v1_7_0.ts +++ b/src/system/version_migration/versions/v1_7_0.ts @@ -1,15 +1,18 @@ +import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator"; +import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator"; import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; import { globalScene } from "#app/global-scene"; import { DexAttr, type SessionSaveData, type SystemSaveData } from "#app/system/game-data"; -import * as Utils from "#app/utils"; +import { isNullOrUndefined } from "#app/utils"; -export const systemMigrators = [ - /** - * If a starter is caught, but the only forms registered as caught are not starterSelectable, - * unlock the default form. - * @param data {@linkcode SystemSaveData} - */ - function migrateUnselectableForms(data: SystemSaveData) { +/** + * If a starter is caught, but the only forms registered as caught are not starterSelectable, + * unlock the default form. + * @param data - {@linkcode SystemSaveData} + */ +const migrateUnselectableForms: SystemSaveMigrator = { + version: "1.7.0", + migrate: (data: SystemSaveData): void => { if (data.starterData && data.dexData) { Object.keys(data.starterData).forEach(sd => { const caughtAttr = data.dexData[sd]?.caughtAttr; @@ -30,12 +33,13 @@ export const systemMigrators = [ }); } }, -] as const; +}; -export const settingsMigrators = [] as const; +export const systemMigrators: Readonly = [migrateUnselectableForms] as const; -export const sessionMigrators = [ - function migrateTera(data: SessionSaveData) { +const migrateTera: SessionSaveMigrator = { + version: "1.7.0", + migrate: (data: SessionSaveData): void => { for (let i = 0; i < data.modifiers.length; ) { if (data.modifiers[i].className === "TerastallizeModifier") { data.party.forEach(p => { @@ -63,15 +67,17 @@ export const sessionMigrators = [ } data.party.forEach(p => { - if (Utils.isNullOrUndefined(p.teraType)) { + if (isNullOrUndefined(p.teraType)) { p.teraType = getPokemonSpeciesForm(p.species, p.formIndex).type1; } }); data.enemyParty.forEach(p => { - if (Utils.isNullOrUndefined(p.teraType)) { + if (isNullOrUndefined(p.teraType)) { p.teraType = getPokemonSpeciesForm(p.species, p.formIndex).type1; } }); }, -] as const; +}; + +export const sessionMigrators: Readonly = [migrateTera] as const; diff --git a/src/system/version_migration/versions/v1_8_3.ts b/src/system/version_migration/versions/v1_8_3.ts index d35530c28e9..6e2d96d3673 100644 --- a/src/system/version_migration/versions/v1_8_3.ts +++ b/src/system/version_migration/versions/v1_8_3.ts @@ -1,14 +1,16 @@ +import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { DexAttr, type SystemSaveData } from "#app/system/game-data"; import { Species } from "#enums/species"; -export const systemMigrators = [ - /** - * If a starter is caught, but the only forms registered as caught are not starterSelectable, - * unlock the default form. - * @param data {@linkcode SystemSaveData} - */ - function migratePichuForms(data: SystemSaveData) { +/** + * If a starter is caught, but the only forms registered as caught are not starterSelectable, + * unlock the default form. + * @param data - {@linkcode SystemSaveData} + */ +const migratePichuForms: SystemSaveMigrator = { + version: "1.8.3", + migrate: (data: SystemSaveData): void => { if (data.starterData && data.dexData) { // This is Pichu's Pokédex number const sd = 172; @@ -23,8 +25,6 @@ export const systemMigrators = [ } } }, -] as const; +}; -export const settingsMigrators = [] as const; - -export const sessionMigrators = [] as const; +export const systemMigrators: Readonly = [migratePichuForms] as const; From cb5deb408ff75310cf4fca403099d3b57f4c376d Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sun, 6 Apr 2025 21:25:20 -0700 Subject: [PATCH 08/52] [Refactor] Delete stale pokemon objects at the end of a battle Co-authored-by: Frutescens --- src/phases/battle-end-phase.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index ff17b17ab8b..dea575a71b3 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -73,6 +73,11 @@ export class BattleEndPhase extends BattlePhase { } globalScene.clearEnemyHeldItemModifiers(); + try { + globalScene.getEnemyParty().forEach(p => p.destroy()); + } catch { + console.warn("Unable to destroy stale pokemon objects in BattleEndPhase."); + } const lapsingModifiers = globalScene.findModifiers( m => m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier, From 17a56cc6c1d2dfebfd6fbb0045fb742b421ccb7b Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Mon, 7 Apr 2025 15:57:50 -0700 Subject: [PATCH 09/52] Move `try/catch` inside `for` loop Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> --- src/phases/battle-end-phase.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index dea575a71b3..645b04aea46 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -73,10 +73,12 @@ export class BattleEndPhase extends BattlePhase { } globalScene.clearEnemyHeldItemModifiers(); - try { - globalScene.getEnemyParty().forEach(p => p.destroy()); - } catch { - console.warn("Unable to destroy stale pokemon objects in BattleEndPhase."); + for (const p of globalScene.getEnemyParty()) { + try { + p.destroy(); + } catch { + console.warn("Unable to destroy stale pokemon objects in BattleEndPhase."); + } } const lapsingModifiers = globalScene.findModifiers( From 1171656d12b6333bfda5ff9c10b55d393d21898f Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Mon, 7 Apr 2025 16:02:58 -0700 Subject: [PATCH 10/52] Update console message --- src/phases/battle-end-phase.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index 645b04aea46..0d831c65b52 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -77,7 +77,7 @@ export class BattleEndPhase extends BattlePhase { try { p.destroy(); } catch { - console.warn("Unable to destroy stale pokemon objects in BattleEndPhase."); + console.warn("Unable to destroy stale pokemon object in BattleEndPhase:", p); } } From 31835e6d534b0a399df01222633f3d5ba8eaa81d Mon Sep 17 00:00:00 2001 From: Dean <69436131+emdeann@users.noreply.github.com> Date: Mon, 7 Apr 2025 16:32:10 -0700 Subject: [PATCH 11/52] [Bug] Fix #4972 Status-Prevention Abilities do not Cure Status (#5406) * Add PostSummonHealAbAttr and give it to appropriate abilities * Add attr to insomnia * Remove attr from leaf guard (it does not activate on gain with sun up) * Add tests and remove attr from shields down * Add PostSummonRemoveBattlerTag and give it to oblivious and own tempo * Add tests for oblivious and own tempo * Fix oblivious test sometimes failing * Remove Comatose changes as it doesn't reapply * Remove unused tagRemoved field * Fix tests checking status instead of tag * Fix attr comments * Add PostSetStatusHealStatusAbAttr * Add ResetStatusPhase * Modify pokemon.resetStatus to use ResetStatusPhase * Move post status effects to ObtainStatusEffectPhase * Ensure status overriding (ie rest) works properly * Add PostApplyBattlerTagRemoveTagAbAttr for own tempo and oblivious * Guard removeTag call in PostApplyBattlerTagRemoveTagAbAttr * Commenting * Handle Mold Breaker case in MoveEndPhase * Remove PostSummonHealStatusAbAttr from purifying salt * Fix not passing overrideStatus to canSetStatus * Apply suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Add isNullOrUndefined import * Add canApply to new attrs * Add followup argument back * Remove guard around new MoveEndPhase --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/ability.ts | 78 ++++++++++++++++++++++++ src/data/moves/move.ts | 12 ++-- src/field/pokemon.ts | 38 +++--------- src/phases/move-end-phase.ts | 17 +++++- src/phases/move-phase.ts | 10 ++- src/phases/obtain-status-effect-phase.ts | 9 +++ src/phases/reset-status-phase.ts | 44 +++++++++++++ test/abilities/immunity.test.ts | 51 ++++++++++++++++ test/abilities/insomnia.test.ts | 51 ++++++++++++++++ test/abilities/limber.test.ts | 51 ++++++++++++++++ test/abilities/magma_armor.test.ts | 51 ++++++++++++++++ test/abilities/oblivious.test.ts | 69 +++++++++++++++++++++ test/abilities/own_tempo.test.ts | 51 ++++++++++++++++ test/abilities/thermal_exchange.test.ts | 51 ++++++++++++++++ test/abilities/vital_spirit.test.ts | 51 ++++++++++++++++ test/abilities/water_bubble.test.ts | 51 ++++++++++++++++ test/abilities/water_veil.test.ts | 51 ++++++++++++++++ 17 files changed, 693 insertions(+), 43 deletions(-) create mode 100644 src/phases/reset-status-phase.ts create mode 100644 test/abilities/immunity.test.ts create mode 100644 test/abilities/insomnia.test.ts create mode 100644 test/abilities/limber.test.ts create mode 100644 test/abilities/magma_armor.test.ts create mode 100644 test/abilities/oblivious.test.ts create mode 100644 test/abilities/own_tempo.test.ts create mode 100644 test/abilities/thermal_exchange.test.ts create mode 100644 test/abilities/vital_spirit.test.ts create mode 100644 test/abilities/water_bubble.test.ts create mode 100644 test/abilities/water_veil.test.ts diff --git a/src/data/ability.ts b/src/data/ability.ts index a7107ce2e9d..f8c9b4cb8fe 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2295,6 +2295,11 @@ export class PostSummonAbAttr extends AbAttr { applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {} } +/** + * Base class for ability attributes which remove an effect on summon + */ +export class PostSummonRemoveEffectAbAttr extends PostSummonAbAttr {} + /** * Removes specified arena tags when a Pokemon is summoned. */ @@ -2405,6 +2410,31 @@ export class PostSummonAddBattlerTagAbAttr extends PostSummonAbAttr { } } +/** + * Removes Specific battler tags when a Pokemon is summoned + * + * This should realistically only ever activate on gain rather than on summon + */ +export class PostSummonRemoveBattlerTagAbAttr extends PostSummonRemoveEffectAbAttr { + private immuneTags: BattlerTagType[]; + + /** + * @param immuneTags - The {@linkcode BattlerTagType | battler tags} the Pokémon is immune to. + */ + constructor(...immuneTags: BattlerTagType[]) { + super(); + this.immuneTags = immuneTags; + } + + 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 { + this.immuneTags.forEach(tagType => pokemon.removeTag(tagType)); + } +} + export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { private stats: BattleStat[]; private stages: number; @@ -2592,6 +2622,43 @@ export class PostSummonTerrainChangeAbAttr extends PostSummonAbAttr { } } +/** + * Heals a status effect if the Pokemon is afflicted with it upon switch in (or gain) + */ +export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr { + private immuneEffects: StatusEffect[]; + private statusHealed: StatusEffect; + + /** + * @param immuneEffects - The {@linkcode StatusEffect}s the Pokémon is immune to. + */ + constructor(...immuneEffects: StatusEffect[]) { + super(); + this.immuneEffects = immuneEffects; + } + + public override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + const status = pokemon.status?.effect; + return !Utils.isNullOrUndefined(status) && (this.immuneEffects.length < 1 || this.immuneEffects.includes(status)) + } + + public override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { + const status = pokemon.status?.effect; + if (!Utils.isNullOrUndefined(status)) { + this.statusHealed = status; + pokemon.resetStatus(false); + pokemon.updateInfo(); + } + } + + public override getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null { + if (this.statusHealed) { + return getStatusEffectHealText(this.statusHealed, getPokemonNameWithAffix(_pokemon)); + } + return null; + } +} + export class PostSummonFormChangeAbAttr extends PostSummonAbAttr { private formFunc: (p: Pokemon) => number; @@ -6291,6 +6358,7 @@ export function initAbilities() { .ignorable(), new Ability(Abilities.LIMBER, 3) .attr(StatusEffectImmunityAbAttr, StatusEffect.PARALYSIS) + .attr(PostSummonHealStatusAbAttr, StatusEffect.PARALYSIS) .ignorable(), new Ability(Abilities.SAND_VEIL, 3) .attr(StatMultiplierAbAttr, Stat.EVA, 1.2) @@ -6308,6 +6376,7 @@ export function initAbilities() { .ignorable(), new Ability(Abilities.OBLIVIOUS, 3) .attr(BattlerTagImmunityAbAttr, [ BattlerTagType.INFATUATED, BattlerTagType.TAUNT ]) + .attr(PostSummonRemoveBattlerTagAbAttr, BattlerTagType.INFATUATED, BattlerTagType.TAUNT) .attr(IntimidateImmunityAbAttr) .ignorable(), new Ability(Abilities.CLOUD_NINE, 3) @@ -6320,6 +6389,7 @@ export function initAbilities() { .attr(StatMultiplierAbAttr, Stat.ACC, 1.3), new Ability(Abilities.INSOMNIA, 3) .attr(StatusEffectImmunityAbAttr, StatusEffect.SLEEP) + .attr(PostSummonHealStatusAbAttr, StatusEffect.SLEEP) .attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY) .ignorable(), new Ability(Abilities.COLOR_CHANGE, 3) @@ -6327,6 +6397,7 @@ export function initAbilities() { .condition(getSheerForceHitDisableAbCondition()), new Ability(Abilities.IMMUNITY, 3) .attr(StatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) + .attr(PostSummonHealStatusAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .ignorable(), new Ability(Abilities.FLASH_FIRE, 3) .attr(TypeImmunityAddBattlerTagAbAttr, PokemonType.FIRE, BattlerTagType.FIRE_BOOST, 1) @@ -6336,6 +6407,7 @@ export function initAbilities() { .ignorable(), new Ability(Abilities.OWN_TEMPO, 3) .attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED) + .attr(PostSummonRemoveBattlerTagAbAttr, BattlerTagType.CONFUSED) .attr(IntimidateImmunityAbAttr) .ignorable(), new Ability(Abilities.SUCTION_CUPS, 3) @@ -6401,9 +6473,11 @@ export function initAbilities() { .ignorable(), new Ability(Abilities.MAGMA_ARMOR, 3) .attr(StatusEffectImmunityAbAttr, StatusEffect.FREEZE) + .attr(PostSummonHealStatusAbAttr, StatusEffect.FREEZE) .ignorable(), new Ability(Abilities.WATER_VEIL, 3) .attr(StatusEffectImmunityAbAttr, StatusEffect.BURN) + .attr(PostSummonHealStatusAbAttr, StatusEffect.BURN) .ignorable(), new Ability(Abilities.MAGNET_PULL, 3) .attr(ArenaTrapAbAttr, (user, target) => { @@ -6497,6 +6571,7 @@ export function initAbilities() { .attr(DoubleBattleChanceAbAttr), new Ability(Abilities.VITAL_SPIRIT, 3) .attr(StatusEffectImmunityAbAttr, StatusEffect.SLEEP) + .attr(PostSummonHealStatusAbAttr, StatusEffect.SLEEP) .attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY) .ignorable(), new Ability(Abilities.WHITE_SMOKE, 3) @@ -6835,6 +6910,7 @@ export function initAbilities() { .attr(MoveTypeChangeAbAttr, PokemonType.ICE, 1.2, (user, target, move) => move.type === PokemonType.NORMAL && !move.hasAttr(VariableMoveTypeAttr)), new Ability(Abilities.SWEET_VEIL, 6) .attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.SLEEP) + .attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.SLEEP) .attr(UserFieldBattlerTagImmunityAbAttr, BattlerTagType.DROWSY) .ignorable() .partial(), // Mold Breaker ally should not be affected by Sweet Veil @@ -6919,6 +6995,7 @@ export function initAbilities() { .attr(ReceivedTypeDamageMultiplierAbAttr, PokemonType.FIRE, 0.5) .attr(MoveTypePowerBoostAbAttr, PokemonType.WATER, 2) .attr(StatusEffectImmunityAbAttr, StatusEffect.BURN) + .attr(PostSummonHealStatusAbAttr, StatusEffect.BURN) .ignorable(), new Ability(Abilities.STEELWORKER, 7) .attr(MoveTypePowerBoostAbAttr, PokemonType.STEEL), @@ -7197,6 +7274,7 @@ export function initAbilities() { new Ability(Abilities.THERMAL_EXCHANGE, 9) .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(Abilities.ANGER_SHELL, 9) .attr(PostDefendHpGatedStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, 0.5, [ Stat.ATK, Stat.SPATK, Stat.SPD ], 1) diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 7a820d984d0..1af4be4fdf0 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -2443,12 +2443,8 @@ export class StatusEffectAttr extends MoveEffectAttr { const statusCheck = moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance; if (statusCheck) { const pokemon = this.selfTarget ? user : target; - if (pokemon.status) { - if (this.overrideStatus) { - pokemon.resetStatus(); - } else { - return false; - } + if (pokemon.status && !this.overrideStatus) { + return false; } if (user !== target && target.isSafeguarded(user)) { @@ -2457,8 +2453,8 @@ export class StatusEffectAttr extends MoveEffectAttr { } return false; } - if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0)) - && pokemon.trySetStatus(this.effect, true, user, this.turnsRemaining)) { + if (((!pokemon.status || this.overrideStatus) || (pokemon.status.effect === this.effect && moveChance < 0)) + && pokemon.trySetStatus(this.effect, true, user, this.turnsRemaining, null, this.overrideStatus)) { applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, false, this.effect); return true; } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index f3e758e4efd..7d856696188 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -264,6 +264,7 @@ import { StatusEffect } from "#enums/status-effect"; import { doShinySparkleAnim } from "#app/field/anims"; import { MoveFlags } from "#enums/MoveFlags"; import { timedEventManager } from "#app/global-event-manager"; +import { ResetStatusPhase } from "#app/phases/reset-status-phase"; export enum LearnMoveSituation { MISC, @@ -4809,7 +4810,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (newTag.canAdd(this)) { this.summonData.tags.push(newTag); newTag.onAdd(this); - return true; } @@ -5480,8 +5480,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { sourcePokemon: Pokemon | null = null, turnsRemaining = 0, sourceText: string | null = null, + overrideStatus?: boolean ): boolean { - if (!this.canSetStatus(effect, asPhase, false, sourcePokemon)) { + if (!this.canSetStatus(effect, asPhase, overrideStatus, sourcePokemon)) { return false; } if (this.isFainted() && effect !== StatusEffect.FAINT) { @@ -5497,6 +5498,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (asPhase) { + if (overrideStatus) { + this.resetStatus(false); + } globalScene.unshiftPhase( new ObtainStatusEffectPhase( this.getBattlerIndex(), @@ -5536,20 +5540,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { effect = effect!; // If `effect` is undefined then `trySetStatus()` will have already returned early via the `canSetStatus()` call this.status = new Status(effect, 0, sleepTurnsRemaining?.value); - if (effect !== StatusEffect.FAINT) { - globalScene.triggerPokemonFormChange( - this, - SpeciesFormChangeStatusEffectTrigger, - true, - ); - applyPostSetStatusAbAttrs( - PostSetStatusAbAttr, - this, - effect, - sourcePokemon, - ); - } - return true; } @@ -5564,21 +5554,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!revive && lastStatus === StatusEffect.FAINT) { return; } - this.status = null; - if (lastStatus === StatusEffect.SLEEP) { - this.setFrameRate(10); - if (this.getTag(BattlerTagType.NIGHTMARE)) { - this.lapseTag(BattlerTagType.NIGHTMARE); - } - } - if (confusion) { - if (this.getTag(BattlerTagType.CONFUSED)) { - this.lapseTag(BattlerTagType.CONFUSED); - } - } - if (reloadAssets) { - this.loadAssets(false).then(() => this.playAnim()); - } + globalScene.unshiftPhase(new ResetStatusPhase(this, confusion, reloadAssets)); } /** diff --git a/src/phases/move-end-phase.ts b/src/phases/move-end-phase.ts index 53856956401..176abee5e98 100644 --- a/src/phases/move-end-phase.ts +++ b/src/phases/move-end-phase.ts @@ -2,11 +2,18 @@ import { globalScene } from "#app/global-scene"; import { BattlerTagLapseType } from "#app/data/battler-tags"; import { PokemonPhase } from "./pokemon-phase"; import type { BattlerIndex } from "#app/battle"; +import { applyPostSummonAbAttrs, PostSummonRemoveEffectAbAttr } from "#app/data/ability"; +import type Pokemon from "#app/field/pokemon"; export class MoveEndPhase extends PokemonPhase { private wasFollowUp: boolean; - constructor(battlerIndex: BattlerIndex, wasFollowUp = false) { + + /** Targets from the preceding MovePhase */ + private targets: Pokemon[]; + constructor(battlerIndex: BattlerIndex, targets: Pokemon[], wasFollowUp = false) { super(battlerIndex); + + this.targets = targets; this.wasFollowUp = wasFollowUp; } @@ -17,9 +24,15 @@ export class MoveEndPhase extends PokemonPhase { if (!this.wasFollowUp && pokemon?.isActive(true)) { pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE); } - globalScene.arena.setIgnoreAbilities(false); + // Remove effects which were set on a Pokemon which removes them on summon (i.e. via Mold Breaker) + for (const target of this.targets) { + if (target) { + applyPostSummonAbAttrs(PostSummonRemoveEffectAbAttr, target); + } + } + this.end(); } } diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 5232dfee8ba..478229dcae8 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -169,7 +169,11 @@ export class MovePhase extends BattlePhase { // Check move to see if arena.ignoreAbilities should be true. if (!this.followUp || this.reflected) { - if (this.move.getMove().doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user: this.pokemon, isFollowUp: this.followUp })) { + if ( + this.move + .getMove() + .doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user: this.pokemon, isFollowUp: this.followUp }) + ) { globalScene.arena.setIgnoreAbilities(true, this.pokemon.getBattlerIndex()); } } @@ -473,7 +477,9 @@ export class MovePhase extends BattlePhase { * Queues a {@linkcode MoveEndPhase} and then ends the phase */ public end(): void { - globalScene.unshiftPhase(new MoveEndPhase(this.pokemon.getBattlerIndex(), this.followUp)); + globalScene.unshiftPhase( + new MoveEndPhase(this.pokemon.getBattlerIndex(), this.getActiveTargetPokemon(), this.followUp), + ); super.end(); } diff --git a/src/phases/obtain-status-effect-phase.ts b/src/phases/obtain-status-effect-phase.ts index a0c0c14e93f..cba9399b996 100644 --- a/src/phases/obtain-status-effect-phase.ts +++ b/src/phases/obtain-status-effect-phase.ts @@ -6,6 +6,9 @@ import { StatusEffect } from "#app/enums/status-effect"; import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { PokemonPhase } from "./pokemon-phase"; +import { SpeciesFormChangeStatusEffectTrigger } from "#app/data/pokemon-forms"; +import { applyPostSetStatusAbAttrs, PostSetStatusAbAttr } from "#app/data/ability"; +import { isNullOrUndefined } from "#app/utils"; export class ObtainStatusEffectPhase extends PokemonPhase { private statusEffect?: StatusEffect; @@ -44,6 +47,12 @@ export class ObtainStatusEffectPhase extends PokemonPhase { this.sourceText ?? undefined, ), ); + if (!isNullOrUndefined(this.statusEffect) && this.statusEffect !== StatusEffect.FAINT) { + globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeStatusEffectTrigger, true); + // If mold breaker etc was used to set this status, it shouldn't apply to abilities activated afterwards + globalScene.arena.setIgnoreAbilities(false); + applyPostSetStatusAbAttrs(PostSetStatusAbAttr, pokemon, this.statusEffect, this.sourcePokemon); + } this.end(); }); return; diff --git a/src/phases/reset-status-phase.ts b/src/phases/reset-status-phase.ts new file mode 100644 index 00000000000..0ba3559d9b7 --- /dev/null +++ b/src/phases/reset-status-phase.ts @@ -0,0 +1,44 @@ +import type Pokemon from "#app/field/pokemon"; +import { BattlePhase } from "#app/phases/battle-phase"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { StatusEffect } from "#enums/status-effect"; + +/** + * Phase which handles resetting a Pokemon's status to none + * + * 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 { + private readonly pokemon: Pokemon; + private readonly affectConfusion: boolean; + private readonly reloadAssets: boolean; + + constructor(pokemon: Pokemon, affectConfusion: boolean, reloadAssets: boolean) { + super(); + + this.pokemon = pokemon; + this.affectConfusion = affectConfusion; + this.reloadAssets = reloadAssets; + } + + public override start() { + const lastStatus = this.pokemon.status?.effect; + this.pokemon.status = null; + if (lastStatus === StatusEffect.SLEEP) { + this.pokemon.setFrameRate(10); + if (this.pokemon.getTag(BattlerTagType.NIGHTMARE)) { + this.pokemon.lapseTag(BattlerTagType.NIGHTMARE); + } + } + if (this.affectConfusion) { + if (this.pokemon.getTag(BattlerTagType.CONFUSED)) { + this.pokemon.lapseTag(BattlerTagType.CONFUSED); + } + } + if (this.reloadAssets) { + this.pokemon.loadAssets(false).then(() => this.pokemon.playAnim()); + } + this.pokemon.updateInfo(true); + this.end(); + } +} diff --git a/test/abilities/immunity.test.ts b/test/abilities/immunity.test.ts new file mode 100644 index 00000000000..51e9598720b --- /dev/null +++ b/test/abilities/immunity.test.ts @@ -0,0 +1,51 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { StatusEffect } from "#enums/status-effect"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Immunity", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should remove poison when gained", async () => { + game.override.ability(Abilities.IMMUNITY) + .enemyAbility(Abilities.BALL_FETCH) + .moveset(Moves.SKILL_SWAP) + .enemyMoveset(Moves.SPLASH), + + await game.classicMode.startBattle([ Species.FEEBAS ]); + const enemy = game.scene.getEnemyPokemon(); + enemy?.trySetStatus(StatusEffect.POISON); + expect(enemy?.status?.effect).toBe(StatusEffect.POISON); + + game.move.select(Moves.SKILL_SWAP); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy?.status).toBeNull(); + }); +}); diff --git a/test/abilities/insomnia.test.ts b/test/abilities/insomnia.test.ts new file mode 100644 index 00000000000..91fdc3fc668 --- /dev/null +++ b/test/abilities/insomnia.test.ts @@ -0,0 +1,51 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { StatusEffect } from "#enums/status-effect"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Insomnia", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should remove sleep when gained", async () => { + game.override.ability(Abilities.INSOMNIA) + .enemyAbility(Abilities.BALL_FETCH) + .moveset(Moves.SKILL_SWAP) + .enemyMoveset(Moves.SPLASH), + + await game.classicMode.startBattle([ Species.FEEBAS ]); + const enemy = game.scene.getEnemyPokemon(); + enemy?.trySetStatus(StatusEffect.SLEEP); + expect(enemy?.status?.effect).toBe(StatusEffect.SLEEP); + + game.move.select(Moves.SKILL_SWAP); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy?.status).toBeNull(); + }); +}); diff --git a/test/abilities/limber.test.ts b/test/abilities/limber.test.ts new file mode 100644 index 00000000000..2b167cc155f --- /dev/null +++ b/test/abilities/limber.test.ts @@ -0,0 +1,51 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { StatusEffect } from "#enums/status-effect"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Limber", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should remove paralysis when gained", async () => { + game.override.ability(Abilities.LIMBER) + .enemyAbility(Abilities.BALL_FETCH) + .moveset(Moves.SKILL_SWAP) + .enemyMoveset(Moves.SPLASH), + + await game.classicMode.startBattle([ Species.FEEBAS ]); + const enemy = game.scene.getEnemyPokemon(); + enemy?.trySetStatus(StatusEffect.PARALYSIS); + expect(enemy?.status?.effect).toBe(StatusEffect.PARALYSIS); + + game.move.select(Moves.SKILL_SWAP); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy?.status).toBeNull(); + }); +}); diff --git a/test/abilities/magma_armor.test.ts b/test/abilities/magma_armor.test.ts new file mode 100644 index 00000000000..b1d62f948d2 --- /dev/null +++ b/test/abilities/magma_armor.test.ts @@ -0,0 +1,51 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { StatusEffect } from "#enums/status-effect"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Magma Armor", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should remove freeze when gained", async () => { + game.override.ability(Abilities.MAGMA_ARMOR) + .enemyAbility(Abilities.BALL_FETCH) + .moveset(Moves.SKILL_SWAP) + .enemyMoveset(Moves.SPLASH), + + await game.classicMode.startBattle([ Species.FEEBAS ]); + const enemy = game.scene.getEnemyPokemon(); + enemy?.trySetStatus(StatusEffect.FREEZE); + expect(enemy?.status?.effect).toBe(StatusEffect.FREEZE); + + game.move.select(Moves.SKILL_SWAP); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy?.status).toBeNull(); + }); +}); diff --git a/test/abilities/oblivious.test.ts b/test/abilities/oblivious.test.ts new file mode 100644 index 00000000000..d5089ef6a72 --- /dev/null +++ b/test/abilities/oblivious.test.ts @@ -0,0 +1,69 @@ +import { Abilities } from "#enums/abilities"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Abilities - Oblivious", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should remove taunt when gained", async () => { + game.override.ability(Abilities.OBLIVIOUS) + .enemyAbility(Abilities.BALL_FETCH) + .moveset(Moves.SKILL_SWAP) + .enemyMoveset(Moves.SPLASH), + + await game.classicMode.startBattle([ Species.FEEBAS ]); + const enemy = game.scene.getEnemyPokemon(); + enemy?.addTag(BattlerTagType.TAUNT); + expect(enemy?.getTag(BattlerTagType.TAUNT)).toBeTruthy(); + + game.move.select(Moves.SKILL_SWAP); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy?.getTag(BattlerTagType.TAUNT)).toBeFalsy(); + }); + + it("should remove infatuation when gained", async () => { + game.override.ability(Abilities.OBLIVIOUS) + .enemyAbility(Abilities.BALL_FETCH) + .moveset(Moves.SKILL_SWAP) + .enemyMoveset(Moves.SPLASH), + + await game.classicMode.startBattle([ Species.FEEBAS ]); + const enemy = game.scene.getEnemyPokemon(); + vi.spyOn(enemy!, "isOppositeGender").mockReturnValue(true); + enemy?.addTag(BattlerTagType.INFATUATED, 5, Moves.JUDGMENT, game.scene.getPlayerPokemon()?.id); // sourceID needs to be defined + expect(enemy?.getTag(BattlerTagType.INFATUATED)).toBeTruthy(); + + game.move.select(Moves.SKILL_SWAP); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy?.getTag(BattlerTagType.INFATUATED)).toBeFalsy(); + }); +}); diff --git a/test/abilities/own_tempo.test.ts b/test/abilities/own_tempo.test.ts new file mode 100644 index 00000000000..936b4311b20 --- /dev/null +++ b/test/abilities/own_tempo.test.ts @@ -0,0 +1,51 @@ +import { Abilities } from "#enums/abilities"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Own Tempo", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should remove confusion when gained", async () => { + game.override.ability(Abilities.OWN_TEMPO) + .enemyAbility(Abilities.BALL_FETCH) + .moveset(Moves.SKILL_SWAP) + .enemyMoveset(Moves.SPLASH), + + await game.classicMode.startBattle([ Species.FEEBAS ]); + const enemy = game.scene.getEnemyPokemon(); + enemy?.addTag(BattlerTagType.CONFUSED); + expect(enemy?.getTag(BattlerTagType.CONFUSED)).toBeTruthy(); + + game.move.select(Moves.SKILL_SWAP); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy?.getTag(BattlerTagType.CONFUSED)).toBeFalsy(); + }); +}); diff --git a/test/abilities/thermal_exchange.test.ts b/test/abilities/thermal_exchange.test.ts new file mode 100644 index 00000000000..124c1dba286 --- /dev/null +++ b/test/abilities/thermal_exchange.test.ts @@ -0,0 +1,51 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { StatusEffect } from "#enums/status-effect"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Thermal Exchange", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should remove burn when gained", async () => { + game.override.ability(Abilities.THERMAL_EXCHANGE) + .enemyAbility(Abilities.BALL_FETCH) + .moveset(Moves.SKILL_SWAP) + .enemyMoveset(Moves.SPLASH), + + await game.classicMode.startBattle([ Species.FEEBAS ]); + const enemy = game.scene.getEnemyPokemon(); + enemy?.trySetStatus(StatusEffect.BURN); + expect(enemy?.status?.effect).toBe(StatusEffect.BURN); + + game.move.select(Moves.SKILL_SWAP); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy?.status).toBeNull(); + }); +}); diff --git a/test/abilities/vital_spirit.test.ts b/test/abilities/vital_spirit.test.ts new file mode 100644 index 00000000000..3a53c3f520e --- /dev/null +++ b/test/abilities/vital_spirit.test.ts @@ -0,0 +1,51 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { StatusEffect } from "#enums/status-effect"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Vital Spirit", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should remove sleep when gained", async () => { + game.override.ability(Abilities.INSOMNIA) + .enemyAbility(Abilities.BALL_FETCH) + .moveset(Moves.SKILL_SWAP) + .enemyMoveset(Moves.SPLASH), + + await game.classicMode.startBattle([ Species.FEEBAS ]); + const enemy = game.scene.getEnemyPokemon(); + enemy?.trySetStatus(StatusEffect.SLEEP); + expect(enemy?.status?.effect).toBe(StatusEffect.SLEEP); + + game.move.select(Moves.SKILL_SWAP); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy?.status).toBeNull(); + }); +}); diff --git a/test/abilities/water_bubble.test.ts b/test/abilities/water_bubble.test.ts new file mode 100644 index 00000000000..0b85a5814da --- /dev/null +++ b/test/abilities/water_bubble.test.ts @@ -0,0 +1,51 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { StatusEffect } from "#enums/status-effect"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Water Bubble", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should remove burn when gained", async () => { + game.override.ability(Abilities.THERMAL_EXCHANGE) + .enemyAbility(Abilities.BALL_FETCH) + .moveset(Moves.SKILL_SWAP) + .enemyMoveset(Moves.SPLASH), + + await game.classicMode.startBattle([ Species.FEEBAS ]); + const enemy = game.scene.getEnemyPokemon(); + enemy?.trySetStatus(StatusEffect.BURN); + expect(enemy?.status?.effect).toBe(StatusEffect.BURN); + + game.move.select(Moves.SKILL_SWAP); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy?.status).toBeNull(); + }); +}); diff --git a/test/abilities/water_veil.test.ts b/test/abilities/water_veil.test.ts new file mode 100644 index 00000000000..38c9a05600b --- /dev/null +++ b/test/abilities/water_veil.test.ts @@ -0,0 +1,51 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { StatusEffect } from "#enums/status-effect"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Water Veil", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should remove burn when gained", async () => { + game.override.ability(Abilities.THERMAL_EXCHANGE) + .enemyAbility(Abilities.BALL_FETCH) + .moveset(Moves.SKILL_SWAP) + .enemyMoveset(Moves.SPLASH), + + await game.classicMode.startBattle([ Species.FEEBAS ]); + const enemy = game.scene.getEnemyPokemon(); + enemy?.trySetStatus(StatusEffect.BURN); + expect(enemy?.status?.effect).toBe(StatusEffect.BURN); + + game.move.select(Moves.SKILL_SWAP); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy?.status).toBeNull(); + }); +}); From 787feceb14f0cd635ca833e94a877b6fe379e50a Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Wed, 9 Apr 2025 10:43:05 -0500 Subject: [PATCH 12/52] [Refactor] Refactor variant sprite code part 1 (#5592) * Move exp to its own masterlist, simplify initVariantData * Update test/sprites/pokemonSprite.test.ts * Extract loadPokemonVariantAssets out of BattleScene * move variant.ts and update pokemon.loadAssets * Add fuzzy matching for applying variant recolors * Move glsl shaders to their own files * Remove extra variants from shader masterlist Their exp sprites have since been removed. Co-authored-by: Unicorn_Power <189861924+Unicornpowerstar@users.noreply.github.com> * Make exp sprite keys a set instead of an array * Remove outdated exp sprite jsons Co-authored-by: Unicorn_Power <189861924+Unicornpowerstar@users.noreply.github.com> --------- Co-authored-by: Unicorn_Power <189861924+Unicornpowerstar@users.noreply.github.com> --- .../pokemon/variant/_exp_masterlist.json | 656 +++++++++++++++++ .../images/pokemon/variant/_masterlist.json | 664 ------------------ public/images/pokemon/variant/exp/698.json | 48 -- public/images/pokemon/variant/exp/703.json | 32 - public/images/pokemon/variant/exp/708.json | 28 - public/images/pokemon/variant/exp/714.json | 32 - .../images/pokemon/variant/exp/back/698.json | 38 - .../images/pokemon/variant/exp/back/703.json | 28 - .../images/pokemon/variant/exp/back/708.json | 40 -- .../images/pokemon/variant/exp/back/714.json | 24 - scripts/find_sprite_variant_mismatches.py | 4 + src/battle-scene.ts | 137 +--- .../utils/encounter-phase-utils.ts | 2 +- src/data/pokemon-species.ts | 31 +- src/data/trainers/trainer-config.ts | 7 +- src/data/variant.ts | 31 - src/field/anims.ts | 2 +- src/field/mystery-encounter-intro.ts | 7 +- src/field/pokemon.ts | 188 ++--- src/overrides.ts | 2 +- src/pipelines/field-sprite.ts | 208 +----- src/pipelines/glsl/fieldSpriteFragShader.frag | 168 +++++ src/pipelines/glsl/invert.frag | 10 + src/pipelines/glsl/spriteFragShader.frag | 279 ++++++++ src/pipelines/glsl/spriteShader.vert | 32 + src/pipelines/invert.ts | 14 +- src/pipelines/sprite.ts | 312 +------- src/sprites/pokemon-asset-loader.ts | 11 + src/sprites/pokemon-sprite.ts | 79 +++ src/sprites/sprite-keys.ts | 1 + src/sprites/sprite-utils.ts | 28 + src/sprites/variant.ts | 145 ++++ src/system/game-data.ts | 2 +- src/system/pokemon-data.ts | 2 +- src/ui/battle-info.ts | 2 +- src/ui/hatched-pokemon-container.ts | 2 +- src/ui/party-ui-handler.ts | 2 +- src/ui/pokedex-mon-container.ts | 2 +- src/ui/pokedex-page-ui-handler.ts | 4 +- src/ui/pokedex-ui-handler.ts | 4 +- src/ui/pokemon-info-container.ts | 2 +- src/ui/run-info-ui-handler.ts | 2 +- src/ui/starter-select-ui-handler.ts | 4 +- src/ui/summary-ui-handler.ts | 4 +- src/utils.ts | 22 + test/sprites/pokemonSprite.test.ts | 13 +- test/testUtils/helpers/overridesHelper.ts | 2 +- 47 files changed, 1599 insertions(+), 1758 deletions(-) create mode 100644 public/images/pokemon/variant/_exp_masterlist.json delete mode 100644 public/images/pokemon/variant/exp/698.json delete mode 100644 public/images/pokemon/variant/exp/703.json delete mode 100644 public/images/pokemon/variant/exp/708.json delete mode 100644 public/images/pokemon/variant/exp/714.json delete mode 100644 public/images/pokemon/variant/exp/back/698.json delete mode 100644 public/images/pokemon/variant/exp/back/703.json delete mode 100644 public/images/pokemon/variant/exp/back/708.json delete mode 100644 public/images/pokemon/variant/exp/back/714.json delete mode 100644 src/data/variant.ts create mode 100644 src/pipelines/glsl/fieldSpriteFragShader.frag create mode 100644 src/pipelines/glsl/invert.frag create mode 100644 src/pipelines/glsl/spriteFragShader.frag create mode 100644 src/pipelines/glsl/spriteShader.vert create mode 100644 src/sprites/pokemon-asset-loader.ts create mode 100644 src/sprites/pokemon-sprite.ts create mode 100644 src/sprites/sprite-keys.ts create mode 100644 src/sprites/sprite-utils.ts create mode 100644 src/sprites/variant.ts diff --git a/public/images/pokemon/variant/_exp_masterlist.json b/public/images/pokemon/variant/_exp_masterlist.json new file mode 100644 index 00000000000..0ef5f209439 --- /dev/null +++ b/public/images/pokemon/variant/_exp_masterlist.json @@ -0,0 +1,656 @@ +{ + "3-mega": [0, 2, 2], + "6-mega-x": [0, 2, 2], + "6-mega-y": [0, 2, 2], + "80-mega": [0, 1, 1], + "94-mega": [2, 2, 2], + "127-mega": [0, 1, 1], + "130-mega": [0, 1, 1], + "142-mega": [0, 1, 1], + "150-mega-x": [0, 1, 1], + "150-mega-y": [0, 1, 1], + "181-mega": [0, 1, 2], + "212-mega": [1, 1, 2], + "229-mega": [0, 1, 1], + "248-mega": [0, 1, 1], + "257-mega": [0, 1, 1], + "282-mega": [0, 2, 2], + "302-mega": [0, 1, 1], + "303-mega": [0, 1, 1], + "306-mega": [1, 1, 1], + "308-mega": [0, 1, 1], + "310-mega": [0, 1, 1], + "334-mega": [0, 2, 1], + "354-mega": [0, 1, 1], + "359-mega": [0, 1, 1], + "362-mega": [0, 1, 1], + "373-mega": [0, 1, 1], + "376-mega": [0, 1, 1], + "380-mega": [0, 1, 1], + "381-mega": [0, 1, 1], + "382-primal": [0, 1, 1], + "383-primal": [0, 1, 1], + "384-mega": [0, 2, 1], + "428-mega": [0, 1, 1], + "445-mega": [1, 1, 1], + "448-mega": [1, 1, 1], + "475-mega": [0, 2, 2], + "531-mega": [0, 1, 1], + "653": [0, 1, 1], + "654": [0, 1, 1], + "655": [0, 1, 1], + "656": [0, 1, 1], + "657": [0, 1, 1], + "658": [0, 1, 1], + "658-ash": [0, 1, 1], + "664": [0, 1, 1], + "665": [0, 1, 1], + "666-archipelago": [0, 1, 1], + "666-continental": [0, 1, 1], + "666-elegant": [0, 1, 1], + "666-fancy": [0, 1, 1], + "666-garden": [0, 1, 1], + "666-high-plains": [0, 1, 1], + "666-icy-snow": [0, 1, 1], + "666-jungle": [0, 1, 1], + "666-marine": [0, 1, 1], + "666-meadow": [0, 1, 1], + "666-modern": [0, 1, 1], + "666-monsoon": [0, 1, 1], + "666-ocean": [0, 1, 1], + "666-poke-ball": [0, 1, 1], + "666-polar": [0, 1, 1], + "666-river": [0, 1, 1], + "666-sandstorm": [0, 1, 1], + "666-savanna": [0, 1, 1], + "666-sun": [0, 1, 1], + "666-tundra": [0, 1, 1], + "669-red": [0, 2, 2], + "669-blue": [0, 1, 1], + "669-white": [0, 1, 1], + "669-yellow": [0, 1, 1], + "669-orange": [0, 2, 2], + "670-white": [0, 1, 1], + "670-blue": [0, 1, 1], + "670-orange": [0, 1, 1], + "670-red": [0, 1, 1], + "670-yellow": [0, 1, 1], + "671-red": [0, 1, 2], + "671-blue": [0, 1, 2], + "671-yellow": [0, 1, 1], + "671-white": [0, 1, 2], + "671-orange": [0, 1, 2], + "672": [0, 1, 1], + "673": [0, 1, 1], + "676": [0, 1, 1], + "677": [0, 1, 1], + "678-female": [0, 1, 1], + "678": [0, 1, 1], + "682": [0, 1, 1], + "683": [0, 1, 1], + "684": [0, 1, 1], + "685": [0, 1, 1], + "688": [0, 1, 1], + "689": [0, 1, 1], + "690": [0, 1, 1], + "691": [0, 1, 1], + "696": [0, 1, 1], + "697": [0, 1, 1], + "699": [0, 1, 1], + "700": [0, 1, 1], + "702": [0, 1, 1], + "704": [0, 1, 1], + "705": [0, 1, 1], + "706": [0, 1, 1], + "709": [0, 1, 1], + "710": [0, 1, 1], + "711": [1, 1, 1], + "712": [0, 1, 1], + "713": [0, 1, 1], + "715": [0, 1, 1], + "716-active": [0, 1, 1], + "716-neutral": [0, 1, 1], + "717": [0, 2, 2], + "720-unbound": [1, 1, 1], + "720": [1, 1, 1], + "728": [0, 1, 1], + "729": [0, 1, 1], + "730": [0, 1, 1], + "734": [0, 1, 1], + "735": [0, 1, 1], + "742": [0, 2, 2], + "743": [0, 2, 2], + "747": [0, 2, 2], + "748": [0, 1, 1], + "751": [0, 1, 1], + "752": [0, 1, 1], + "753": [0, 1, 1], + "754": [0, 2, 2], + "755": [0, 1, 1], + "756": [0, 1, 1], + "761": [0, 1, 1], + "762": [0, 1, 1], + "763": [0, 1, 1], + "767": [0, 1, 1], + "768": [0, 1, 1], + "770": [0, 0, 0], + "771": [0, 2, 2], + "772": [0, 1, 1], + "773-fighting": [0, 1, 1], + "773-psychic": [0, 1, 1], + "773-poison": [0, 1, 1], + "773-ground": [0, 1, 1], + "773-ghost": [0, 1, 1], + "773-steel": [0, 1, 1], + "773-rock": [0, 1, 1], + "773-grass": [0, 1, 1], + "773-dragon": [0, 1, 1], + "773-bug": [0, 1, 1], + "773-ice": [0, 1, 1], + "773-dark": [0, 1, 1], + "773": [0, 1, 1], + "773-fairy": [0, 1, 1], + "773-water": [0, 1, 1], + "773-electric": [0, 1, 1], + "773-flying": [0, 1, 1], + "773-fire": [0, 1, 1], + "776": [0, 1, 1], + "777": [0, 1, 1], + "778-busted": [0, 1, 1], + "778-disguised": [0, 1, 1], + "779": [0, 1, 1], + "789": [1, 1, 1], + "790": [0, 1, 1], + "791": [2, 1, 1], + "792": [0, 1, 1], + "793": [0, 2, 2], + "797": [0, 1, 1], + "798": [0, 1, 1], + "800-dawn-wings": [0, 1, 1], + "800-dusk-mane": [0, 1, 1], + "800-ultra": [0, 1, 1], + "800": [0, 1, 1], + "802": [1, 1, 1], + "803": [0, 1, 1], + "804": [0, 1, 1], + "807": [0, 1, 1], + "808": [0, 1, 1], + "809": [0, 1, 1], + "816": [0, 1, 1], + "817": [0, 1, 1], + "818": [1, 1, 1], + "821": [0, 2, 2], + "822": [0, 1, 1], + "823": [0, 1, 1], + "829": [0, 1, 1], + "830": [0, 1, 1], + "835": [0, 1, 1], + "836": [0, 2, 2], + "850": [0, 1, 1], + "851": [0, 1, 1], + "854": [0, 1, 1], + "855": [0, 1, 1], + "856": [0, 1, 1], + "857": [0, 2, 2], + "858": [0, 1, 1], + "859": [0, 1, 1], + "860": [0, 1, 1], + "861": [0, 1, 1], + "862": [0, 1, 1], + "863": [0, 1, 1], + "864": [0, 1, 1], + "867": [0, 1, 1], + "872": [1, 1, 1], + "873": [1, 1, 1], + "876-female": [0, 1, 1], + "876": [0, 1, 1], + "877-hangry": [1, 1, 1], + "877": [1, 1, 1], + "880": [0, 1, 1], + "881": [0, 1, 1], + "882": [0, 2, 1], + "883": [0, 1, 1], + "884": [0, 1, 1], + "885": [1, 1, 1], + "886": [1, 1, 1], + "887": [1, 1, 1], + "888": [0, 1, 1], + "888-crowned": [0, 1, 1], + "889": [0, 1, 1], + "889-crowned": [0, 1, 1], + "890": [0, 2, 1], + "890-eternamax": [0, 1, 1], + "891": [1, 1, 1], + "892-rapid-strike": [1, 1, 1], + "892": [1, 1, 1], + "894": [0, 1, 1], + "895": [0, 1, 1], + "896": [1, 1, 1], + "897": [1, 1, 1], + "898": [1, 1, 1], + "898-ice": [1, 1, 1], + "898-shadow": [1, 1, 1], + "900": [0, 1, 1], + "901": [0, 1, 1], + "903": [0, 1, 1], + "909": [0, 1, 1], + "910": [0, 2, 2], + "911": [0, 2, 2], + "912": [0, 1, 2], + "913": [0, 1, 2], + "914": [0, 2, 1], + "919": [1, 1, 1], + "920": [1, 1, 1], + "924": [1, 1, 1], + "925-four": [1, 2, 2], + "925-three": [1, 2, 2], + "932": [0, 2, 2], + "933": [0, 2, 2], + "934": [0, 1, 1], + "935": [1, 1, 2], + "936": [2, 2, 2], + "937": [2, 2, 2], + "940": [0, 1, 1], + "941": [0, 1, 1], + "944": [0, 1, 1], + "945": [0, 1, 1], + "948": [0, 1, 1], + "949": [0, 1, 1], + "951": [0, 1, 1], + "952": [0, 1, 1], + "953": [0, 1, 1], + "954": [0, 1, 1], + "957": [2, 2, 2], + "958": [2, 2, 2], + "959": [2, 2, 2], + "962": [1, 1, 1], + "967": [0, 1, 1], + "968": [0, 1, 1], + "969": [0, 1, 1], + "970": [0, 1, 1], + "973": [1, 1, 1], + "974": [0, 1, 1], + "975": [0, 1, 1], + "978-curly": [0, 2, 2], + "978-droopy": [0, 2, 2], + "978-stretchy": [0, 2, 2], + "979": [2, 2, 2], + "981": [0, 1, 1], + "982": [0, 1, 1], + "982-three-segment": [0, 1, 1], + "987": [1, 1, 1], + "988": [0, 1, 2], + "993": [0, 1, 1], + "994": [0, 1, 2], + "995": [0, 1, 1], + "996": [0, 1, 1], + "997": [0, 2, 2], + "998": [0, 2, 2], + "999": [2, 1, 1], + "1000": [1, 1, 1], + "1001": [0, 1, 1], + "1003": [0, 1, 1], + "1004": [0, 1, 1], + "1006": [0, 2, 1], + "1007-apex-build": [0, 2, 2], + "1008-ultimate-mode": [1, 1, 1], + "2026": [0, 1, 1], + "2027": [0, 1, 1], + "2028": [0, 1, 1], + "2052": [0, 1, 1], + "2053": [0, 1, 0], + "2103": [0, 1, 1], + "4052": [0, 1, 1], + "4077": [0, 1, 1], + "4078": [0, 1, 1], + "4079": [0, 1, 1], + "4080": [2, 1, 1], + "4144": [0, 1, 1], + "4145": [0, 1, 1], + "4146": [0, 1, 1], + "4199": [2, 1, 1], + "4222": [0, 1, 1], + "4263": [0, 1, 1], + "4264": [0, 1, 1], + "4562": [0, 1, 1], + "6100": [0, 1, 1], + "6101": [0, 1, 1], + "6215": [0, 1, 1], + "6503": [0, 1, 1], + "6549": [0, 1, 1], + "6570": [0, 1, 1], + "6571": [0, 1, 1], + "6705": [0, 1, 1], + "6706": [0, 1, 1], + "6713": [0, 1, 1], + "female": { + "6215": [0, 1, 1] + }, + "back": { + "3-mega": [0, 2, 2], + "6-mega-x": [0, 2, 2], + "6-mega-y": [0, 1, 2], + "80-mega": [0, 1, 1], + "94-mega": [1, 1, 1], + "127-mega": [0, 1, 1], + "130-mega": [0, 1, 1], + "142-mega": [0, 1, 1], + "150-mega-x": [0, 1, 1], + "150-mega-y": [0, 1, 1], + "181-mega": [0, 1, 2], + "212-mega": [1, 2, 2], + "229-mega": [0, 1, 1], + "248-mega": [0, 1, 1], + "257-mega": [0, 1, 1], + "282-mega": [0, 1, 1], + "302-mega": [0, 1, 1], + "303-mega": [0, 1, 1], + "306-mega": [1, 1, 1], + "308-mega": [0, 1, 1], + "310-mega": [0, 1, 1], + "334-mega": [0, 1, 1], + "354-mega": [0, 1, 1], + "359-mega": [0, 1, 1], + "362-mega": [0, 1, 1], + "373-mega": [0, 1, 1], + "376-mega": [0, 1, 1], + "380-mega": [0, 1, 1], + "381-mega": [0, 1, 1], + "382-primal": [0, 1, 1], + "383-primal": [0, 1, 1], + "384-mega": [0, 1, 1], + "428-mega": [0, 1, 1], + "445-mega": [1, 1, 1], + "448-mega": [1, 1, 1], + "475-mega": [0, 2, 2], + "531-mega": [0, 1, 1], + "653": [0, 1, 1], + "654": [0, 1, 1], + "655": [0, 1, 1], + "656": [0, 1, 1], + "657": [0, 1, 1], + "658": [0, 1, 1], + "658-ash": [0, 1, 1], + "664": [0, 1, 1], + "665": [0, 1, 1], + "666-archipelago": [0, 1, 1], + "666-continental": [0, 1, 1], + "666-elegant": [0, 1, 1], + "666-fancy": [0, 1, 1], + "666-garden": [0, 1, 1], + "666-high-plains": [0, 1, 1], + "666-icy-snow": [0, 1, 1], + "666-jungle": [0, 1, 1], + "666-marine": [0, 1, 1], + "666-meadow": [0, 1, 1], + "666-modern": [0, 1, 1], + "666-monsoon": [0, 1, 1], + "666-ocean": [0, 1, 1], + "666-poke-ball": [0, 1, 1], + "666-polar": [0, 1, 1], + "666-river": [0, 1, 1], + "666-sandstorm": [0, 1, 1], + "666-savanna": [0, 1, 1], + "666-sun": [0, 1, 1], + "666-tundra": [0, 1, 1], + "669-red": [0, 2, 2], + "669-blue": [0, 2, 2], + "669-white": [0, 2, 2], + "669-yellow": [0, 2, 2], + "669-orange": [0, 2, 2], + "670-white": [0, 1, 1], + "670-blue": [0, 2, 2], + "670-orange": [0, 1, 1], + "670-red": [0, 1, 1], + "670-yellow": [0, 1, 1], + "671-red": [0, 1, 1], + "671-blue": [0, 1, 1], + "671-yellow": [0, 1, 1], + "671-white": [0, 1, 1], + "671-orange": [0, 1, 1], + "672": [0, 1, 1], + "673": [0, 1, 1], + "676": [0, 1, 1], + "677": [0, 1, 1], + "678-female": [0, 1, 1], + "678": [0, 1, 1], + "682": [0, 1, 1], + "683": [0, 1, 1], + "684": [0, 1, 1], + "685": [0, 1, 1], + "688": [0, 1, 1], + "689": [0, 1, 1], + "690": [0, 1, 1], + "691": [0, 1, 1], + "696": [0, 1, 1], + "697": [0, 1, 1], + "699": [0, 2, 2], + "700": [0, 1, 1], + "702": [0, 1, 1], + "704": [0, 1, 1], + "705": [0, 1, 1], + "706": [0, 1, 1], + "709": [0, 1, 1], + "710": [0, 1, 1], + "711": [1, 1, 1], + "712": [0, 1, 1], + "713": [0, 1, 1], + "715": [0, 1, 1], + "716-active": [0, 1, 1], + "716-neutral": [0, 1, 1], + "717": [0, 1, 1], + "720-unbound": [1, 1, 1], + "720": [1, 1, 1], + "728": [0, 1, 1], + "729": [0, 1, 1], + "730": [0, 1, 1], + "734": [0, 1, 1], + "735": [0, 1, 1], + "742": [0, 2, 2], + "743": [0, 2, 2], + "747": [0, 2, 2], + "748": [0, 1, 1], + "751": [0, 1, 1], + "752": [0, 1, 1], + "753": [0, 1, 1], + "754": [0, 2, 2], + "755": [0, 1, 1], + "756": [0, 1, 1], + "761": [0, 1, 1], + "762": [0, 1, 1], + "763": [0, 1, 1], + "767": [0, 1, 1], + "768": [0, 1, 1], + "771": [0, 1, 1], + "772": [0, 1, 1], + "773-fighting": [0, 1, 1], + "773-psychic": [0, 1, 1], + "773-poison": [0, 1, 1], + "773-ground": [0, 1, 1], + "773-ghost": [0, 1, 1], + "773-steel": [0, 1, 1], + "773-rock": [0, 1, 1], + "773-grass": [0, 1, 1], + "773-dragon": [0, 1, 1], + "773-bug": [0, 1, 1], + "773-ice": [0, 1, 1], + "773-dark": [0, 1, 1], + "773": [0, 1, 1], + "773-fairy": [0, 1, 1], + "773-water": [0, 1, 1], + "773-electric": [0, 1, 1], + "773-flying": [0, 1, 1], + "773-fire": [0, 1, 1], + "776": [0, 2, 2], + "777": [0, 1, 1], + "778-busted": [0, 1, 1], + "778-disguised": [0, 1, 1], + "779": [0, 1, 1], + "789": [1, 1, 1], + "790": [0, 1, 1], + "791": [1, 1, 1], + "792": [0, 1, 1], + "793": [0, 1, 1], + "797": [0, 1, 1], + "798": [0, 1, 1], + "800-dawn-wings": [0, 1, 1], + "800-dusk-mane": [0, 1, 1], + "800-ultra": [0, 1, 1], + "800": [0, 1, 1], + "802": [1, 1, 1], + "803": [0, 1, 1], + "804": [0, 1, 1], + "807": [0, 1, 1], + "808": [0, 1, 1], + "809": [0, 1, 1], + "816": [0, 1, 1], + "817": [0, 1, 1], + "818": [0, 1, 1], + "821": [0, 1, 1], + "822": [0, 1, 1], + "823": [0, 1, 1], + "829": [0, 1, 1], + "830": [0, 1, 1], + "835": [0, 1, 1], + "836": [0, 1, 1], + "850": [0, 1, 1], + "851": [0, 1, 1], + "854": [0, 1, 1], + "855": [0, 1, 1], + "856": [0, 1, 1], + "857": [0, 2, 2], + "858": [0, 1, 1], + "859": [0, 1, 1], + "860": [0, 1, 1], + "861": [0, 1, 1], + "862": [0, 1, 1], + "863": [0, 1, 1], + "864": [0, 1, 1], + "867": [0, 1, 1], + "872": [1, 1, 1], + "873": [1, 1, 1], + "876-female": [0, 1, 1], + "876": [0, 1, 1], + "877-hangry": [1, 1, 1], + "877": [1, 1, 1], + "880": [0, 1, 1], + "881": [0, 1, 1], + "882": [0, 1, 1], + "883": [0, 1, 1], + "884": [0, 1, 1], + "885": [1, 1, 1], + "886": [1, 1, 1], + "887": [1, 1, 1], + "888": [0, 1, 1], + "888-crowned": [0, 1, 1], + "889": [0, 1, 1], + "889-crowned": [0, 1, 1], + "890": [0, 1, 1], + "891": [1, 1, 1], + "892-rapid-strike": [1, 1, 1], + "892": [1, 1, 1], + "894": [0, 1, 1], + "895": [0, 1, 1], + "896": [1, 1, 1], + "897": [1, 1, 1], + "898": [1, 1, 1], + "898-ice": [1, 1, 1], + "898-shadow": [1, 1, 1], + "900": [0, 1, 1], + "901": [0, 1, 1], + "903": [0, 1, 1], + "909": [0, 1, 1], + "910": [0, 2, 2], + "911": [0, 1, 1], + "912": [0, 1, 1], + "913": [0, 1, 1], + "914": [0, 2, 2], + "919": [1, 1, 1], + "920": [1, 1, 1], + "924": [1, 1, 1], + "925-four": [1, 2, 2], + "925-three": [1, 2, 2], + "932": [0, 1, 1], + "933": [0, 1, 1], + "934": [0, 1, 1], + "935": [2, 2, 2], + "936": [2, 2, 2], + "937": [2, 2, 2], + "940": [0, 1, 1], + "941": [0, 1, 1], + "944": [0, 1, 1], + "945": [0, 1, 1], + "948": [0, 1, 1], + "949": [0, 1, 1], + "951": [0, 1, 1], + "952": [0, 2, 1], + "953": [0, 1, 1], + "954": [0, 1, 1], + "957": [1, 1, 1], + "958": [1, 1, 1], + "959": [1, 1, 1], + "962": [1, 1, 1], + "967": [0, 1, 1], + "968": [0, 2, 2], + "969": [0, 1, 1], + "970": [0, 1, 1], + "973": [1, 1, 1], + "974": [0, 1, 1], + "975": [0, 1, 1], + "978-curly": [0, 2, 2], + "978-droopy": [0, 2, 2], + "978-stretchy": [0, 1, 1], + "979": [1, 1, 1], + "981": [0, 1, 1], + "982": [0, 1, 1], + "982-three-segment": [0, 1, 1], + "987": [1, 1, 1], + "988": [0, 1, 1], + "993": [0, 1, 1], + "994": [0, 1, 1], + "995": [0, 1, 1], + "996": [0, 1, 1], + "997": [0, 1, 1], + "998": [0, 1, 1], + "999": [1, 1, 1], + "1000": [1, 1, 1], + "1001": [0, 1, 1], + "1003": [0, 1, 1], + "1004": [0, 1, 1], + "1006": [0, 2, 2], + "1007-apex-build": [0, 2, 2], + "1008-ultimate-mode": [1, 1, 1], + "2026": [0, 1, 1], + "2027": [0, 1, 1], + "2028": [0, 1, 1], + "2052": [0, 1, 1], + "2053": [0, 1, 1], + "2103": [0, 1, 1], + "4052": [0, 1, 1], + "4077": [0, 1, 1], + "4078": [0, 1, 1], + "4079": [0, 1, 1], + "4080": [2, 2, 2], + "4144": [0, 1, 1], + "4145": [0, 1, 1], + "4146": [0, 1, 1], + "4199": [2, 1, 1], + "4222": [0, 1, 1], + "4263": [0, 1, 1], + "4264": [0, 1, 1], + "4562": [0, 1, 1], + "6100": [0, 1, 1], + "6101": [0, 1, 1], + "6215": [0, 1, 1], + "6503": [0, 1, 1], + "6549": [0, 1, 1], + "6570": [0, 1, 1], + "6571": [0, 1, 1], + "6705": [0, 1, 1], + "6706": [0, 1, 1], + "6713": [0, 1, 1], + "female": { + "6215": [0, 1, 1] + } + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/_masterlist.json b/public/images/pokemon/variant/_masterlist.json index 175b56139a6..ac683d9544e 100644 --- a/public/images/pokemon/variant/_masterlist.json +++ b/public/images/pokemon/variant/_masterlist.json @@ -1813,669 +1813,5 @@ "593": [1, 1, 1], "6215": [0, 1, 1] } - }, - "exp": { - "3-mega": [0, 2, 2], - "6-mega-x": [0, 2, 2], - "6-mega-y": [0, 2, 2], - "80-mega": [0, 1, 1], - "94-mega": [2, 2, 2], - "127-mega": [0, 1, 1], - "130-mega": [0, 1, 1], - "142-mega": [0, 1, 1], - "150-mega-x": [0, 1, 1], - "150-mega-y": [0, 1, 1], - "181-mega": [0, 1, 2], - "212-mega": [1, 1, 2], - "229-mega": [0, 1, 1], - "248-mega": [0, 1, 1], - "257-mega": [0, 1, 1], - "282-mega": [0, 2, 2], - "302-mega": [0, 1, 1], - "303-mega": [0, 1, 1], - "306-mega": [1, 1, 1], - "308-mega": [0, 1, 1], - "310-mega": [0, 1, 1], - "334-mega": [0, 2, 1], - "354-mega": [0, 1, 1], - "359-mega": [0, 1, 1], - "362-mega": [0, 1, 1], - "373-mega": [0, 1, 1], - "376-mega": [0, 1, 1], - "380-mega": [0, 1, 1], - "381-mega": [0, 1, 1], - "382-primal": [0, 1, 1], - "383-primal": [0, 1, 1], - "384-mega": [0, 2, 1], - "428-mega": [0, 1, 1], - "445-mega": [1, 1, 1], - "448-mega": [1, 1, 1], - "475-mega": [0, 2, 2], - "531-mega": [0, 1, 1], - "653": [0, 1, 1], - "654": [0, 1, 1], - "655": [0, 1, 1], - "656": [0, 1, 1], - "657": [0, 1, 1], - "658": [0, 1, 1], - "658-ash": [0, 1, 1], - "664": [0, 1, 1], - "665": [0, 1, 1], - "666-archipelago": [0, 1, 1], - "666-continental": [0, 1, 1], - "666-elegant": [0, 1, 1], - "666-fancy": [0, 1, 1], - "666-garden": [0, 1, 1], - "666-high-plains": [0, 1, 1], - "666-icy-snow": [0, 1, 1], - "666-jungle": [0, 1, 1], - "666-marine": [0, 1, 1], - "666-meadow": [0, 1, 1], - "666-modern": [0, 1, 1], - "666-monsoon": [0, 1, 1], - "666-ocean": [0, 1, 1], - "666-poke-ball": [0, 1, 1], - "666-polar": [0, 1, 1], - "666-river": [0, 1, 1], - "666-sandstorm": [0, 1, 1], - "666-savanna": [0, 1, 1], - "666-sun": [0, 1, 1], - "666-tundra": [0, 1, 1], - "669-red": [0, 2, 2], - "669-blue": [0, 1, 1], - "669-white": [0, 1, 1], - "669-yellow": [0, 1, 1], - "669-orange": [0, 2, 2], - "670-white": [0, 1, 1], - "670-blue": [0, 1, 1], - "670-orange": [0, 1, 1], - "670-red": [0, 1, 1], - "670-yellow": [0, 1, 1], - "671-red": [0, 1, 2], - "671-blue": [0, 1, 2], - "671-yellow": [0, 1, 1], - "671-white": [0, 1, 2], - "671-orange": [0, 1, 2], - "672": [0, 1, 1], - "673": [0, 1, 1], - "676": [0, 1, 1], - "677": [0, 1, 1], - "678-female": [0, 1, 1], - "678": [0, 1, 1], - "682": [0, 1, 1], - "683": [0, 1, 1], - "684": [0, 1, 1], - "685": [0, 1, 1], - "688": [0, 1, 1], - "689": [0, 1, 1], - "690": [0, 1, 1], - "691": [0, 1, 1], - "696": [0, 1, 1], - "697": [0, 1, 1], - "698": [0, 1, 1], - "699": [0, 1, 1], - "700": [0, 1, 1], - "702": [0, 1, 1], - "703": [0, 1, 1], - "704": [0, 1, 1], - "705": [0, 1, 1], - "706": [0, 1, 1], - "708": [0, 1, 1], - "709": [0, 1, 1], - "710": [0, 1, 1], - "711": [1, 1, 1], - "712": [0, 1, 1], - "713": [0, 1, 1], - "714": [0, 1, 1], - "715": [0, 1, 1], - "716-active": [0, 1, 1], - "716-neutral": [0, 1, 1], - "717": [0, 2, 2], - "720-unbound": [1, 1, 1], - "720": [1, 1, 1], - "728": [0, 1, 1], - "729": [0, 1, 1], - "730": [0, 1, 1], - "734": [0, 1, 1], - "735": [0, 1, 1], - "742": [0, 2, 2], - "743": [0, 2, 2], - "747": [0, 2, 2], - "748": [0, 1, 1], - "751": [0, 1, 1], - "752": [0, 1, 1], - "753": [0, 1, 1], - "754": [0, 2, 2], - "755": [0, 1, 1], - "756": [0, 1, 1], - "761": [0, 1, 1], - "762": [0, 1, 1], - "763": [0, 1, 1], - "767": [0, 1, 1], - "768": [0, 1, 1], - "770": [0, 0, 0], - "771": [0, 2, 2], - "772": [0, 1, 1], - "773-fighting": [0, 1, 1], - "773-psychic": [0, 1, 1], - "773-poison": [0, 1, 1], - "773-ground": [0, 1, 1], - "773-ghost": [0, 1, 1], - "773-steel": [0, 1, 1], - "773-rock": [0, 1, 1], - "773-grass": [0, 1, 1], - "773-dragon": [0, 1, 1], - "773-bug": [0, 1, 1], - "773-ice": [0, 1, 1], - "773-dark": [0, 1, 1], - "773": [0, 1, 1], - "773-fairy": [0, 1, 1], - "773-water": [0, 1, 1], - "773-electric": [0, 1, 1], - "773-flying": [0, 1, 1], - "773-fire": [0, 1, 1], - "776": [0, 1, 1], - "777": [0, 1, 1], - "778-busted": [0, 1, 1], - "778-disguised": [0, 1, 1], - "779": [0, 1, 1], - "789": [1, 1, 1], - "790": [0, 1, 1], - "791": [2, 1, 1], - "792": [0, 1, 1], - "793": [0, 2, 2], - "797": [0, 1, 1], - "798": [0, 1, 1], - "800-dawn-wings": [0, 1, 1], - "800-dusk-mane": [0, 1, 1], - "800-ultra": [0, 1, 1], - "800": [0, 1, 1], - "802": [1, 1, 1], - "803": [0, 1, 1], - "804": [0, 1, 1], - "807": [0, 1, 1], - "808": [0, 1, 1], - "809": [0, 1, 1], - "816": [0, 1, 1], - "817": [0, 1, 1], - "818": [1, 1, 1], - "821": [0, 2, 2], - "822": [0, 1, 1], - "823": [0, 1, 1], - "829": [0, 1, 1], - "830": [0, 1, 1], - "835": [0, 1, 1], - "836": [0, 2, 2], - "850": [0, 1, 1], - "851": [0, 1, 1], - "854": [0, 1, 1], - "855": [0, 1, 1], - "856": [0, 1, 1], - "857": [0, 2, 2], - "858": [0, 1, 1], - "859": [0, 1, 1], - "860": [0, 1, 1], - "861": [0, 1, 1], - "862": [0, 1, 1], - "863": [0, 1, 1], - "864": [0, 1, 1], - "867": [0, 1, 1], - "872": [1, 1, 1], - "873": [1, 1, 1], - "876-female": [0, 1, 1], - "876": [0, 1, 1], - "877-hangry": [1, 1, 1], - "877": [1, 1, 1], - "880": [0, 1, 1], - "881": [0, 1, 1], - "882": [0, 2, 1], - "883": [0, 1, 1], - "884": [0, 1, 1], - "885": [1, 1, 1], - "886": [1, 1, 1], - "887": [1, 1, 1], - "888": [0, 1, 1], - "888-crowned": [0, 1, 1], - "889": [0, 1, 1], - "889-crowned": [0, 1, 1], - "890": [0, 2, 1], - "890-eternamax": [0, 1, 1], - "891": [1, 1, 1], - "892-rapid-strike": [1, 1, 1], - "892": [1, 1, 1], - "894": [0, 1, 1], - "895": [0, 1, 1], - "896": [1, 1, 1], - "897": [1, 1, 1], - "898": [1, 1, 1], - "898-ice": [1, 1, 1], - "898-shadow": [1, 1, 1], - "900": [0, 1, 1], - "901": [0, 1, 1], - "903": [0, 1, 1], - "909": [0, 1, 1], - "910": [0, 2, 2], - "911": [0, 2, 2], - "912": [0, 1, 2], - "913": [0, 1, 2], - "914": [0, 2, 1], - "919": [1, 1, 1], - "920": [1, 1, 1], - "924": [1, 1, 1], - "925-four": [1, 2, 2], - "925-three": [1, 2, 2], - "932": [0, 2, 2], - "933": [0, 2, 2], - "934": [0, 1, 1], - "935": [1, 1, 2], - "936": [2, 2, 2], - "937": [2, 2, 2], - "940": [0, 1, 1], - "941": [0, 1, 1], - "944": [0, 1, 1], - "945": [0, 1, 1], - "948": [0, 1, 1], - "949": [0, 1, 1], - "951": [0, 1, 1], - "952": [0, 1, 1], - "953": [0, 1, 1], - "954": [0, 1, 1], - "957": [2, 2, 2], - "958": [2, 2, 2], - "959": [2, 2, 2], - "962": [1, 1, 1], - "967": [0, 1, 1], - "968": [0, 1, 1], - "969": [0, 1, 1], - "970": [0, 1, 1], - "973": [1, 1, 1], - "974": [0, 1, 1], - "975": [0, 1, 1], - "978-curly": [0, 2, 2], - "978-droopy": [0, 2, 2], - "978-stretchy": [0, 2, 2], - "979": [2, 2, 2], - "981": [0, 1, 1], - "982": [0, 1, 1], - "982-three-segment": [0, 1, 1], - "987": [1, 1, 1], - "988": [0, 1, 2], - "993": [0, 1, 1], - "994": [0, 1, 2], - "995": [0, 1, 1], - "996": [0, 1, 1], - "997": [0, 2, 2], - "998": [0, 2, 2], - "999": [2, 1, 1], - "1000": [1, 1, 1], - "1001": [0, 1, 1], - "1003": [0, 1, 1], - "1004": [0, 1, 1], - "1006": [0, 2, 1], - "1007-apex-build": [0, 2, 2], - "1008-ultimate-mode": [1, 1, 1], - "2026": [0, 1, 1], - "2027": [0, 1, 1], - "2028": [0, 1, 1], - "2052": [0, 1, 1], - "2053": [0, 1, 0], - "2103": [0, 1, 1], - "4052": [0, 1, 1], - "4077": [0, 1, 1], - "4078": [0, 1, 1], - "4079": [0, 1, 1], - "4080": [2, 1, 1], - "4144": [0, 1, 1], - "4145": [0, 1, 1], - "4146": [0, 1, 1], - "4199": [2, 1, 1], - "4222": [0, 1, 1], - "4263": [0, 1, 1], - "4264": [0, 1, 1], - "4562": [0, 1, 1], - "6100": [0, 1, 1], - "6101": [0, 1, 1], - "6215": [0, 1, 1], - "6503": [0, 1, 1], - "6549": [0, 1, 1], - "6570": [0, 1, 1], - "6571": [0, 1, 1], - "6705": [0, 1, 1], - "6706": [0, 1, 1], - "6713": [0, 1, 1], - "female": { - "6215": [0, 1, 1] - }, - "back": { - "3-mega": [0, 2, 2], - "6-mega-x": [0, 2, 2], - "6-mega-y": [0, 1, 2], - "80-mega": [0, 1, 1], - "94-mega": [1, 1, 1], - "127-mega": [0, 1, 1], - "130-mega": [0, 1, 1], - "142-mega": [0, 1, 1], - "150-mega-x": [0, 1, 1], - "150-mega-y": [0, 1, 1], - "181-mega": [0, 1, 2], - "212-mega": [1, 2, 2], - "229-mega": [0, 1, 1], - "248-mega": [0, 1, 1], - "257-mega": [0, 1, 1], - "282-mega": [0, 1, 1], - "302-mega": [0, 1, 1], - "303-mega": [0, 1, 1], - "306-mega": [1, 1, 1], - "308-mega": [0, 1, 1], - "310-mega": [0, 1, 1], - "334-mega": [0, 1, 1], - "354-mega": [0, 1, 1], - "359-mega": [0, 1, 1], - "362-mega": [0, 1, 1], - "373-mega": [0, 1, 1], - "376-mega": [0, 1, 1], - "380-mega": [0, 1, 1], - "381-mega": [0, 1, 1], - "382-primal": [0, 1, 1], - "383-primal": [0, 1, 1], - "384-mega": [0, 1, 1], - "428-mega": [0, 1, 1], - "445-mega": [1, 1, 1], - "448-mega": [1, 1, 1], - "475-mega": [0, 2, 2], - "531-mega": [0, 1, 1], - "653": [0, 1, 1], - "654": [0, 1, 1], - "655": [0, 1, 1], - "656": [0, 1, 1], - "657": [0, 1, 1], - "658": [0, 1, 1], - "658-ash": [0, 1, 1], - "664": [0, 1, 1], - "665": [0, 1, 1], - "666-archipelago": [0, 1, 1], - "666-continental": [0, 1, 1], - "666-elegant": [0, 1, 1], - "666-fancy": [0, 1, 1], - "666-garden": [0, 1, 1], - "666-high-plains": [0, 1, 1], - "666-icy-snow": [0, 1, 1], - "666-jungle": [0, 1, 1], - "666-marine": [0, 1, 1], - "666-meadow": [0, 1, 1], - "666-modern": [0, 1, 1], - "666-monsoon": [0, 1, 1], - "666-ocean": [0, 1, 1], - "666-poke-ball": [0, 1, 1], - "666-polar": [0, 1, 1], - "666-river": [0, 1, 1], - "666-sandstorm": [0, 1, 1], - "666-savanna": [0, 1, 1], - "666-sun": [0, 1, 1], - "666-tundra": [0, 1, 1], - "669-red": [0, 2, 2], - "669-blue": [0, 2, 2], - "669-white": [0, 2, 2], - "669-yellow": [0, 2, 2], - "669-orange": [0, 2, 2], - "670-white": [0, 1, 1], - "670-blue": [0, 2, 2], - "670-orange": [0, 1, 1], - "670-red": [0, 1, 1], - "670-yellow": [0, 1, 1], - "671-red": [0, 1, 1], - "671-blue": [0, 1, 1], - "671-yellow": [0, 1, 1], - "671-white": [0, 1, 1], - "671-orange": [0, 1, 1], - "672": [0, 1, 1], - "673": [0, 1, 1], - "676": [0, 1, 1], - "677": [0, 1, 1], - "678-female": [0, 1, 1], - "678": [0, 1, 1], - "682": [0, 1, 1], - "683": [0, 1, 1], - "684": [0, 1, 1], - "685": [0, 1, 1], - "688": [0, 1, 1], - "689": [0, 1, 1], - "690": [0, 1, 1], - "691": [0, 1, 1], - "696": [0, 1, 1], - "697": [0, 1, 1], - "698": [0, 1, 1], - "699": [0, 2, 2], - "700": [0, 1, 1], - "702": [0, 1, 1], - "703": [0, 1, 1], - "704": [0, 1, 1], - "705": [0, 1, 1], - "706": [0, 1, 1], - "708": [0, 1, 1], - "709": [0, 1, 1], - "710": [0, 1, 1], - "711": [1, 1, 1], - "712": [0, 1, 1], - "713": [0, 1, 1], - "714": [0, 1, 1], - "715": [0, 1, 1], - "716-active": [0, 1, 1], - "716-neutral": [0, 1, 1], - "717": [0, 1, 1], - "720-unbound": [1, 1, 1], - "720": [1, 1, 1], - "728": [0, 1, 1], - "729": [0, 1, 1], - "730": [0, 1, 1], - "734": [0, 1, 1], - "735": [0, 1, 1], - "742": [0, 2, 2], - "743": [0, 2, 2], - "747": [0, 2, 2], - "748": [0, 1, 1], - "751": [0, 1, 1], - "752": [0, 1, 1], - "753": [0, 1, 1], - "754": [0, 2, 2], - "755": [0, 1, 1], - "756": [0, 1, 1], - "761": [0, 1, 1], - "762": [0, 1, 1], - "763": [0, 1, 1], - "767": [0, 1, 1], - "768": [0, 1, 1], - "771": [0, 1, 1], - "772": [0, 1, 1], - "773-fighting": [0, 1, 1], - "773-psychic": [0, 1, 1], - "773-poison": [0, 1, 1], - "773-ground": [0, 1, 1], - "773-ghost": [0, 1, 1], - "773-steel": [0, 1, 1], - "773-rock": [0, 1, 1], - "773-grass": [0, 1, 1], - "773-dragon": [0, 1, 1], - "773-bug": [0, 1, 1], - "773-ice": [0, 1, 1], - "773-dark": [0, 1, 1], - "773": [0, 1, 1], - "773-fairy": [0, 1, 1], - "773-water": [0, 1, 1], - "773-electric": [0, 1, 1], - "773-flying": [0, 1, 1], - "773-fire": [0, 1, 1], - "776": [0, 2, 2], - "777": [0, 1, 1], - "778-busted": [0, 1, 1], - "778-disguised": [0, 1, 1], - "779": [0, 1, 1], - "789": [1, 1, 1], - "790": [0, 1, 1], - "791": [1, 1, 1], - "792": [0, 1, 1], - "793": [0, 1, 1], - "797": [0, 1, 1], - "798": [0, 1, 1], - "800-dawn-wings": [0, 1, 1], - "800-dusk-mane": [0, 1, 1], - "800-ultra": [0, 1, 1], - "800": [0, 1, 1], - "802": [1, 1, 1], - "803": [0, 1, 1], - "804": [0, 1, 1], - "807": [0, 1, 1], - "808": [0, 1, 1], - "809": [0, 1, 1], - "816": [0, 1, 1], - "817": [0, 1, 1], - "818": [0, 1, 1], - "821": [0, 1, 1], - "822": [0, 1, 1], - "823": [0, 1, 1], - "829": [0, 1, 1], - "830": [0, 1, 1], - "835": [0, 1, 1], - "836": [0, 1, 1], - "850": [0, 1, 1], - "851": [0, 1, 1], - "854": [0, 1, 1], - "855": [0, 1, 1], - "856": [0, 1, 1], - "857": [0, 2, 2], - "858": [0, 1, 1], - "859": [0, 1, 1], - "860": [0, 1, 1], - "861": [0, 1, 1], - "862": [0, 1, 1], - "863": [0, 1, 1], - "864": [0, 1, 1], - "867": [0, 1, 1], - "872": [1, 1, 1], - "873": [1, 1, 1], - "876-female": [0, 1, 1], - "876": [0, 1, 1], - "877-hangry": [1, 1, 1], - "877": [1, 1, 1], - "880": [0, 1, 1], - "881": [0, 1, 1], - "882": [0, 1, 1], - "883": [0, 1, 1], - "884": [0, 1, 1], - "885": [1, 1, 1], - "886": [1, 1, 1], - "887": [1, 1, 1], - "888": [0, 1, 1], - "888-crowned": [0, 1, 1], - "889": [0, 1, 1], - "889-crowned": [0, 1, 1], - "890": [0, 1, 1], - "891": [1, 1, 1], - "892-rapid-strike": [1, 1, 1], - "892": [1, 1, 1], - "894": [0, 1, 1], - "895": [0, 1, 1], - "896": [1, 1, 1], - "897": [1, 1, 1], - "898": [1, 1, 1], - "898-ice": [1, 1, 1], - "898-shadow": [1, 1, 1], - "900": [0, 1, 1], - "901": [0, 1, 1], - "903": [0, 1, 1], - "909": [0, 1, 1], - "910": [0, 2, 2], - "911": [0, 1, 1], - "912": [0, 1, 1], - "913": [0, 1, 1], - "914": [0, 2, 2], - "919": [1, 1, 1], - "920": [1, 1, 1], - "924": [1, 1, 1], - "925-four": [1, 2, 2], - "925-three": [1, 2, 2], - "932": [0, 1, 1], - "933": [0, 1, 1], - "934": [0, 1, 1], - "935": [2, 2, 2], - "936": [2, 2, 2], - "937": [2, 2, 2], - "940": [0, 1, 1], - "941": [0, 1, 1], - "944": [0, 1, 1], - "945": [0, 1, 1], - "948": [0, 1, 1], - "949": [0, 1, 1], - "951": [0, 1, 1], - "952": [0, 2, 1], - "953": [0, 1, 1], - "954": [0, 1, 1], - "957": [1, 1, 1], - "958": [1, 1, 1], - "959": [1, 1, 1], - "962": [1, 1, 1], - "967": [0, 1, 1], - "968": [0, 2, 2], - "969": [0, 1, 1], - "970": [0, 1, 1], - "973": [1, 1, 1], - "974": [0, 1, 1], - "975": [0, 1, 1], - "978-curly": [0, 2, 2], - "978-droopy": [0, 2, 2], - "978-stretchy": [0, 1, 1], - "979": [1, 1, 1], - "981": [0, 1, 1], - "982": [0, 1, 1], - "982-three-segment": [0, 1, 1], - "987": [1, 1, 1], - "988": [0, 1, 1], - "993": [0, 1, 1], - "994": [0, 1, 1], - "995": [0, 1, 1], - "996": [0, 1, 1], - "997": [0, 1, 1], - "998": [0, 1, 1], - "999": [1, 1, 1], - "1000": [1, 1, 1], - "1001": [0, 1, 1], - "1003": [0, 1, 1], - "1004": [0, 1, 1], - "1006": [0, 2, 2], - "1007-apex-build": [0, 2, 2], - "1008-ultimate-mode": [1, 1, 1], - "2026": [0, 1, 1], - "2027": [0, 1, 1], - "2028": [0, 1, 1], - "2052": [0, 1, 1], - "2053": [0, 1, 1], - "2103": [0, 1, 1], - "4052": [0, 1, 1], - "4077": [0, 1, 1], - "4078": [0, 1, 1], - "4079": [0, 1, 1], - "4080": [2, 2, 2], - "4144": [0, 1, 1], - "4145": [0, 1, 1], - "4146": [0, 1, 1], - "4199": [2, 1, 1], - "4222": [0, 1, 1], - "4263": [0, 1, 1], - "4264": [0, 1, 1], - "4562": [0, 1, 1], - "6100": [0, 1, 1], - "6101": [0, 1, 1], - "6215": [0, 1, 1], - "6503": [0, 1, 1], - "6549": [0, 1, 1], - "6570": [0, 1, 1], - "6571": [0, 1, 1], - "6705": [0, 1, 1], - "6706": [0, 1, 1], - "6713": [0, 1, 1], - "female": { - "6215": [0, 1, 1] - } - } } } \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/698.json b/public/images/pokemon/variant/exp/698.json deleted file mode 100644 index daf9b8c6f84..00000000000 --- a/public/images/pokemon/variant/exp/698.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "1": { - "cbaa84": "44827c", - "b3747e": "4b7465", - "eeffbf": "cdffb5", - "dcffb2": "8eeab9", - "ffbfca": "43bf8d", - "b7ffb2": "72d8ce", - "fff2b2": "9bffa9", - "85b4cc": "cf755d", - "a6e1ff": "efab87", - "101010": "101010", - "cacaca": "cacaca", - "537180": "b04f4b", - "2eaeec": "4dc796", - "1f75a0": "29988e", - "fdfdfd": "fdfdfd", - "d197a1": "d197a1", - "ffdce6": "ffdce6", - "217aa6": "7f99e1", - "30b2f2": "b5dcff", - "f9f9f9": "e6e3b4", - "c0c0c0": "d7cca0" - }, - "2": { - "cbaa84": "cc78db", - "b3747e": "c452a6", - "eeffbf": "ed9ff2", - "dcffb2": "d7bbf4", - "ffbfca": "faccff", - "b7ffb2": "dceeff", - "fff2b2": "eb88b9", - "85b4cc": "654a8a", - "a6e1ff": "936daa", - "101010": "101010", - "cacaca": "cacaca", - "537180": "392d65", - "2eaeec": "ad4e6e", - "1f75a0": "8d2656", - "fdfdfd": "fdfdfd", - "d197a1": "d197a1", - "ffdce6": "ffdce6", - "217aa6": "efaa51", - "30b2f2": "ffd169", - "f9f9f9": "373453", - "c0c0c0": "282747" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/703.json b/public/images/pokemon/variant/exp/703.json deleted file mode 100644 index c024feb1b30..00000000000 --- a/public/images/pokemon/variant/exp/703.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "1": { - "535763": "292638", - "306090": "c35b2a", - "c3c7d3": "68638e", - "88aacc": "e67c37", - "fefefe": "fefefe", - "a3a7b3": "4d496b", - "737783": "37344e", - "101010": "101010", - "bbddff": "ffa633", - "1fbfdf": "ff9b44", - "5f6060": "e6ac60", - "fcfefe": "ffeed6", - "bfbbbb": "ffd3a1" - }, - "2": { - "535763": "976ba9", - "306090": "a03c69", - "c3c7d3": "faecff", - "88aacc": "e25493", - "fefefe": "ffe2ee", - "a3a7b3": "e4cdf9", - "737783": "cca1db", - "101010": "101010", - "bbddff": "f591bd", - "1fbfdf": "de5f8e", - "5f6060": "5a3d84", - "fcfefe": "a473bf", - "bfbbbb": "8359a7" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/708.json b/public/images/pokemon/variant/exp/708.json deleted file mode 100644 index b32bbb79cd9..00000000000 --- a/public/images/pokemon/variant/exp/708.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "1": { - "101010": "101010", - "2b2a3a": "722023", - "603d2b": "36384f", - "215738": "4d362e", - "48484a": "a14743", - "c18760": "7c808c", - "3fa76c": "907f76", - "915e45": "575a6a", - "0b0c0b": "0b0c0b", - "da585b": "5996d2", - "ff8c8f": "87d1ff" - }, - "2": { - "101010": "101010", - "2b2a3a": "6f5f80", - "603d2b": "31161d", - "215738": "a94079", - "48484a": "9c92a4", - "c18760": "7e5658", - "3fa76c": "da7ea8", - "915e45": "56323a", - "0b0c0b": "0b0c0b", - "da585b": "e18933", - "ff8c8f": "ffc875" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/714.json b/public/images/pokemon/variant/exp/714.json deleted file mode 100644 index 018366c5381..00000000000 --- a/public/images/pokemon/variant/exp/714.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "1": { - "6a3f73": "731338", - "bd70cc": "a42c54", - "101010": "101010", - "bfacbf": "7047ba", - "8e5499": "8e1d4b", - "f2daf2": "8d7be3", - "404040": "202558", - "665c66": "2f386b", - "ccb43d": "ff8a58", - "f8f8f8": "8d7be3", - "595959": "2f386b", - "ffe14c": "ffc182", - "000000": "101010" - }, - "2": { - "6a3f73": "5f151c", - "bd70cc": "c24430", - "101010": "101010", - "bfacbf": "f9e8dd", - "8e5499": "882c27", - "f2daf2": "f8f8f8", - "404040": "5b1922", - "665c66": "7c2928", - "ccb43d": "33d8d0", - "f8f8f8": "f8f8f8", - "595959": "7c2928", - "ffe14c": "49ffcd", - "000000": "101010" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/back/698.json b/public/images/pokemon/variant/exp/back/698.json deleted file mode 100644 index af193c3bc0c..00000000000 --- a/public/images/pokemon/variant/exp/back/698.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "1": { - "b3747e": "4b7465", - "ffbfca": "43bf8d", - "fff2b2": "9bffa9", - "537180": "b04f4b", - "a6e1ff": "efab87", - "101010": "101010", - "85b4cc": "cf755d", - "217aa6": "7f99e1", - "30b2f2": "b5dcff", - "fdfdfd": "fdfdfd", - "c0c0c0": "d7cca0", - "cacaca": "cacaca", - "cbaa84": "44827c", - "dcffb2": "8eeab9", - "eeffbf": "cdffb5", - "b7ffb2": "72d8ce" - }, - "2": { - "b3747e": "c452a6", - "ffbfca": "faccff", - "fff2b2": "eb88b9", - "537180": "392d65", - "a6e1ff": "936daa", - "101010": "101010", - "85b4cc": "654a8a", - "217aa6": "efaa51", - "30b2f2": "ffd169", - "fdfdfd": "fdfdfd", - "c0c0c0": "282747", - "cacaca": "cacaca", - "cbaa84": "cc78db", - "dcffb2": "d7bbf4", - "eeffbf": "ed9ff2", - "b7ffb2": "dceeff" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/back/703.json b/public/images/pokemon/variant/exp/back/703.json deleted file mode 100644 index 376abd466d2..00000000000 --- a/public/images/pokemon/variant/exp/back/703.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "1": { - "306090": "c35b2a", - "88aacc": "e67c37", - "fefefe": "fefefe", - "535763": "292638", - "a3a7b3": "4d496b", - "737783": "37344e", - "bbddff": "ffa633", - "101010": "101010", - "5f6060": "e6ac60", - "bfbbbb": "ffd3a1", - "fcfefe": "ffeed6" - }, - "2": { - "306090": "a03c69", - "88aacc": "e25493", - "fefefe": "ffe2ee", - "535763": "976ba9", - "a3a7b3": "e4cdf9", - "737783": "cca1db", - "bbddff": "f591bd", - "101010": "101010", - "5f6060": "5a3d84", - "bfbbbb": "8359a7", - "fcfefe": "a473bf" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/back/708.json b/public/images/pokemon/variant/exp/back/708.json deleted file mode 100644 index 7d41d6d24b0..00000000000 --- a/public/images/pokemon/variant/exp/back/708.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "1": { - "1a1a1c": "1a1a1c", - "686665": "646085", - "103222": "802c26", - "221b17": "221b17", - "090606": "090606", - "4ab38e": "a14743", - "38956f": "a14743", - "ab9074": "7c808c", - "4e4e4e": "494e5b", - "917860": "7c808c", - "424244": "2b303c", - "78604c": "575a6a", - "6b5442": "40435a", - "5f4939": "36384f", - "4f2a09": "292929", - "6c4513": "36384f", - "353638": "353638" - }, - "2": { - "1a1a1c": "1a1a1c", - "686665": "ccc3cf", - "103222": "a94079", - "221b17": "221b17", - "090606": "090606", - "4ab38e": "da7ea8", - "38956f": "da7ea8", - "ab9074": "7e5658", - "4e4e4e": "9c92a4", - "917860": "7e5658", - "424244": "6f5f80", - "78604c": "56323a", - "6b5442": "47232b", - "5f4939": "31161d", - "4f2a09": "250e14", - "6c4513": "31161d", - "353638": "57496b" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/back/714.json b/public/images/pokemon/variant/exp/back/714.json deleted file mode 100644 index 22933e71338..00000000000 --- a/public/images/pokemon/variant/exp/back/714.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "1": { - "101010": "101010", - "6a3f73": "500a25", - "bd70cc": "a42c54", - "8e5499": "8e1d4b", - "404040": "202558", - "595959": "2f386b", - "bfacbf": "8d7be3", - "665c66": "2f386b", - "f2daf2": "8d7be3" - }, - "2": { - "101010": "101010", - "6a3f73": "5f151c", - "bd70cc": "c24430", - "8e5499": "882c27", - "404040": "5b1922", - "595959": "7c2928", - "bfacbf": "f9e8dd", - "665c66": "7c2928", - "f2daf2": "f9e8dd" - } -} \ No newline at end of file diff --git a/scripts/find_sprite_variant_mismatches.py b/scripts/find_sprite_variant_mismatches.py index 483695fdb66..b26058c2de3 100644 --- a/scripts/find_sprite_variant_mismatches.py +++ b/scripts/find_sprite_variant_mismatches.py @@ -22,6 +22,9 @@ from typing import Literal as L MASTERLIST_PATH = os.path.join( os.path.dirname(os.path.dirname(__file__)), "public", "images", "pokemon", "variant", "_masterlist.json" ) +EXP_MASTERLIST_PATH = os.path.join( + os.path.dirname(os.path.dirname(__file__)), "public", "images", "pokemon", "variant", "_exp_masterlist.json" +) DEFAULT_OUTPUT_PATH = "sprite-mismatches.csv" @@ -93,6 +96,7 @@ if __name__ == "__main__": help=f"The path to a file to save the output file. If not specified, will write to {DEFAULT_OUTPUT_PATH}.", ) p.add_argument("--masterlist", default=MASTERLIST_PATH, help=f"The path to the masterlist file to validate. Defaults to {MASTERLIST_PATH}.") + p.add_argument("--exp-masterlist", default=EXP_MASTERLIST_PATH, help=f"The path to the exp masterlist file to validate against. Defaults to {EXP_MASTERLIST_PATH}.") args = p.parse_args() mismatches = make_mismatch_sprite_list(args.masterlist) write_mismatch_csv(args.output, mismatches) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index f676ba63306..acc8dafdd35 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -106,8 +106,8 @@ import PokemonInfoContainer from "#app/ui/pokemon-info-container"; import { biomeDepths, getBiomeName } from "#app/data/balance/biomes"; import { SceneBase } from "#app/scene-base"; import CandyBar from "#app/ui/candy-bar"; -import type { Variant, VariantSet } from "#app/data/variant"; -import { variantColorCache, variantData } from "#app/data/variant"; +import type { Variant } from "#app/sprites/variant"; +import { variantData, clearVariantData } from "#app/sprites/variant"; import type { Localizable } from "#app/interfaces/locales"; import Overrides from "#app/overrides"; import { InputsController } from "#app/inputs-controller"; @@ -170,6 +170,8 @@ 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"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; @@ -182,8 +184,6 @@ const OPP_IVS_OVERRIDE_VALIDATED: number[] = ( export const startingWave = Overrides.STARTING_WAVE_OVERRIDE || 1; -const expSpriteKeys: string[] = []; - export let starterColors: StarterColors; interface StarterColors { [key: string]: [string, string]; @@ -409,7 +409,7 @@ export default class BattleScene extends SceneBase { } const variant = atlasPath.includes("variant/") || /_[0-3]$/.test(atlasPath); if (experimental) { - experimental = this.hasExpSprite(key); + experimental = hasExpSprite(key); } if (variant) { atlasPath = atlasPath.replace("variant/", ""); @@ -421,35 +421,6 @@ export default class BattleScene extends SceneBase { ); } - /** - * Load the variant assets for the given sprite and stores them in {@linkcode variantColorCache} - */ - public async loadPokemonVariantAssets(spriteKey: string, fileRoot: string, variant?: Variant): Promise { - const useExpSprite = this.experimentalSprites && this.hasExpSprite(spriteKey); - if (useExpSprite) { - fileRoot = `exp/${fileRoot}`; - } - let variantConfig = variantData; - fileRoot.split("/").map(p => (variantConfig ? (variantConfig = variantConfig[p]) : null)); - const variantSet = variantConfig as VariantSet; - - return new Promise(resolve => { - if (variantSet && variant !== undefined && variantSet[variant] === 1) { - if (variantColorCache.hasOwnProperty(spriteKey)) { - return resolve(); - } - this.cachedFetch(`./images/pokemon/variant/${fileRoot}.json`) - .then(res => res.json()) - .then(c => { - variantColorCache[spriteKey] = c; - resolve(); - }); - } else { - resolve(); - } - }); - } - async preload() { if (DEBUG_RNG) { const originalRealInRange = Phaser.Math.RND.realInRange; @@ -783,53 +754,36 @@ export default class BattleScene extends SceneBase { } async initExpSprites(): Promise { - if (expSpriteKeys.length) { + if (expSpriteKeys.size > 0) { return; } this.cachedFetch("./exp-sprites.json") .then(res => res.json()) .then(keys => { if (Array.isArray(keys)) { - expSpriteKeys.push(...keys); + for (const key of keys) { + expSpriteKeys.add(key); + } } Promise.resolve(); }); } + /** + * Initialize the variant data. + * If experimental sprites are enabled, their entries are replaced via this method. + */ async initVariantData(): Promise { - for (const key of Object.keys(variantData)) { - delete variantData[key]; + clearVariantData(); + const otherVariantData = await this.cachedFetch("./images/pokemon/variant/_masterlist.json").then(r => r.json()); + for (const k of Object.keys(otherVariantData)) { + variantData[k] = otherVariantData[k]; } - await this.cachedFetch("./images/pokemon/variant/_masterlist.json") - .then(res => res.json()) - .then(v => { - for (const k of Object.keys(v)) { - variantData[k] = v[k]; - } - if (this.experimentalSprites) { - const expVariantData = variantData["exp"]; - const traverseVariantData = (keys: string[]) => { - let variantTree = variantData; - let expTree = expVariantData; - keys.map((k: string, i: number) => { - if (i < keys.length - 1) { - variantTree = variantTree[k]; - expTree = expTree[k]; - } else if (variantTree.hasOwnProperty(k) && expTree.hasOwnProperty(k)) { - if (["back", "female"].includes(k)) { - traverseVariantData(keys.concat(k)); - } else { - variantTree[k] = expTree[k]; - } - } - }); - }; - for (const ek of Object.keys(expVariantData)) { - traverseVariantData([ek]); - } - } - Promise.resolve(); - }); + if (!this.experimentalSprites) { + return; + } + const expVariantData = await this.cachedFetch("./images/pokemon/variant/_exp_masterlist.json").then(r => r.json()); + Utils.deepMergeObjects(variantData, expVariantData); } cachedFetch(url: string, init?: RequestInit): Promise { @@ -843,48 +797,15 @@ export default class BattleScene extends SceneBase { return fetch(url, init); } - initStarterColors(): Promise { - return new Promise(resolve => { - if (starterColors) { - return resolve(); - } - - this.cachedFetch("./starter-colors.json") - .then(res => res.json()) - .then(sc => { - starterColors = {}; - for (const key of Object.keys(sc)) { - starterColors[key] = sc[key]; - } - - resolve(); - }); - }); - } - - hasExpSprite(key: string): boolean { - const keyMatch = /^pkmn__?(back__)?(shiny__)?(female__)?(\d+)(\-.*?)?(?:_[1-3])?$/g.exec(key); - if (!keyMatch) { - return false; + async initStarterColors(): Promise { + if (starterColors) { + return; } - - let k = keyMatch[4]!; - if (keyMatch[2]) { - k += "s"; + const sc = await this.cachedFetch("./starter-colors.json").then(res => res.json()); + starterColors = {}; + for (const key of Object.keys(sc)) { + starterColors[key] = sc[key]; } - if (keyMatch[1]) { - k += "b"; - } - if (keyMatch[3]) { - k += "f"; - } - if (keyMatch[5]) { - k += keyMatch[5]; - } - if (!expSpriteKeys.includes(k)) { - return false; - } - return true; } public getPlayerParty(): PlayerPokemon[] { diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 76d07bf01ba..f3a06242a13 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -58,7 +58,7 @@ 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/data/variant"; +import type { Variant } from "#app/sprites/variant"; import { StatusEffect } from "#enums/status-effect"; import { globalScene } from "#app/global-scene"; import { getPokemonSpecies } from "#app/data/pokemon-species"; diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 929d632eb0b..a8942a39880 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -26,11 +26,12 @@ import { pokemonSpeciesLevelMoves, } from "#app/data/balance/pokemon-level-moves"; import type { Stat } from "#enums/stat"; -import type { Variant, VariantSet } from "#app/data/variant"; -import { variantData } from "#app/data/variant"; +import type { Variant, VariantSet } from "#app/sprites/variant"; +import { variantData } from "#app/sprites/variant"; import { speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters"; import { SpeciesFormKey } from "#enums/species-form-key"; import { starterPassiveAbilities } from "#app/data/balance/passives"; +import { loadPokemonVariantAssets } from "#app/sprites/pokemon-sprite"; export enum Region { NORMAL, @@ -387,6 +388,7 @@ export abstract class PokemonSpeciesForm { return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; } + /** Compute the sprite ID of the pokemon form. */ getSpriteId(female: boolean, formIndex?: number, shiny?: boolean, variant = 0, back?: boolean): string { if (formIndex === undefined || this instanceof PokemonForm) { formIndex = this.formIndex; @@ -394,7 +396,9 @@ export abstract class PokemonSpeciesForm { const formSpriteKey = this.getFormSpriteKey(formIndex); const showGenderDiffs = - this.genderDiffs && female && ![SpeciesFormKey.MEGA, SpeciesFormKey.GIGANTAMAX].find(k => formSpriteKey === k); + this.genderDiffs && + female && + ![SpeciesFormKey.MEGA, SpeciesFormKey.GIGANTAMAX].includes(formSpriteKey as SpeciesFormKey); const baseSpriteKey = `${showGenderDiffs ? "female__" : ""}${this.speciesId}${formSpriteKey ? `-${formSpriteKey}` : ""}`; @@ -585,18 +589,19 @@ export abstract class PokemonSpeciesForm { return true; } - loadAssets( + async loadAssets( female: boolean, formIndex?: number, - shiny?: boolean, + shiny = false, variant?: Variant, - startLoad?: boolean, - back?: boolean, + startLoad = false, + back = false, ): Promise { - return new Promise(resolve => { - const spriteKey = this.getSpriteKey(female, formIndex, shiny, variant, back); - globalScene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant, back)); - globalScene.load.audio(`${this.getCryKey(formIndex)}`, `audio/${this.getCryKey(formIndex)}.m4a`); + const spriteKey = this.getSpriteKey(female, formIndex, shiny, variant, back); + globalScene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant, back)); + globalScene.load.audio(this.getCryKey(formIndex), `audio/${this.getCryKey(formIndex)}.m4a`); + + return new Promise(resolve => { globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => { const originalWarn = console.warn; // Ignore warnings for missing frames, because there will be a lot @@ -621,7 +626,9 @@ export abstract class PokemonSpeciesForm { const spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant, back) .replace("variant/", "") .replace(/_[1-3]$/, ""); - globalScene.loadPokemonVariantAssets(spriteKey, spritePath, variant).then(() => resolve()); + if (!Utils.isNullOrUndefined(variant)) { + loadPokemonVariantAssets(spriteKey, spritePath, variant).then(() => resolve()); + } }); if (startLoad) { if (!globalScene.load.isLoading()) { diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index a5ba19290fe..5fab70971ec 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -2236,12 +2236,7 @@ export const trainerConfigs: TrainerConfigs = { Species.PHANTUMP, Species.PUMPKABOO, ], - [TrainerPoolTier.RARE]: [ - Species.SNEASEL, - Species.LITWICK, - Species.PAWNIARD, - Species.NOIBAT, - ], + [TrainerPoolTier.RARE]: [Species.SNEASEL, Species.LITWICK, Species.PAWNIARD, Species.NOIBAT], [TrainerPoolTier.SUPER_RARE]: [Species.SLIGGOO, Species.HISUI_SLIGGOO, Species.HISUI_AVALUGG], }), [TrainerType.BRYONY]: new TrainerConfig(++t) diff --git a/src/data/variant.ts b/src/data/variant.ts deleted file mode 100644 index 13c11b0bb40..00000000000 --- a/src/data/variant.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { VariantTier } from "#app/enums/variant-tier"; - -export type Variant = 0 | 1 | 2; - -export type VariantSet = [Variant, Variant, Variant]; - -export const variantData: any = {}; - -export const variantColorCache = {}; - -export function getVariantTint(variant: Variant): number { - switch (variant) { - case 0: - return 0xf8c020; - case 1: - return 0x20f8f0; - case 2: - return 0xe81048; - } -} - -export function getVariantIcon(variant: Variant): number { - switch (variant) { - case 0: - return VariantTier.STANDARD; - case 1: - return VariantTier.RARE; - case 2: - return VariantTier.EPIC; - } -} diff --git a/src/field/anims.ts b/src/field/anims.ts index cd6209dddff..eb895c2d8f9 100644 --- a/src/field/anims.ts +++ b/src/field/anims.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { PokeballType } from "#enums/pokeball"; -import type { Variant } from "#app/data/variant"; +import type { Variant } from "#app/sprites/variant"; import { getFrameMs, randGauss } from "#app/utils"; export function addPokeballOpenParticles(x: number, y: number, pokeballType: PokeballType): void { diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index 649a969d415..e1fb0c37074 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -4,9 +4,10 @@ import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounte import type { Species } from "#enums/species"; import { isNullOrUndefined } from "#app/utils"; import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import type { Variant } from "#app/data/variant"; +import type { Variant } from "#app/sprites/variant"; import { doShinySparkleAnim } from "#app/field/anims"; import PlayAnimationConfig = Phaser.Types.Animations.PlayAnimationConfig; +import { loadPokemonVariantAssets } from "#app/sprites/pokemon-sprite"; type KnownFileRoot = | "arenas" @@ -233,8 +234,8 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con this.spriteConfigs.forEach(config => { if (config.isPokemon) { globalScene.loadPokemonAtlas(config.spriteKey, config.fileRoot); - if (config.isShiny) { - shinyPromises.push(globalScene.loadPokemonVariantAssets(config.spriteKey, config.fileRoot, config.variant)); + if (config.isShiny && !isNullOrUndefined(config.variant)) { + shinyPromises.push(loadPokemonVariantAssets(config.spriteKey, config.fileRoot, config.variant)); } } else if (config.isItem) { globalScene.loadAtlas("items", ""); diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 7d856696188..72da3f1ed6f 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2,9 +2,9 @@ import Phaser from "phaser"; import type { AnySound } from "#app/battle-scene"; import type BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; -import type { Variant, VariantSet } from "#app/data/variant"; -import { variantColorCache } from "#app/data/variant"; -import { variantData } from "#app/data/variant"; +import type { Variant, VariantSet } from "#app/sprites/variant"; +import { populateVariantColors, variantColorCache } from "#app/sprites/variant"; +import { variantData } from "#app/sprites/variant"; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo, @@ -263,7 +263,9 @@ import { Nature } from "#enums/nature"; import { StatusEffect } from "#enums/status-effect"; import { doShinySparkleAnim } from "#app/field/anims"; import { MoveFlags } from "#enums/MoveFlags"; +import { hasExpSprite } from "#app/sprites/sprite-utils"; 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 { @@ -696,115 +698,79 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { abstract getBattlerIndex(): BattlerIndex; - loadAssets(ignoreOverride = true): Promise { - return new Promise(resolve => { - const moveIds = this.getMoveset().map(m => m.getMove().id); - Promise.allSettled(moveIds.map(m => initMoveAnim(m))).then(() => { - loadMoveAnimAssets(moveIds); - this.getSpeciesForm().loadAssets( - this.getGender() === Gender.FEMALE, - this.formIndex, - this.shiny, - this.variant, - ); - if (this.isPlayer() || this.getFusionSpeciesForm()) { - globalScene.loadPokemonAtlas( - this.getBattleSpriteKey(true, ignoreOverride), - this.getBattleSpriteAtlasPath(true, ignoreOverride), - ); - } - if (this.getFusionSpeciesForm()) { - this.getFusionSpeciesForm().loadAssets( - this.getFusionGender() === Gender.FEMALE, - this.fusionFormIndex, - this.fusionShiny, - this.fusionVariant, - ); - globalScene.loadPokemonAtlas( - this.getFusionBattleSpriteKey(true, ignoreOverride), - this.getFusionBattleSpriteAtlasPath(true, ignoreOverride), - ); - } - globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => { - if (this.isPlayer()) { - const originalWarn = console.warn; - // Ignore warnings for missing frames, because there will be a lot - console.warn = () => {}; - const battleFrameNames = globalScene.anims.generateFrameNames( - this.getBattleSpriteKey(), - { zeroPad: 4, suffix: ".png", start: 1, end: 400 }, - ); - console.warn = originalWarn; - if (!globalScene.anims.exists(this.getBattleSpriteKey())) { - globalScene.anims.create({ - key: this.getBattleSpriteKey(), - frames: battleFrameNames, - frameRate: 10, - repeat: -1, - }); - } - } - this.playAnim(); - const updateFusionPaletteAndResolve = () => { - this.updateFusionPalette(); - if (this.summonData?.speciesForm) { - this.updateFusionPalette(true); - } - resolve(); - }; - if (this.shiny) { - const populateVariantColors = ( - isBackSprite = false, - ): Promise => { - return new Promise(async resolve => { - const battleSpritePath = this.getBattleSpriteAtlasPath( - isBackSprite, - ignoreOverride, - ) - .replace("variant/", "") - .replace(/_[1-3]$/, ""); - let config = variantData; - const useExpSprite = - globalScene.experimentalSprites && - globalScene.hasExpSprite( - this.getBattleSpriteKey(isBackSprite, ignoreOverride), - ); - battleSpritePath - .split("/") - .map(p => (config ? (config = config[p]) : null)); - const variantSet: VariantSet = config as VariantSet; - if (variantSet && variantSet[this.variant] === 1) { - const cacheKey = this.getBattleSpriteKey(isBackSprite); - if (!variantColorCache.hasOwnProperty(cacheKey)) { - await this.populateVariantColorCache( - cacheKey, - useExpSprite, - battleSpritePath, - ); - } - } - resolve(); - }); - }; - if (this.isPlayer()) { - Promise.all([ - populateVariantColors(false), - populateVariantColors(true), - ]).then(() => updateFusionPaletteAndResolve()); - } else { - populateVariantColors(false).then(() => - updateFusionPaletteAndResolve(), - ); - } - } else { - updateFusionPaletteAndResolve(); - } - }); - if (!globalScene.load.isLoading()) { - globalScene.load.start(); - } + async loadAssets(ignoreOverride = true): Promise { + /** Promises that are loading assets and can be run concurrently. */ + const loadPromises: Promise[] = []; + // Assets for moves + loadPromises.push(loadMoveAnimations(this.getMoveset().map(m => m.getMove().id))); + + // Load the assets for the species form + loadPromises.push( + this.getSpeciesForm().loadAssets(this.getGender() === Gender.FEMALE, this.formIndex, this.shiny, this.variant), + ); + + if (this.isPlayer() || this.getFusionSpeciesForm()) { + globalScene.loadPokemonAtlas( + this.getBattleSpriteKey(true, ignoreOverride), + this.getBattleSpriteAtlasPath(true, ignoreOverride), + ); + } + if (this.getFusionSpeciesForm()) { + loadPromises.push(this.getFusionSpeciesForm().loadAssets( + this.getFusionGender() === Gender.FEMALE, + this.fusionFormIndex, + this.fusionShiny, + this.fusionVariant, + )); + globalScene.loadPokemonAtlas( + this.getFusionBattleSpriteKey(true, ignoreOverride), + this.getFusionBattleSpriteAtlasPath(true, ignoreOverride), + ); + } + + if (this.shiny) { + loadPromises.push(populateVariantColors(this, false, ignoreOverride)) + if (this.isPlayer()) { + loadPromises.push(populateVariantColors(this, true, ignoreOverride)); + } + } + + await Promise.allSettled(loadPromises); + + // Wait for the assets we queued to load to finish loading, then... + if (!globalScene.load.isLoading()) { + globalScene.load.start(); + } + // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#creating_a_promise_around_an_old_callback_api + await new Promise(resolve => globalScene.load.once(Phaser.Loader.Events.COMPLETE, resolve)); + + // With the sprites loaded, generate the animation frame information + if (this.isPlayer()) { + const originalWarn = console.warn; + // Ignore warnings for missing frames, because there will be a lot + console.warn = () => {}; + const battleFrameNames = globalScene.anims.generateFrameNames(this.getBattleSpriteKey(), { + zeroPad: 4, + suffix: ".png", + start: 1, + end: 400, }); - }); + console.warn = originalWarn; + globalScene.anims.create({ + key: this.getBattleSpriteKey(), + frames: battleFrameNames, + frameRate: 10, + repeat: -1, + }); + } + // With everything loaded, now begin playing the animation. + this.playAnim(); + + // update the fusion palette + this.updateFusionPalette(); + if (this.summonData?.speciesForm) { + this.updateFusionPalette(true); + } } /** diff --git a/src/overrides.ts b/src/overrides.ts index 3a9a54e740b..21c72cd7b98 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -2,7 +2,7 @@ import { type PokeballCounts } from "#app/battle-scene"; import { EvolutionItem } from "#app/data/balance/pokemon-evolutions"; import { Gender } from "#app/data/gender"; import { FormChangeItem } from "#app/data/pokemon-forms"; -import { Variant } from "#app/data/variant"; +import { Variant } from "#app/sprites/variant"; import { type ModifierOverride } from "#app/modifier/modifier-type"; import { Unlockables } from "#app/system/unlockables"; import { Abilities } from "#enums/abilities"; diff --git a/src/pipelines/field-sprite.ts b/src/pipelines/field-sprite.ts index 547281d7dee..612c9fae052 100644 --- a/src/pipelines/field-sprite.ts +++ b/src/pipelines/field-sprite.ts @@ -1,210 +1,8 @@ import { globalScene } from "#app/global-scene"; import { TerrainType, getTerrainColor } from "../data/terrain"; import * as Utils from "../utils"; - -const spriteFragShader = ` -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif - -uniform sampler2D uMainSampler[%count%]; - -varying vec2 outTexCoord; -varying float outTexId; -varying float outTintEffect; -varying vec4 outTint; - -uniform float time; -uniform int ignoreTimeTint; -uniform int isOutside; -uniform vec3 dayTint; -uniform vec3 duskTint; -uniform vec3 nightTint; -uniform vec3 terrainColor; -uniform float terrainColorRatio; - -float blendOverlay(float base, float blend) { - return base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend)); -} - -vec3 blendOverlay(vec3 base, vec3 blend) { - return vec3(blendOverlay(base.r,blend.r),blendOverlay(base.g,blend.g),blendOverlay(base.b,blend.b)); -} - -vec3 blendHardLight(vec3 base, vec3 blend) { - return blendOverlay(blend, base); -} - -float hue2rgb(float f1, float f2, float hue) { - if (hue < 0.0) - hue += 1.0; - else if (hue > 1.0) - hue -= 1.0; - float res; - if ((6.0 * hue) < 1.0) - res = f1 + (f2 - f1) * 6.0 * hue; - else if ((2.0 * hue) < 1.0) - res = f2; - else if ((3.0 * hue) < 2.0) - res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0; - else - res = f1; - return res; -} - -vec3 rgb2hsl(vec3 color) { - vec3 hsl; - - float fmin = min(min(color.r, color.g), color.b); - float fmax = max(max(color.r, color.g), color.b); - float delta = fmax - fmin; - - hsl.z = (fmax + fmin) / 2.0; - - if (delta == 0.0) { - hsl.x = 0.0; - hsl.y = 0.0; - } else { - if (hsl.z < 0.5) - hsl.y = delta / (fmax + fmin); - else - hsl.y = delta / (2.0 - fmax - fmin); - - float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta; - float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta; - float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta; - - if (color.r == fmax ) - hsl.x = deltaB - deltaG; - else if (color.g == fmax) - hsl.x = (1.0 / 3.0) + deltaR - deltaB; - else if (color.b == fmax) - hsl.x = (2.0 / 3.0) + deltaG - deltaR; - - if (hsl.x < 0.0) - hsl.x += 1.0; - else if (hsl.x > 1.0) - hsl.x -= 1.0; - } - - return hsl; -} - -vec3 hsl2rgb(vec3 hsl) { - vec3 rgb; - - if (hsl.y == 0.0) - rgb = vec3(hsl.z); - else { - float f2; - - if (hsl.z < 0.5) - f2 = hsl.z * (1.0 + hsl.y); - else - f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z); - - float f1 = 2.0 * hsl.z - f2; - - rgb.r = hue2rgb(f1, f2, hsl.x + (1.0/3.0)); - rgb.g = hue2rgb(f1, f2, hsl.x); - rgb.b = hue2rgb(f1, f2, hsl.x - (1.0/3.0)); - } - - return rgb; -} - -vec3 blendHue(vec3 base, vec3 blend) { - vec3 baseHSL = rgb2hsl(base); - return hsl2rgb(vec3(rgb2hsl(blend).r, baseHSL.g, baseHSL.b)); -} - -void main() { - vec4 texture; - - %forloop% - - vec4 texel = vec4(outTint.bgr * outTint.a, outTint.a); - - // Multiply texture tint - vec4 color = texture * texel; - - if (outTintEffect == 1.0) { - // Solid color + texture alpha - color.rgb = mix(texture.rgb, outTint.bgr * outTint.a, texture.a); - } else if (outTintEffect == 2.0) { - // Solid color, no texture - color = texel; - } - - /* Apply day/night tint */ - if (color.a > 0.0 && ignoreTimeTint == 0) { - vec3 dayNightTint; - - if (time < 0.25) { - dayNightTint = dayTint; - } else if (isOutside == 0 && time < 0.5) { - dayNightTint = mix(dayTint, nightTint, (time - 0.25) / 0.25); - } else if (time < 0.375) { - dayNightTint = mix(dayTint, duskTint, (time - 0.25) / 0.125); - } else if (time < 0.5) { - dayNightTint = mix(duskTint, nightTint, (time - 0.375) / 0.125); - } else if (time < 0.75) { - dayNightTint = nightTint; - } else if (isOutside == 0) { - dayNightTint = mix(nightTint, dayTint, (time - 0.75) / 0.25); - } else if (time < 0.875) { - dayNightTint = mix(nightTint, duskTint, (time - 0.75) / 0.125); - } else { - dayNightTint = mix(duskTint, dayTint, (time - 0.875) / 0.125); - } - - color = vec4(blendHardLight(color.rgb, dayNightTint), color.a); - } - - if (terrainColorRatio > 0.0 && (1.0 - terrainColorRatio) < outTexCoord.y) { - if (color.a > 0.0 && (terrainColor.r > 0.0 || terrainColor.g > 0.0 || terrainColor.b > 0.0)) { - color.rgb = mix(color.rgb, blendHue(color.rgb, terrainColor), 1.0); - } - } - - gl_FragColor = color; -} -`; - -const spriteVertShader = ` -precision mediump float; - -uniform mat4 uProjectionMatrix; -uniform int uRoundPixels; -uniform vec2 uResolution; - -attribute vec2 inPosition; -attribute vec2 inTexCoord; -attribute float inTexId; -attribute float inTintEffect; -attribute vec4 inTint; - -varying vec2 outTexCoord; -varying float outTexId; -varying vec2 outPosition; -varying float outTintEffect; -varying vec4 outTint; - -void main() { - gl_Position = uProjectionMatrix * vec4(inPosition, 1.0, 1.0); - if (uRoundPixels == 1) - { - gl_Position.xy = floor(((gl_Position.xy + 1.0) * 0.5 * uResolution) + 0.5) / uResolution * 2.0 - 1.0; - } - outTexCoord = inTexCoord; - outTexId = inTexId; - outPosition = inPosition; - outTint = inTint; - outTintEffect = inTintEffect; -} -`; +import fieldSpriteFragShader from "./glsl/fieldSpriteFragShader.frag?raw"; +import spriteVertShader from "./glsl/spriteShader.vert?raw"; export default class FieldSpritePipeline extends Phaser.Renderer.WebGL.Pipelines.MultiPipeline { constructor(game: Phaser.Game, config?: Phaser.Types.Renderer.WebGL.WebGLPipelineConfig) { @@ -212,7 +10,7 @@ export default class FieldSpritePipeline extends Phaser.Renderer.WebGL.Pipelines config || { game: game, name: "field-sprite", - fragShader: spriteFragShader, + fragShader: fieldSpriteFragShader, vertShader: spriteVertShader, }, ); diff --git a/src/pipelines/glsl/fieldSpriteFragShader.frag b/src/pipelines/glsl/fieldSpriteFragShader.frag new file mode 100644 index 00000000000..e79dea86fe9 --- /dev/null +++ b/src/pipelines/glsl/fieldSpriteFragShader.frag @@ -0,0 +1,168 @@ +#ifdef GL_FRAGMENT_PRECISION_HIGH +precision highp float; +#else +precision mediump float; +#endif + +uniform sampler2D uMainSampler[%count%]; + +varying vec2 outTexCoord; +varying float outTexId; +varying float outTintEffect; +varying vec4 outTint; + +uniform float time; +uniform int ignoreTimeTint; +uniform int isOutside; +uniform vec3 dayTint; +uniform vec3 duskTint; +uniform vec3 nightTint; +uniform vec3 terrainColor; +uniform float terrainColorRatio; + +float blendOverlay(float base, float blend) { + return base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend)); +} + +vec3 blendOverlay(vec3 base, vec3 blend) { + return vec3(blendOverlay(base.r,blend.r),blendOverlay(base.g,blend.g),blendOverlay(base.b,blend.b)); +} + +vec3 blendHardLight(vec3 base, vec3 blend) { + return blendOverlay(blend, base); +} + +float hue2rgb(float f1, float f2, float hue) { + if (hue < 0.0) + hue += 1.0; + else if (hue > 1.0) + hue -= 1.0; + float res; + if ((6.0 * hue) < 1.0) + res = f1 + (f2 - f1) * 6.0 * hue; + else if ((2.0 * hue) < 1.0) + res = f2; + else if ((3.0 * hue) < 2.0) + res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0; + else + res = f1; + return res; +} + +vec3 rgb2hsl(vec3 color) { + vec3 hsl; + + float fmin = min(min(color.r, color.g), color.b); + float fmax = max(max(color.r, color.g), color.b); + float delta = fmax - fmin; + + hsl.z = (fmax + fmin) / 2.0; + + if (delta == 0.0) { + hsl.x = 0.0; + hsl.y = 0.0; + } else { + if (hsl.z < 0.5) + hsl.y = delta / (fmax + fmin); + else + hsl.y = delta / (2.0 - fmax - fmin); + + float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta; + float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta; + float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta; + + if (color.r == fmax ) + hsl.x = deltaB - deltaG; + else if (color.g == fmax) + hsl.x = (1.0 / 3.0) + deltaR - deltaB; + else if (color.b == fmax) + hsl.x = (2.0 / 3.0) + deltaG - deltaR; + + if (hsl.x < 0.0) + hsl.x += 1.0; + else if (hsl.x > 1.0) + hsl.x -= 1.0; + } + + return hsl; +} + +vec3 hsl2rgb(vec3 hsl) { + vec3 rgb; + + if (hsl.y == 0.0) + rgb = vec3(hsl.z); + else { + float f2; + + if (hsl.z < 0.5) + f2 = hsl.z * (1.0 + hsl.y); + else + f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z); + + float f1 = 2.0 * hsl.z - f2; + + rgb.r = hue2rgb(f1, f2, hsl.x + (1.0/3.0)); + rgb.g = hue2rgb(f1, f2, hsl.x); + rgb.b = hue2rgb(f1, f2, hsl.x - (1.0/3.0)); + } + + return rgb; +} + +vec3 blendHue(vec3 base, vec3 blend) { + vec3 baseHSL = rgb2hsl(base); + return hsl2rgb(vec3(rgb2hsl(blend).r, baseHSL.g, baseHSL.b)); +} + +void main() { + vec4 texture; + + %forloop% + + vec4 texel = vec4(outTint.bgr * outTint.a, outTint.a); + + // Multiply texture tint + vec4 color = texture * texel; + + if (outTintEffect == 1.0) { + // Solid color + texture alpha + color.rgb = mix(texture.rgb, outTint.bgr * outTint.a, texture.a); + } else if (outTintEffect == 2.0) { + // Solid color, no texture + color = texel; + } + + /* Apply day/night tint */ + if (color.a > 0.0 && ignoreTimeTint == 0) { + vec3 dayNightTint; + + if (time < 0.25) { + dayNightTint = dayTint; + } else if (isOutside == 0 && time < 0.5) { + dayNightTint = mix(dayTint, nightTint, (time - 0.25) / 0.25); + } else if (time < 0.375) { + dayNightTint = mix(dayTint, duskTint, (time - 0.25) / 0.125); + } else if (time < 0.5) { + dayNightTint = mix(duskTint, nightTint, (time - 0.375) / 0.125); + } else if (time < 0.75) { + dayNightTint = nightTint; + } else if (isOutside == 0) { + dayNightTint = mix(nightTint, dayTint, (time - 0.75) / 0.25); + } else if (time < 0.875) { + dayNightTint = mix(nightTint, duskTint, (time - 0.75) / 0.125); + } else { + dayNightTint = mix(duskTint, dayTint, (time - 0.875) / 0.125); + } + + color = vec4(blendHardLight(color.rgb, dayNightTint), color.a); + } + + if (terrainColorRatio > 0.0 && (1.0 - terrainColorRatio) < outTexCoord.y) { + if (color.a > 0.0 && (terrainColor.r > 0.0 || terrainColor.g > 0.0 || terrainColor.b > 0.0)) { + color.rgb = mix(color.rgb, blendHue(color.rgb, terrainColor), 1.0); + } + } + + gl_FragColor = color; +} \ No newline at end of file diff --git a/src/pipelines/glsl/invert.frag b/src/pipelines/glsl/invert.frag new file mode 100644 index 00000000000..24d9ee83a55 --- /dev/null +++ b/src/pipelines/glsl/invert.frag @@ -0,0 +1,10 @@ +precision mediump float; + +uniform sampler2D uMainSampler; + +varying vec2 outTexCoord; + +void main() +{ + gl_FragColor = 1.0 - texture2D(uMainSampler, outTexCoord); +} \ No newline at end of file diff --git a/src/pipelines/glsl/spriteFragShader.frag b/src/pipelines/glsl/spriteFragShader.frag new file mode 100644 index 00000000000..3765e595b70 --- /dev/null +++ b/src/pipelines/glsl/spriteFragShader.frag @@ -0,0 +1,279 @@ +#ifdef GL_FRAGMENT_PRECISION_HIGH +precision highp float; +#else +precision mediump float; +#endif + +uniform sampler2D uMainSampler[%count%]; + +varying vec2 outTexCoord; +varying float outTexId; +varying vec2 outPosition; +varying float outTintEffect; +varying vec4 outTint; + +uniform float time; +uniform int ignoreTimeTint; +uniform int isOutside; +uniform vec3 dayTint; +uniform vec3 duskTint; +uniform vec3 nightTint; +uniform float teraTime; +uniform vec3 teraColor; +uniform int hasShadow; +uniform int yCenter; +uniform float fieldScale; +uniform float vCutoff; +uniform vec2 relPosition; +uniform vec2 texFrameUv; +uniform vec2 size; +uniform vec2 texSize; +uniform float yOffset; +uniform float yShadowOffset; +uniform vec4 tone; +uniform ivec4 baseVariantColors[32]; +uniform vec4 variantColors[32]; +uniform ivec4 spriteColors[32]; +uniform ivec4 fusionSpriteColors[32]; + +const vec3 lumaF = vec3(.299, .587, .114); + +float blendOverlay(float base, float blend) { + return base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend)); +} + +vec3 blendOverlay(vec3 base, vec3 blend) { + return vec3(blendOverlay(base.r,blend.r),blendOverlay(base.g,blend.g),blendOverlay(base.b,blend.b)); +} + +vec3 blendHardLight(vec3 base, vec3 blend) { + return blendOverlay(blend, base); +} + +float hue2rgb(float f1, float f2, float hue) { + if (hue < 0.0) + hue += 1.0; + else if (hue > 1.0) + hue -= 1.0; + float res; + if ((6.0 * hue) < 1.0) + res = f1 + (f2 - f1) * 6.0 * hue; + else if ((2.0 * hue) < 1.0) + res = f2; + else if ((3.0 * hue) < 2.0) + res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0; + else + res = f1; + return res; +} + +vec3 rgb2hsl(vec3 color) { + vec3 hsl; + + float fmin = min(min(color.r, color.g), color.b); + float fmax = max(max(color.r, color.g), color.b); + float delta = fmax - fmin; + + hsl.z = (fmax + fmin) / 2.0; + + if (delta == 0.0) { + hsl.x = 0.0; + hsl.y = 0.0; + } else { + if (hsl.z < 0.5) + hsl.y = delta / (fmax + fmin); + else + hsl.y = delta / (2.0 - fmax - fmin); + + float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta; + float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta; + float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta; + + if (color.r == fmax ) + hsl.x = deltaB - deltaG; + else if (color.g == fmax) + hsl.x = (1.0 / 3.0) + deltaR - deltaB; + else if (color.b == fmax) + hsl.x = (2.0 / 3.0) + deltaG - deltaR; + + if (hsl.x < 0.0) + hsl.x += 1.0; + else if (hsl.x > 1.0) + hsl.x -= 1.0; + } + + return hsl; +} + +vec3 hsl2rgb(vec3 hsl) { + vec3 rgb; + + if (hsl.y == 0.0) + rgb = vec3(hsl.z); + else { + float f2; + + if (hsl.z < 0.5) + f2 = hsl.z * (1.0 + hsl.y); + else + f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z); + + float f1 = 2.0 * hsl.z - f2; + + rgb.r = hue2rgb(f1, f2, hsl.x + (1.0/3.0)); + rgb.g = hue2rgb(f1, f2, hsl.x); + rgb.b= hue2rgb(f1, f2, hsl.x - (1.0/3.0)); + } + + return rgb; +} + +vec3 blendHue(vec3 base, vec3 blend) { + vec3 baseHSL = rgb2hsl(base); + return hsl2rgb(vec3(rgb2hsl(blend).r, baseHSL.g, baseHSL.b)); +} + +vec3 rgb2hsv(vec3 c) { + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) { + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +void main() { + vec4 texture = texture2D(uMainSampler[0], outTexCoord); + + ivec4 colorInt = ivec4(texture*255.0); + + for (int i = 0; i < 32; i++) { + if (baseVariantColors[i][3] == 0) + break; + // abs value is broken in this version of gles with highp + ivec3 diffs = ivec3( + (colorInt.r > baseVariantColors[i].r) ? colorInt.r - baseVariantColors[i].r : baseVariantColors[i].r - colorInt.r, + (colorInt.g > baseVariantColors[i].g) ? colorInt.g - baseVariantColors[i].g : baseVariantColors[i].g - colorInt.g, + (colorInt.b > baseVariantColors[i].b) ? colorInt.b - baseVariantColors[i].b : baseVariantColors[i].b - colorInt.b + ); + // Set color threshold to be within 3 points for each channel + bvec3 threshold = lessThan(diffs, ivec3(3)); + + if (texture.a > 0.0 && all(threshold)) { + texture.rgb = variantColors[i].rgb; + break; + } + } + + for (int i = 0; i < 32; i++) { + if (spriteColors[i][3] == 0) + break; + if (texture.a > 0.0 && colorInt.r == spriteColors[i].r && colorInt.g == spriteColors[i].g && colorInt.b == spriteColors[i].b) { + vec3 fusionColor = vec3(float(fusionSpriteColors[i].r) / 255.0, float(fusionSpriteColors[i].g) / 255.0, float(fusionSpriteColors[i].b) / 255.0); + vec3 bg = vec3(spriteColors[i].rgb) / 255.0; + float gray = (bg.r + bg.g + bg.b) / 3.0; + bg = vec3(gray, gray, gray); + vec3 fg = fusionColor; + texture.rgb = mix(1.0 - 2.0 * (1.0 - bg) * (1.0 - fg), 2.0 * bg * fg, step(bg, vec3(0.5))); + break; + } + } + + vec4 texel = vec4(outTint.bgr * outTint.a, outTint.a); + + // Multiply texture tint + vec4 color = texture * texel; + + if (color.a > 0.0 && teraColor.r > 0.0 && teraColor.g > 0.0 && teraColor.b > 0.0) { + vec2 relUv = vec2((outTexCoord.x - texFrameUv.x) / (size.x / texSize.x), (outTexCoord.y - texFrameUv.y) / (size.y / texSize.y)); + vec2 teraTexCoord = vec2(relUv.x * (size.x / 200.0), relUv.y * (size.y / 120.0)); + vec4 teraCol = texture2D(uMainSampler[1], teraTexCoord); + float floorValue = 86.0 / 255.0; + vec3 teraPatternHsv = rgb2hsv(teraCol.rgb); + teraCol.rgb = hsv2rgb(vec3((teraPatternHsv.b - floorValue) * 4.0 + teraTexCoord.x * fieldScale / 2.0 + teraTexCoord.y * fieldScale / 2.0 + teraTime * 255.0, teraPatternHsv.b, teraPatternHsv.b)); + + color.rgb = mix(color.rgb, blendHue(color.rgb, teraColor), 0.625); + teraCol.rgb = mix(teraCol.rgb, teraColor, 0.5); + color.rgb = blendOverlay(color.rgb, teraCol.rgb); + + if (any(lessThan(teraCol.rgb, vec3(1.0)))) { + vec3 teraColHsv = rgb2hsv(teraColor); + color.rgb = mix(color.rgb, teraColor, (1.0 - teraColHsv.g) / 2.0); + } + } + + if (outTintEffect == 1.0) { + // Solid color + texture alpha + color.rgb = mix(texture.rgb, outTint.bgr * outTint.a, texture.a); + } else if (outTintEffect == 2.0) { + // Solid color, no texture + color = texel; + } + + /* Apply gray */ + float luma = dot(color.rgb, lumaF); + color.rgb = mix(color.rgb, vec3(luma), tone.w); + + /* Apply tone */ + color.rgb += tone.rgb * (color.a / 255.0); + + /* Apply day/night tint */ + if (color.a > 0.0 && ignoreTimeTint == 0) { + vec3 dayNightTint; + + if (time < 0.25) { + dayNightTint = dayTint; + } else if (isOutside == 0 && time < 0.5) { + dayNightTint = mix(dayTint, nightTint, (time - 0.25) / 0.25); + } else if (time < 0.375) { + dayNightTint = mix(dayTint, duskTint, (time - 0.25) / 0.125); + } else if (time < 0.5) { + dayNightTint = mix(duskTint, nightTint, (time - 0.375) / 0.125); + } else if (time < 0.75) { + dayNightTint = nightTint; + } else if (isOutside == 0) { + dayNightTint = mix(nightTint, dayTint, (time - 0.75) / 0.25); + } else if (time < 0.875) { + dayNightTint = mix(nightTint, duskTint, (time - 0.75) / 0.125); + } else { + dayNightTint = mix(duskTint, dayTint, (time - 0.875) / 0.125); + } + + color.rgb = blendHardLight(color.rgb, dayNightTint); + } + + if (hasShadow == 1) { + float width = size.x - (yOffset / 2.0); + + float spriteX = ((floor(outPosition.x / fieldScale) - relPosition.x) / width) + 0.5; + float spriteY = ((floor(outPosition.y / fieldScale) - relPosition.y - yShadowOffset) / size.y); + + if (yCenter == 1) { + spriteY += 0.5; + } else { + spriteY += 1.0; + } + + bool yOverflow = outTexCoord.y >= vCutoff; + + if ((spriteY >= 0.9 && (color.a == 0.0 || yOverflow))) { + float shadowSpriteY = (spriteY - 0.9) * (1.0 / 0.15); + if (distance(vec2(spriteX, shadowSpriteY), vec2(0.5, 0.5)) < 0.5) { + color = vec4(vec3(0.0, 0.0, 0.0), 0.5); + } else if (yOverflow) { + discard; + } + } else if (yOverflow) { + discard; + } + } + + gl_FragColor = color; +} \ No newline at end of file diff --git a/src/pipelines/glsl/spriteShader.vert b/src/pipelines/glsl/spriteShader.vert new file mode 100644 index 00000000000..33743384b47 --- /dev/null +++ b/src/pipelines/glsl/spriteShader.vert @@ -0,0 +1,32 @@ +precision mediump float; + +uniform mat4 uProjectionMatrix; +uniform int uRoundPixels; +uniform vec2 uResolution; + +attribute vec2 inPosition; +attribute vec2 inTexCoord; +attribute float inTexId; +attribute float inTintEffect; +attribute vec4 inTint; + +varying vec2 outTexCoord; +varying vec2 outtexFrameUv; +varying float outTexId; +varying vec2 outPosition; +varying float outTintEffect; +varying vec4 outTint; + +void main() +{ + gl_Position = uProjectionMatrix * vec4(inPosition, 1.0, 1.0); + if (uRoundPixels == 1) + { + gl_Position.xy = floor(((gl_Position.xy + 1.0) * 0.5 * uResolution) + 0.5) / uResolution * 2.0 - 1.0; + } + outTexCoord = inTexCoord; + outTexId = inTexId; + outPosition = inPosition; + outTint = inTint; + outTintEffect = inTintEffect; +} \ No newline at end of file diff --git a/src/pipelines/invert.ts b/src/pipelines/invert.ts index a945d0c95aa..0ebc3ad865f 100644 --- a/src/pipelines/invert.ts +++ b/src/pipelines/invert.ts @@ -1,17 +1,5 @@ import type { Game } from "phaser"; - -const fragShader = ` -precision mediump float; - -uniform sampler2D uMainSampler; - -varying vec2 outTexCoord; - -void main() -{ - gl_FragColor = 1.0 - texture2D(uMainSampler, outTexCoord); -} -`; +import fragShader from "./glsl/invert.frag?raw"; export default class InvertPostFX extends Phaser.Renderer.WebGL.Pipelines.PostFXPipeline { constructor(game: Game) { diff --git a/src/pipelines/sprite.ts b/src/pipelines/sprite.ts index 439e35f711f..acbaac50476 100644 --- a/src/pipelines/sprite.ts +++ b/src/pipelines/sprite.ts @@ -1,318 +1,12 @@ -import { variantColorCache } from "#app/data/variant"; +import { variantColorCache } from "#app/sprites/variant"; import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro"; import Pokemon from "#app/field/pokemon"; import Trainer from "#app/field/trainer"; import { globalScene } from "#app/global-scene"; import * as Utils from "#app/utils"; import FieldSpritePipeline from "./field-sprite"; - -const spriteFragShader = ` -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif - -uniform sampler2D uMainSampler[%count%]; - -varying vec2 outTexCoord; -varying float outTexId; -varying vec2 outPosition; -varying float outTintEffect; -varying vec4 outTint; - -uniform float time; -uniform int ignoreTimeTint; -uniform int isOutside; -uniform vec3 dayTint; -uniform vec3 duskTint; -uniform vec3 nightTint; -uniform float teraTime; -uniform vec3 teraColor; -uniform int hasShadow; -uniform int yCenter; -uniform float fieldScale; -uniform float vCutoff; -uniform vec2 relPosition; -uniform vec2 texFrameUv; -uniform vec2 size; -uniform vec2 texSize; -uniform float yOffset; -uniform float yShadowOffset; -uniform vec4 tone; -uniform ivec4 baseVariantColors[32]; -uniform vec4 variantColors[32]; -uniform ivec4 spriteColors[32]; -uniform ivec4 fusionSpriteColors[32]; - -const vec3 lumaF = vec3(.299, .587, .114); - -float blendOverlay(float base, float blend) { - return base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend)); -} - -vec3 blendOverlay(vec3 base, vec3 blend) { - return vec3(blendOverlay(base.r,blend.r),blendOverlay(base.g,blend.g),blendOverlay(base.b,blend.b)); -} - -vec3 blendHardLight(vec3 base, vec3 blend) { - return blendOverlay(blend, base); -} - -float hue2rgb(float f1, float f2, float hue) { - if (hue < 0.0) - hue += 1.0; - else if (hue > 1.0) - hue -= 1.0; - float res; - if ((6.0 * hue) < 1.0) - res = f1 + (f2 - f1) * 6.0 * hue; - else if ((2.0 * hue) < 1.0) - res = f2; - else if ((3.0 * hue) < 2.0) - res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0; - else - res = f1; - return res; -} - -vec3 rgb2hsl(vec3 color) { - vec3 hsl; - - float fmin = min(min(color.r, color.g), color.b); - float fmax = max(max(color.r, color.g), color.b); - float delta = fmax - fmin; - - hsl.z = (fmax + fmin) / 2.0; - - if (delta == 0.0) { - hsl.x = 0.0; - hsl.y = 0.0; - } else { - if (hsl.z < 0.5) - hsl.y = delta / (fmax + fmin); - else - hsl.y = delta / (2.0 - fmax - fmin); - - float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta; - float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta; - float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta; - - if (color.r == fmax ) - hsl.x = deltaB - deltaG; - else if (color.g == fmax) - hsl.x = (1.0 / 3.0) + deltaR - deltaB; - else if (color.b == fmax) - hsl.x = (2.0 / 3.0) + deltaG - deltaR; - - if (hsl.x < 0.0) - hsl.x += 1.0; - else if (hsl.x > 1.0) - hsl.x -= 1.0; - } - - return hsl; -} - -vec3 hsl2rgb(vec3 hsl) { - vec3 rgb; - - if (hsl.y == 0.0) - rgb = vec3(hsl.z); - else { - float f2; - - if (hsl.z < 0.5) - f2 = hsl.z * (1.0 + hsl.y); - else - f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z); - - float f1 = 2.0 * hsl.z - f2; - - rgb.r = hue2rgb(f1, f2, hsl.x + (1.0/3.0)); - rgb.g = hue2rgb(f1, f2, hsl.x); - rgb.b= hue2rgb(f1, f2, hsl.x - (1.0/3.0)); - } - - return rgb; -} - -vec3 blendHue(vec3 base, vec3 blend) { - vec3 baseHSL = rgb2hsl(base); - return hsl2rgb(vec3(rgb2hsl(blend).r, baseHSL.g, baseHSL.b)); -} - -vec3 rgb2hsv(vec3 c) { - vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); - vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); - vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); - - float d = q.x - min(q.w, q.y); - float e = 1.0e-10; - return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); -} - -vec3 hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); -} - -void main() { - vec4 texture = texture2D(uMainSampler[0], outTexCoord); - - ivec4 colorInt = ivec4(int(texture.r * 255.0), int(texture.g * 255.0), int(texture.b * 255.0), int(texture.a * 255.0)); - - for (int i = 0; i < 32; i++) { - if (baseVariantColors[i][3] == 0) - break; - if (texture.a > 0.0 && colorInt.r == baseVariantColors[i].r && colorInt.g == baseVariantColors[i].g && colorInt.b == baseVariantColors[i].b) { - texture.rgb = variantColors[i].rgb; - break; - } - } - - for (int i = 0; i < 32; i++) { - if (spriteColors[i][3] == 0) - break; - if (texture.a > 0.0 && colorInt.r == spriteColors[i].r && colorInt.g == spriteColors[i].g && colorInt.b == spriteColors[i].b) { - vec3 fusionColor = vec3(float(fusionSpriteColors[i].r) / 255.0, float(fusionSpriteColors[i].g) / 255.0, float(fusionSpriteColors[i].b) / 255.0); - vec3 bg = vec3(float(spriteColors[i].r) / 255.0, float(spriteColors[i].g) / 255.0, float(spriteColors[i].b) / 255.0); - float gray = (bg.r + bg.g + bg.b) / 3.0; - bg = vec3(gray, gray, gray); - vec3 fg = fusionColor; - texture.rgb = mix(1.0 - 2.0 * (1.0 - bg) * (1.0 - fg), 2.0 * bg * fg, step(bg, vec3(0.5))); - break; - } - } - - vec4 texel = vec4(outTint.bgr * outTint.a, outTint.a); - - // Multiply texture tint - vec4 color = texture * texel; - - if (color.a > 0.0 && teraColor.r > 0.0 && teraColor.g > 0.0 && teraColor.b > 0.0) { - vec2 relUv = vec2((outTexCoord.x - texFrameUv.x) / (size.x / texSize.x), (outTexCoord.y - texFrameUv.y) / (size.y / texSize.y)); - vec2 teraTexCoord = vec2(relUv.x * (size.x / 200.0), relUv.y * (size.y / 120.0)); - vec4 teraCol = texture2D(uMainSampler[1], teraTexCoord); - float floorValue = 86.0 / 255.0; - vec3 teraPatternHsv = rgb2hsv(teraCol.rgb); - teraCol.rgb = hsv2rgb(vec3((teraPatternHsv.b - floorValue) * 4.0 + teraTexCoord.x * fieldScale / 2.0 + teraTexCoord.y * fieldScale / 2.0 + teraTime * 255.0, teraPatternHsv.b, teraPatternHsv.b)); - - color.rgb = mix(color.rgb, blendHue(color.rgb, teraColor), 0.625); - teraCol.rgb = mix(teraCol.rgb, teraColor, 0.5); - color.rgb = blendOverlay(color.rgb, teraCol.rgb); - - if (teraColor.r < 1.0 || teraColor.g < 1.0 || teraColor.b < 1.0) { - vec3 teraColHsv = rgb2hsv(teraColor); - color.rgb = mix(color.rgb, teraColor, (1.0 - teraColHsv.g) / 2.0); - } - } - - if (outTintEffect == 1.0) { - // Solid color + texture alpha - color.rgb = mix(texture.rgb, outTint.bgr * outTint.a, texture.a); - } else if (outTintEffect == 2.0) { - // Solid color, no texture - color = texel; - } - - /* Apply gray */ - float luma = dot(color.rgb, lumaF); - color.rgb = mix(color.rgb, vec3(luma), tone.w); - - /* Apply tone */ - color.rgb += tone.rgb * (color.a / 255.0); - - /* Apply day/night tint */ - if (color.a > 0.0 && ignoreTimeTint == 0) { - vec3 dayNightTint; - - if (time < 0.25) { - dayNightTint = dayTint; - } else if (isOutside == 0 && time < 0.5) { - dayNightTint = mix(dayTint, nightTint, (time - 0.25) / 0.25); - } else if (time < 0.375) { - dayNightTint = mix(dayTint, duskTint, (time - 0.25) / 0.125); - } else if (time < 0.5) { - dayNightTint = mix(duskTint, nightTint, (time - 0.375) / 0.125); - } else if (time < 0.75) { - dayNightTint = nightTint; - } else if (isOutside == 0) { - dayNightTint = mix(nightTint, dayTint, (time - 0.75) / 0.25); - } else if (time < 0.875) { - dayNightTint = mix(nightTint, duskTint, (time - 0.75) / 0.125); - } else { - dayNightTint = mix(duskTint, dayTint, (time - 0.875) / 0.125); - } - - color.rgb = blendHardLight(color.rgb, dayNightTint); - } - - if (hasShadow == 1) { - float width = size.x - (yOffset / 2.0); - - float spriteX = ((floor(outPosition.x / fieldScale) - relPosition.x) / width) + 0.5; - float spriteY = ((floor(outPosition.y / fieldScale) - relPosition.y - yShadowOffset) / size.y); - - if (yCenter == 1) { - spriteY += 0.5; - } else { - spriteY += 1.0; - } - - bool yOverflow = outTexCoord.y >= vCutoff; - - if ((spriteY >= 0.9 && (color.a == 0.0 || yOverflow))) { - float shadowSpriteY = (spriteY - 0.9) * (1.0 / 0.15); - if (distance(vec2(spriteX, shadowSpriteY), vec2(0.5, 0.5)) < 0.5) { - color = vec4(vec3(0.0, 0.0, 0.0), 0.5); - } else if (yOverflow) { - discard; - } - } else if (yOverflow) { - discard; - } - } - - gl_FragColor = color; -} -`; - -const spriteVertShader = ` -precision mediump float; - -uniform mat4 uProjectionMatrix; -uniform int uRoundPixels; -uniform vec2 uResolution; - -attribute vec2 inPosition; -attribute vec2 inTexCoord; -attribute float inTexId; -attribute float inTintEffect; -attribute vec4 inTint; - -varying vec2 outTexCoord; -varying vec2 outtexFrameUv; -varying float outTexId; -varying vec2 outPosition; -varying float outTintEffect; -varying vec4 outTint; - -void main() -{ - gl_Position = uProjectionMatrix * vec4(inPosition, 1.0, 1.0); - if (uRoundPixels == 1) - { - gl_Position.xy = floor(((gl_Position.xy + 1.0) * 0.5 * uResolution) + 0.5) / uResolution * 2.0 - 1.0; - } - outTexCoord = inTexCoord; - outTexId = inTexId; - outPosition = inPosition; - outTint = inTint; - outTintEffect = inTintEffect; -} -`; +import spriteFragShader from "./glsl/spriteFragShader.frag?raw"; +import spriteVertShader from "./glsl/spriteShader.vert?raw"; export default class SpritePipeline extends FieldSpritePipeline { private _tone: number[]; diff --git a/src/sprites/pokemon-asset-loader.ts b/src/sprites/pokemon-asset-loader.ts new file mode 100644 index 00000000000..4ce88f4f1fb --- /dev/null +++ b/src/sprites/pokemon-asset-loader.ts @@ -0,0 +1,11 @@ +import type { Moves } from "#enums/moves"; +import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; + +/** + * Asynchronously load the animations and assets for the provided moves. + * @param moveIds - An array of move IDs to load assets for. + */ +export async function loadMoveAnimations(moveIds: Moves[]): Promise { + await Promise.allSettled(moveIds.map(m => initMoveAnim(m))); + await loadMoveAnimAssets(moveIds); +} diff --git a/src/sprites/pokemon-sprite.ts b/src/sprites/pokemon-sprite.ts new file mode 100644 index 00000000000..66432f5a4ea --- /dev/null +++ b/src/sprites/pokemon-sprite.ts @@ -0,0 +1,79 @@ +import { globalScene } from "#app/global-scene"; +import { variantColorCache, variantData } from "#app/sprites/variant"; +import { Gender } from "#app/data/gender"; +import { hasExpSprite } from "./sprite-utils"; +import type { Variant, VariantSet } from "#app/sprites/variant"; +import type Pokemon from "#app/field/pokemon"; +import type BattleScene from "#app/battle-scene"; + +// Regex patterns + +/** Regex matching double underscores */ +const DUNDER_REGEX = /\_{2}/g; + +/** + * Calculate the sprite ID from a pokemon form. + */ +export function getSpriteId(pokemon: Pokemon, ignoreOverride?: boolean): string { + return pokemon + .getSpeciesForm(ignoreOverride) + .getSpriteId( + pokemon.getGender(ignoreOverride) === Gender.FEMALE, + pokemon.formIndex, + pokemon.shiny, + pokemon.variant, + ); +} + +export function getBattleSpriteId(pokemon: Pokemon, back?: boolean, ignoreOverride = false): string { + if (back === undefined) { + back = pokemon.isPlayer(); + } + return pokemon + .getSpeciesForm(ignoreOverride) + .getSpriteId( + pokemon.getGender(ignoreOverride) === Gender.FEMALE, + pokemon.formIndex, + pokemon.shiny, + pokemon.variant, + back, + ); +} + +/** Compute the path to the sprite atlas by converting double underscores to path components (/) + */ +export function getSpriteAtlasPath(pokemon: Pokemon, ignoreOverride = false): string { + const spriteId = getSpriteId(pokemon, ignoreOverride).replace(DUNDER_REGEX, "/"); + return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; +} + +/** + * Load the variant assets for the given sprite and store it in {@linkcode variantColorCache}. + * @param spriteKey - The key of the sprite to load + * @param fileRoot - The root path of the sprite file + * @param variant - The variant to load + * @param scene - The scene to load the assets in (defaults to the global scene) + */ +export async function loadPokemonVariantAssets( + spriteKey: string, + fileRoot: string, + variant: Variant, + scene: BattleScene = globalScene, +): Promise { + if (variantColorCache.hasOwnProperty(spriteKey)) { + return; + } + const useExpSprite = scene.experimentalSprites && hasExpSprite(spriteKey); + if (useExpSprite) { + fileRoot = `exp/${fileRoot}`; + } + let variantConfig = variantData; + fileRoot.split("/").map(p => (variantConfig ? (variantConfig = variantConfig[p]) : null)); + const variantSet = variantConfig as VariantSet; + if (!variantConfig || variantSet[variant] !== 1) { + return; + } + variantColorCache[spriteKey] = await scene + .cachedFetch(`./images/pokemon/variant/${fileRoot}.json`) + .then(res => res.json()); +} diff --git a/src/sprites/sprite-keys.ts b/src/sprites/sprite-keys.ts new file mode 100644 index 00000000000..f023df089f6 --- /dev/null +++ b/src/sprites/sprite-keys.ts @@ -0,0 +1 @@ +export const expSpriteKeys: Set = new Set(); diff --git a/src/sprites/sprite-utils.ts b/src/sprites/sprite-utils.ts new file mode 100644 index 00000000000..8a352de3d55 --- /dev/null +++ b/src/sprites/sprite-utils.ts @@ -0,0 +1,28 @@ +import { expSpriteKeys } from "#app/sprites/sprite-keys"; + +const expKeyRegex = /^pkmn__?(back__)?(shiny__)?(female__)?(\d+)(\-.*?)?(?:_[1-3])?$/; + +export function hasExpSprite(key: string): boolean { + const keyMatch = expKeyRegex.exec(key); + if (!keyMatch) { + return false; + } + + let k = keyMatch[4]!; + if (keyMatch[2]) { + k += "s"; + } + if (keyMatch[1]) { + k += "b"; + } + if (keyMatch[3]) { + k += "f"; + } + if (keyMatch[5]) { + k += keyMatch[5]; + } + if (!expSpriteKeys.has(k)) { + return false; + } + return true; +} diff --git a/src/sprites/variant.ts b/src/sprites/variant.ts new file mode 100644 index 00000000000..7552f63b778 --- /dev/null +++ b/src/sprites/variant.ts @@ -0,0 +1,145 @@ +import { VariantTier } from "#app/enums/variant-tier"; +import { hasExpSprite } from "#app/sprites/sprite-utils"; +import { globalScene } from "#app/global-scene"; +import type Pokemon from "#app/field/pokemon"; +import { isNullOrUndefined } from "#app/utils"; + +export type Variant = 0 | 1 | 2; + +export type VariantSet = [Variant, Variant, Variant]; + +export const variantData: any = {}; + +/** Caches variant colors that have been generated */ +export const variantColorCache = {}; + +export function getVariantTint(variant: Variant): number { + switch (variant) { + case 0: + return 0xf8c020; + case 1: + return 0x20f8f0; + case 2: + return 0xe81048; + } +} + +export function getVariantIcon(variant: Variant): number { + switch (variant) { + case 0: + return VariantTier.STANDARD; + case 1: + return VariantTier.RARE; + case 2: + return VariantTier.EPIC; + } +} + +/** Delete all of the keys in variantData */ +export function clearVariantData(): void { + for (const key in variantData) { + delete variantData[key]; + } +} + +/** Update the variant data to use experiment sprite files for variants that have experimental sprites. */ +export async function mergeExperimentalData(mainData: any, expData: any): Promise { + if (!expData) { + return; + } + + for (const key of Object.keys(expData)) { + if (typeof expData[key] === "object" && !Array.isArray(expData[key])) { + // If the value is an object, recursively merge. + if (!mainData[key]) { + mainData[key] = {}; + } + mergeExperimentalData(mainData[key], expData[key]); + } else { + // Otherwise, replace the value + mainData[key] = expData[key]; + } + } +} + +/** + * Populate the variant color cache with the variant colors for this pokemon. + * The global scene must be initialized before this function is called. + */ +export async function populateVariantColors( + pokemon: Pokemon, + isBackSprite = false, + ignoreOverride = true, +): Promise { + const battleSpritePath = pokemon + .getBattleSpriteAtlasPath(isBackSprite, ignoreOverride) + .replace("variant/", "") + .replace(/_[1-3]$/, ""); + let config = variantData; + const useExpSprite = + globalScene.experimentalSprites && hasExpSprite(pokemon.getBattleSpriteKey(isBackSprite, ignoreOverride)); + battleSpritePath.split("/").map(p => (config ? (config = config[p]) : null)); + const variantSet: VariantSet = config as VariantSet; + if (!variantSet || variantSet[pokemon.variant] !== 1) { + return; + } + const cacheKey = pokemon.getBattleSpriteKey(isBackSprite); + if (!variantColorCache.hasOwnProperty(cacheKey)) { + await populateVariantColorCache(cacheKey, useExpSprite, battleSpritePath); + } +} + +/** + * Gracefully handle errors loading a variant sprite. Log if it fails and attempt to fall back on + * non-experimental sprites before giving up. + * + * @param cacheKey - The cache key for the variant color sprite + * @param attemptedSpritePath - The sprite path that failed to load + * @param useExpSprite - Was the attempted sprite experimental + * @param battleSpritePath - The filename of the sprite + * @param optionalParams - Any additional params to log + */ +async function fallbackVariantColor( + cacheKey: string, + attemptedSpritePath: string, + useExpSprite: boolean, + battleSpritePath: string, + ...optionalParams: any[] +): Promise { + console.warn(`Could not load ${attemptedSpritePath}!`, ...optionalParams); + if (useExpSprite) { + await populateVariantColorCache(cacheKey, false, battleSpritePath); + } +} + +/** + * Fetch a variant color sprite from the key and store it in the variant color cache. + * + * @param cacheKey - The cache key for the variant color sprite + * @param useExpSprite - Should the experimental sprite be used + * @param battleSpritePath - The filename of the sprite + */ +export async function populateVariantColorCache( + cacheKey: string, + useExpSprite: boolean, + battleSpritePath: string, +): Promise { + const spritePath = `./images/pokemon/variant/${useExpSprite ? "exp/" : ""}${battleSpritePath}.json`; + return globalScene + .cachedFetch(spritePath) + .then(res => { + // Prevent the JSON from processing if it failed to load + if (!res.ok) { + return fallbackVariantColor(cacheKey, res.url, useExpSprite, battleSpritePath, res.status, res.statusText); + } + return res.json(); + }) + .catch(error => { + return fallbackVariantColor(cacheKey, spritePath, useExpSprite, battleSpritePath, error); + }) + .then(c => { + if (!isNullOrUndefined(c)) { + variantColorCache[cacheKey] = c; + } + }); +} diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 391ceec503d..061a6d3a194 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -32,7 +32,7 @@ import { Tutorial } from "#app/tutorial"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { allMoves } from "#app/data/moves/move"; import { TrainerVariant } from "#app/field/trainer"; -import type { Variant } from "#app/data/variant"; +import type { Variant } from "#app/sprites/variant"; import { setSettingGamepad, SettingGamepad, settingGamepadDefaults } from "#app/system/settings/settings-gamepad"; import type { SettingKeyboard } from "#app/system/settings/settings-keyboard"; import { setSettingKeyboard } from "#app/system/settings/settings-keyboard"; diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 957d43797a1..7cdcb0c72c3 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -7,7 +7,7 @@ import { getPokemonSpecies, getPokemonSpeciesForm } from "../data/pokemon-specie import { Status } from "../data/status-effect"; import Pokemon, { EnemyPokemon, PokemonMove, PokemonSummonData } from "../field/pokemon"; import { TrainerSlot } from "#enums/trainer-slot"; -import type { Variant } from "#app/data/variant"; +import type { Variant } from "#app/sprites/variant"; import { loadBattlerTag } from "../data/battler-tags"; import type { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index 355ab9167a1..ab006269d4e 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -7,7 +7,7 @@ import { StatusEffect } from "#enums/status-effect"; import { globalScene } from "#app/global-scene"; import { getTypeRgb } from "#app/data/type"; import { PokemonType } from "#enums/pokemon-type"; -import { getVariantTint } from "#app/data/variant"; +import { getVariantTint } from "#app/sprites/variant"; import { Stat } from "#enums/stat"; import BattleFlyout from "./battle-flyout"; import { WindowVariant, addWindow } from "./ui-theme"; diff --git a/src/ui/hatched-pokemon-container.ts b/src/ui/hatched-pokemon-container.ts index 0b283c2e063..9d1c13e19d5 100644 --- a/src/ui/hatched-pokemon-container.ts +++ b/src/ui/hatched-pokemon-container.ts @@ -1,6 +1,6 @@ import type { EggHatchData } from "#app/data/egg-hatch-data"; import { Gender } from "#app/data/gender"; -import { getVariantTint } from "#app/data/variant"; +import { getVariantTint } from "#app/sprites/variant"; import { DexAttr } from "#app/system/game-data"; import { globalScene } from "#app/global-scene"; import type PokemonSpecies from "#app/data/pokemon-species"; diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index caddd64cd28..ebaccc515c1 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -18,7 +18,7 @@ import PokemonIconAnimHandler, { PokemonIconAnimMode } from "#app/ui/pokemon-ico import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { addWindow } from "#app/ui/ui-theme"; import { SpeciesFormChangeItemTrigger, FormChangeItem } from "#app/data/pokemon-forms"; -import { getVariantTint } from "#app/data/variant"; +import { getVariantTint } from "#app/sprites/variant"; import { Button } from "#enums/buttons"; import { applyChallenges, ChallengeType } from "#app/data/challenge"; import MoveInfoOverlay from "#app/ui/move-info-overlay"; diff --git a/src/ui/pokedex-mon-container.ts b/src/ui/pokedex-mon-container.ts index e61da86e95e..410effda40d 100644 --- a/src/ui/pokedex-mon-container.ts +++ b/src/ui/pokedex-mon-container.ts @@ -1,4 +1,4 @@ -import type { Variant } from "#app/data/variant"; +import type { Variant } from "#app/sprites/variant"; import { globalScene } from "#app/global-scene"; import { isNullOrUndefined } from "#app/utils"; import type PokemonSpecies from "../data/pokemon-species"; diff --git a/src/ui/pokedex-page-ui-handler.ts b/src/ui/pokedex-page-ui-handler.ts index 062b4c3797c..eede346f052 100644 --- a/src/ui/pokedex-page-ui-handler.ts +++ b/src/ui/pokedex-page-ui-handler.ts @@ -1,7 +1,7 @@ import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; import { pokemonEvolutions, pokemonPrevolutions, pokemonStarters } from "#app/data/balance/pokemon-evolutions"; -import type { Variant } from "#app/data/variant"; -import { getVariantTint, getVariantIcon } from "#app/data/variant"; +import type { Variant } from "#app/sprites/variant"; +import { getVariantTint, getVariantIcon } from "#app/sprites/variant"; import { argbFromRgba } from "@material/material-color-utilities"; import i18next from "i18next"; import { starterColors } from "#app/battle-scene"; diff --git a/src/ui/pokedex-ui-handler.ts b/src/ui/pokedex-ui-handler.ts index 230b1bcb42b..59b06d476a2 100644 --- a/src/ui/pokedex-ui-handler.ts +++ b/src/ui/pokedex-ui-handler.ts @@ -1,5 +1,5 @@ -import type { Variant } from "#app/data/variant"; -import { getVariantTint, getVariantIcon } from "#app/data/variant"; +import type { Variant } from "#app/sprites/variant"; +import { getVariantTint, getVariantIcon } from "#app/sprites/variant"; import { argbFromRgba } from "@material/material-color-utilities"; import i18next from "i18next"; import { starterColors } from "#app/battle-scene"; diff --git a/src/ui/pokemon-info-container.ts b/src/ui/pokemon-info-container.ts index 56201f38748..1c880f6aec9 100644 --- a/src/ui/pokemon-info-container.ts +++ b/src/ui/pokemon-info-container.ts @@ -1,4 +1,4 @@ -import { getVariantTint } from "#app/data/variant"; +import { getVariantTint } from "#app/sprites/variant"; import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { globalScene } from "#app/global-scene"; import { Gender, getGenderColor, getGenderSymbol } from "../data/gender"; diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index 364cb8e4003..8719950381a 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -18,7 +18,7 @@ import { getTypeRgb } from "#app/data/type"; import { PokemonType } from "#enums/pokemon-type"; import { TypeColor, TypeShadow } from "#app/enums/color"; import { getNatureStatMultiplier, getNatureName } from "../data/nature"; -import { getVariantTint } from "#app/data/variant"; +import { getVariantTint } from "#app/sprites/variant"; import * as Modifier from "../modifier/modifier"; import type { Species } from "#enums/species"; import { PlayerGender } from "#enums/player-gender"; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 1e84b367791..3876f2585db 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -1,8 +1,8 @@ import type { CandyUpgradeNotificationChangedEvent } from "#app/events/battle-scene"; import { BattleSceneEventType } from "#app/events/battle-scene"; import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; -import type { Variant } from "#app/data/variant"; -import { getVariantTint, getVariantIcon } from "#app/data/variant"; +import type { Variant } from "#app/sprites/variant"; +import { getVariantTint, getVariantIcon } from "#app/sprites/variant"; import { argbFromRgba } from "@material/material-color-utilities"; import i18next from "i18next"; import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 9b209ded57a..aa3d014bd95 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -19,8 +19,8 @@ import { StatusEffect } from "#enums/status-effect"; import { getBiomeName } from "#app/data/balance/biomes"; import { getNatureName, getNatureStatMultiplier } from "#app/data/nature"; import { loggedInUser } from "#app/account"; -import type { Variant } from "#app/data/variant"; -import { getVariantTint } from "#app/data/variant"; +import type { Variant } from "#app/sprites/variant"; +import { getVariantTint } from "#app/sprites/variant"; import { Button } from "#enums/buttons"; import type { Ability } from "#app/data/ability"; import i18next from "i18next"; diff --git a/src/utils.ts b/src/utils.ts index 4092b68b405..2f05e2724ff 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -613,3 +613,25 @@ export function animationFileName(move: Moves): string { export function camelCaseToKebabCase(str: string): string { return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, (s, o) => (o ? "-" : "") + s.toLowerCase()); } + +/** + * Merges the two objects, such that for each property in `b` that matches a property in `a`, + * the value in `a` is replaced by the value in `b`. This is done recursively if the property is a non-array object + * + * If the property does not exist in `a` or its `typeof` evaluates differently, the property is skipped. + * If the value of the property is an array, the array is replaced. If it is any other object, the object is merged recursively. + */ +// biome-ignore lint/complexity/noBannedTypes: This function is designed to merge json objects +export function deepMergeObjects(a: Object, b: Object) { + for (const key in b) { + // !(key in a) is redundant here, yet makes it clear that we're explicitly interested in properties that exist in `a` + if (!(key in a) || typeof a[key] !== typeof b[key]) { + continue; + } + if (typeof b[key] === "object" && !Array.isArray(b[key])) { + deepMergeObjects(a[key], b[key]); + } else { + a[key] = b[key]; + } + } +} diff --git a/test/sprites/pokemonSprite.test.ts b/test/sprites/pokemonSprite.test.ts index 5bd08a58cda..a008b75b42e 100644 --- a/test/sprites/pokemonSprite.test.ts +++ b/test/sprites/pokemonSprite.test.ts @@ -3,8 +3,10 @@ import fs from "fs"; import path from "path"; import { beforeAll, describe, expect, it } from "vitest"; import _masterlist from "../../public/images/pokemon/variant/_masterlist.json"; +import _exp_masterlist from "../../public/images/pokemon/variant/_exp_masterlist.json"; type PokemonVariantMasterlist = typeof _masterlist; +type PokemonExpVariantMasterlist = typeof _exp_masterlist; const deepCopy = (data: any) => { return JSON.parse(JSON.stringify(data)); @@ -12,7 +14,7 @@ const deepCopy = (data: any) => { describe("check if every variant's sprite are correctly set", () => { let masterlist: PokemonVariantMasterlist; - let expVariant: PokemonVariantMasterlist["exp"]; + let expVariant: PokemonExpVariantMasterlist; let femaleVariant: PokemonVariantMasterlist["female"]; let backVariant: PokemonVariantMasterlist["back"]; let rootDir: string; @@ -20,13 +22,12 @@ describe("check if every variant's sprite are correctly set", () => { beforeAll(() => { rootDir = `${getAppRootDir()}${path.sep}public${path.sep}images${path.sep}pokemon${path.sep}variant${path.sep}`; masterlist = deepCopy(_masterlist); - expVariant = masterlist.exp; + expVariant = deepCopy(_exp_masterlist); femaleVariant = masterlist.female; backVariant = masterlist.back; - //@ts-ignore - delete masterlist.exp; //TODO: resolve ts-ignore - //@ts-ignore - delete masterlist.female; //TODO: resolve ts-ignore + + // @ts-ignore + delete masterlist.female; // TODO: resolve ts-ignore //@ts-ignore delete masterlist.back; //TODO: resolve ts-ignore }); diff --git a/test/testUtils/helpers/overridesHelper.ts b/test/testUtils/helpers/overridesHelper.ts index 9bb0369a31a..0ed1511255b 100644 --- a/test/testUtils/helpers/overridesHelper.ts +++ b/test/testUtils/helpers/overridesHelper.ts @@ -1,4 +1,4 @@ -import type { Variant } from "#app/data/variant"; +import type { Variant } from "#app/sprites/variant"; import { Weather } from "#app/data/weather"; import { Abilities } from "#app/enums/abilities"; import type { ModifierOverride } from "#app/modifier/modifier-type"; From 1a7442511c4011118172824a97bc627712887327 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Thu, 10 Apr 2025 23:22:42 -0700 Subject: [PATCH 13/52] [Bug] Fix Biome selection RNG (#5645) --- src/phases/select-biome-phase.ts | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index 2d67cb87405..de705728c50 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -33,23 +33,19 @@ export class SelectBiomePhase extends BattlePhase { } else if (globalScene.gameMode.hasRandomBiomes) { setNextBiome(this.generateNextBiome()); } else if (Array.isArray(biomeLinks[currentBiome])) { - let biomes: Biome[] = []; - globalScene.executeWithSeedOffset(() => { - biomes = (biomeLinks[currentBiome] as (Biome | [Biome, number])[]) - .filter(b => !Array.isArray(b) || !randSeedInt(b[1])) - .map(b => (!Array.isArray(b) ? b : b[0])); - }, globalScene.currentBattle.waveIndex); + const biomes: Biome[] = (biomeLinks[currentBiome] as (Biome | [Biome, number])[]) + .filter(b => !Array.isArray(b) || !randSeedInt(b[1])) + .map(b => (!Array.isArray(b) ? b : b[0])); + if (biomes.length > 1 && globalScene.findModifier(m => m instanceof MapModifier)) { - let biomeChoices: Biome[] = []; - globalScene.executeWithSeedOffset(() => { - biomeChoices = ( - !Array.isArray(biomeLinks[currentBiome]) - ? [biomeLinks[currentBiome] as Biome] - : (biomeLinks[currentBiome] as (Biome | [Biome, number])[]) - ) - .filter(b => !Array.isArray(b) || !randSeedInt(b[1])) - .map(b => (Array.isArray(b) ? b[0] : b)); - }, globalScene.currentBattle.waveIndex); + const biomeChoices: Biome[] = ( + !Array.isArray(biomeLinks[currentBiome]) + ? [biomeLinks[currentBiome] as Biome] + : (biomeLinks[currentBiome] as (Biome | [Biome, number])[]) + ) + .filter(b => !Array.isArray(b) || !randSeedInt(b[1])) + .map(b => (Array.isArray(b) ? b[0] : b)); + const biomeSelectItems = biomeChoices.map(b => { const ret: OptionSelectItem = { label: getBiomeName(b), From 81f424dc71f25b58b6b460691a5c8ee0471ad8b0 Mon Sep 17 00:00:00 2001 From: Blitzy <118096277+Blitz425@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:33:25 -0500 Subject: [PATCH 14/52] [Balance] Fix and Adjust TM Compatibility Curse Skill Swap Aqua Tail Zen Headbutt Hidden Power Tera Blast --- src/data/balance/tms.ts | 56 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/data/balance/tms.ts b/src/data/balance/tms.ts index 788ffd4f273..62199fd6968 100644 --- a/src/data/balance/tms.ts +++ b/src/data/balance/tms.ts @@ -19126,6 +19126,8 @@ export const tmSpecies: TmSpecies = { Species.KROOKODILE, Species.SCRAGGY, Species.SCRAFTY, + Species.YAMASK, + Species.COFAGRIGUS, Species.SAWSBUCK, Species.LITWICK, Species.LAMPENT, @@ -19163,6 +19165,7 @@ export const tmSpecies: TmSpecies = { Species.SINISTEA, Species.POLTEAGEIST, Species.PERRSERKER, + Species.RUNERIGUS, Species.PINCURCHIN, Species.STONJOURNER, Species.CUFANT, @@ -19228,6 +19231,7 @@ export const tmSpecies: TmSpecies = { Species.GALAR_SLOWBRO, Species.GALAR_WEEZING, Species.GALAR_SLOWKING, + Species.GALAR_YAMASK, Species.HISUI_ELECTRODE, Species.HISUI_TYPHLOSION, Species.HISUI_QWILFISH, @@ -30922,6 +30926,7 @@ export const tmSpecies: TmSpecies = { Species.MURKROW, Species.SLOWKING, Species.MISDREAVUS, + Species.UNOWN, Species.GIRAFARIG, Species.PINECO, Species.FORRETRESS, @@ -40134,6 +40139,8 @@ export const tmSpecies: TmSpecies = { Species.MEOWSTIC, Species.SPRITZEE, Species.AROMATISSE, + Species.INKAY, + Species.MALAMAR, Species.SYLVEON, Species.CARBINK, Species.PHANTUMP, @@ -49173,6 +49180,7 @@ export const tmSpecies: TmSpecies = { Species.KANGASKHAN, Species.GOLDEEN, Species.SEAKING, + Species.GYARADOS, Species.LAPRAS, Species.VAPOREON, Species.KABUTOPS, @@ -52587,6 +52595,7 @@ export const tmSpecies: TmSpecies = { Species.SNORLAX, Species.MEWTWO, Species.MEW, + Species.MEGANIUM, Species.CYNDAQUIL, Species.QUILAVA, Species.TYPHLOSION, @@ -66205,7 +66214,11 @@ export const tmSpecies: TmSpecies = { Species.SQUIRTLE, Species.WARTORTLE, Species.BLASTOISE, + Species.CATERPIE, + Species.METAPOD, Species.BUTTERFREE, + Species.WEEDLE, + Species.KAKUNA, Species.BEEDRILL, Species.PIDGEY, Species.PIDGEOTTO, @@ -66451,7 +66464,10 @@ export const tmSpecies: TmSpecies = { Species.MIGHTYENA, Species.ZIGZAGOON, Species.LINOONE, + Species.WURMPLE, + Species.SILCOON, Species.BEAUTIFLY, + Species.CASCOON, Species.DUSTOX, Species.LOTAD, Species.LOMBRE, @@ -66987,6 +67003,8 @@ export const tmSpecies: TmSpecies = { Species.STAKATAKA, Species.BLACEPHALON, Species.ZERAORA, + Species.MELTAN, + Species.MELMETAL, Species.ALOLA_RATTATA, Species.ALOLA_RATICATE, Species.ALOLA_RAICHU, @@ -67020,8 +67038,19 @@ export const tmSpecies: TmSpecies = { Species.ROOKIDEE, Species.CORVISQUIRE, Species.CORVIKNIGHT, + Species.BLIPBUG, + Species.DOTTLER, + Species.ORBEETLE, + Species.NICKIT, + Species.THIEVUL, + Species.GOSSIFLEUR, + Species.ELDEGOSS, + Species.WOOLOO, + Species.DUBWOOL, Species.CHEWTLE, Species.DREDNAW, + Species.YAMPER, + Species.BOLTUND, Species.ROLYCOLY, Species.CARKOL, Species.COALOSSAL, @@ -67035,6 +67064,10 @@ export const tmSpecies: TmSpecies = { Species.BARRASKEWDA, Species.TOXEL, Species.TOXTRICITY, + Species.SIZZLIPEDE, + Species.CENTISKORCH, + Species.CLOBBOPUS, + Species.GRAPPLOCT, Species.SINISTEA, Species.POLTEAGEIST, Species.HATENNA, @@ -67043,7 +67076,14 @@ export const tmSpecies: TmSpecies = { Species.IMPIDIMP, Species.MORGREM, Species.GRIMMSNARL, + Species.OBSTAGOON, Species.PERRSERKER, + Species.CURSOLA, + Species.SIRFETCHD, + Species.MR_RIME, + Species.RUNERIGUS, + Species.MILCERY, + Species.ALCREMIE, Species.FALINKS, Species.PINCURCHIN, Species.SNOM, @@ -67054,6 +67094,11 @@ export const tmSpecies: TmSpecies = { Species.MORPEKO, Species.CUFANT, Species.COPPERAJAH, + Species.DRACOZOLT, + Species.ARCTOZOLT, + Species.DRACOVISH, + Species.ARCTOVISH, + Species.DURALUDON, Species.DREEPY, Species.DRAKLOAK, Species.DRAGAPULT, @@ -67195,13 +67240,24 @@ export const tmSpecies: TmSpecies = { Species.IRON_CROWN, Species.PECHARUNT, Species.GALAR_MEOWTH, + Species.GALAR_PONYTA, + Species.GALAR_RAPIDASH, Species.GALAR_SLOWPOKE, Species.GALAR_SLOWBRO, + Species.GALAR_FARFETCHD, Species.GALAR_WEEZING, + Species.GALAR_MR_MIME, Species.GALAR_ARTICUNO, Species.GALAR_ZAPDOS, Species.GALAR_MOLTRES, Species.GALAR_SLOWKING, + Species.GALAR_CORSOLA, + Species.GALAR_ZIGZAGOON, + Species.GALAR_LINOONE, + Species.GALAR_DARUMAKA, + Species.GALAR_DARMANITAN, + Species.GALAR_YAMASK, + Species.GALAR_STUNFISK, Species.HISUI_GROWLITHE, Species.HISUI_ARCANINE, Species.HISUI_VOLTORB, From 6f56dce7712c609dc78f7ff11650eb6a34f8f661 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Fri, 11 Apr 2025 22:31:56 -0700 Subject: [PATCH 15/52] [Biome] Add and apply `lint/style/noNamespaceImport` (#5650) * Add `lint/style/noNamespaceImport` Biome rule * Apply Biome rule, add exception for `*.test.ts` files --- biome.jsonc | 6 +- src/account.ts | 6 +- src/battle-scene.ts | 107 ++-- src/battle.ts | 34 +- src/data/ability.ts | 483 +++++++++--------- src/data/balance/biomes.ts | 10 +- src/data/balance/egg-moves.ts | 6 +- src/data/balance/pokemon-evolutions.ts | 6 +- src/data/battler-tags.ts | 4 +- src/data/berry.ts | 18 +- src/data/challenge.ts | 132 +++-- src/data/daily-run.ts | 14 +- src/data/egg.ts | 31 +- src/data/moves/move.ts | 316 ++++++------ .../mysterious-challengers-encounter.ts | 8 +- .../mystery-encounters/mystery-encounter.ts | 8 +- .../utils/encounter-phase-utils.ts | 23 +- src/data/nature.ts | 4 +- src/data/pokemon-species.ts | 20 +- src/data/trainer-names.ts | 4 +- src/data/trainers/trainer-config.ts | 36 +- src/data/weather.ts | 4 +- src/field/arena.ts | 23 +- src/field/damage-number-handler.ts | 30 +- src/field/pokemon-sprite-sparkle-handler.ts | 8 +- src/field/pokemon.ts | 236 +++++---- src/field/trainer.ts | 18 +- src/game-mode.ts | 8 +- src/inputs-controller.ts | 5 +- src/loading-scene.ts | 12 +- src/phases/move-effect-phase.ts | 58 +-- src/phases/revival-blessing-phase.ts | 6 +- src/phases/select-starter-phase.ts | 6 +- src/pipelines/field-sprite.ts | 4 +- src/pipelines/sprite.ts | 6 +- src/system/achv.ts | 8 +- src/system/game-data.ts | 25 +- src/system/game-speed.ts | 8 +- .../version_migration/version_converter.ts | 3 + src/ui/abstact-option-select-ui-handler.ts | 8 +- src/ui/arena-flyout.ts | 8 +- src/ui/base-stats-overlay.ts | 4 +- src/ui/battle-flyout.ts | 4 +- src/ui/battle-info.ts | 6 +- src/ui/bgm-bar.ts | 4 +- src/ui/candy-bar.ts | 6 +- src/ui/challenges-select-ui-handler.ts | 4 +- src/ui/char-sprite.ts | 4 +- src/ui/daily-run-scoreboard.ts | 8 +- src/ui/egg-gacha-ui-handler.ts | 14 +- src/ui/fight-ui-handler.ts | 12 +- src/ui/form-modal-ui-handler.ts | 4 +- src/ui/game-stats-ui-handler.ts | 8 +- src/ui/login-form-ui-handler.ts | 6 +- src/ui/menu-ui-handler.ts | 20 +- src/ui/message-ui-handler.ts | 6 +- src/ui/modifier-select-ui-handler.ts | 7 +- src/ui/move-info-overlay.ts | 14 +- src/ui/mystery-encounter-ui-handler.ts | 21 +- src/ui/party-ui-handler.ts | 10 +- src/ui/pokedex-info-overlay.ts | 10 +- src/ui/pokedex-page-ui-handler.ts | 6 +- src/ui/pokemon-hatch-info-container.ts | 8 +- src/ui/pokemon-icon-anim-handler.ts | 4 +- src/ui/pokemon-info-container.ts | 14 +- src/ui/run-history-ui-handler.ts | 8 +- src/ui/run-info-ui-handler.ts | 19 +- src/ui/save-slot-select-ui-handler.ts | 13 +- src/ui/saving-icon-handler.ts | 8 +- src/ui/starter-select-ui-handler.ts | 13 +- src/ui/summary-ui-handler.ts | 57 ++- src/ui/target-select-ui-handler.ts | 12 +- src/ui/time-of-day-widget.ts | 10 +- src/ui/title-ui-handler.ts | 12 +- src/ui/ui.ts | 4 +- src/ui/unavailable-modal-ui-handler.ts | 4 +- test/escape-calculations.test.ts | 10 +- test/items/exp_booster.test.ts | 4 +- test/items/leek.test.ts | 6 +- test/items/light_ball.test.ts | 18 +- test/items/metal_powder.test.ts | 10 +- test/items/quick_powder.test.ts | 10 +- test/items/thick_club.test.ts | 18 +- test/moves/multi_target.test.ts | 10 +- .../mystery-encounter/encounter-test-utils.ts | 1 + test/testUtils/gameWrapper.ts | 4 +- 86 files changed, 1112 insertions(+), 1103 deletions(-) diff --git a/biome.jsonc b/biome.jsonc index c5e1d713d86..da80d8ee127 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -65,7 +65,8 @@ "useDefaultParameterLast": "off", // TODO: Fix spots in the codebase where this flag would be triggered, and then enable "useSingleVarDeclarator": "off", "useNodejsImportProtocol": "off", - "useTemplate": "off" // string concatenation is faster: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation + "useTemplate": "off", // string concatenation is faster: https://stackoverflow.com/questions/29055518/are-es6-template-literals-faster-than-string-concatenation + "noNamespaceImport": "error" }, "suspicious": { "noDoubleEquals": "error", @@ -99,6 +100,9 @@ "rules": { "performance": { "noDelete": "off" + }, + "style": { + "noNamespaceImport": "off" } } } diff --git a/src/account.ts b/src/account.ts index 96ce32714bb..7baa7d10a1a 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,11 +1,11 @@ import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; import type { UserInfo } from "#app/@types/UserInfo"; -import { bypassLogin } from "./battle-scene"; -import * as Utils from "./utils"; +import { bypassLogin } from "#app/battle-scene"; +import { randomString } from "#app/utils"; export let loggedInUser: UserInfo | null = null; // This is a random string that is used to identify the client session - unique per session (tab or window) so that the game will only save on the one that the server is expecting -export const clientSessionId = Utils.randomString(32); +export const clientSessionId = randomString(32); export function initLoggedInUser(): void { loggedInUser = { diff --git a/src/battle-scene.ts b/src/battle-scene.ts index acc8dafdd35..8ae2be5af43 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -5,9 +5,20 @@ import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import type { PokemonSpeciesFilter } from "#app/data/pokemon-species"; import type PokemonSpecies from "#app/data/pokemon-species"; import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species"; -import type { Constructor } from "#app/utils"; -import { isNullOrUndefined, randSeedInt } from "#app/utils"; -import * as Utils from "#app/utils"; +import { + fixedInt, + deepMergeObjects, + getIvsFromId, + randSeedInt, + getEnumValues, + randomString, + NumberHolder, + shiftCharCodes, + formatMoney, + isNullOrUndefined, + BooleanHolder, + type Constructor, +} from "#app/utils"; import type { Modifier, ModifierPredicate, TurnHeldItemTransferModifier } from "./modifier/modifier"; import { ConsumableModifier, @@ -733,7 +744,7 @@ export default class BattleScene extends SceneBase { } this.playTimeTimer = this.time.addEvent({ - delay: Utils.fixedInt(1000), + delay: fixedInt(1000), repeat: -1, callback: () => { if (this.gameData) { @@ -783,7 +794,7 @@ export default class BattleScene extends SceneBase { return; } const expVariantData = await this.cachedFetch("./images/pokemon/variant/_exp_masterlist.json").then(r => r.json()); - Utils.deepMergeObjects(variantData, expVariantData); + deepMergeObjects(variantData, expVariantData); } cachedFetch(url: string, init?: RequestInit): Promise { @@ -988,7 +999,7 @@ export default class BattleScene extends SceneBase { } if (boss && !dataSource) { - const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967296)); + const secondaryIvs = getIvsFromId(randSeedInt(4294967296)); for (let s = 0; s < pokemon.ivs.length; s++) { pokemon.ivs[s] = Math.round( @@ -1147,7 +1158,7 @@ export default class BattleScene extends SceneBase { * Generates a random number using the current battle's seed * * This calls {@linkcode Battle.randSeedInt}({@linkcode range}, {@linkcode min}) in `src/battle.ts` - * which calls {@linkcode Utils.randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts` + * which calls {@linkcode randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts` * * @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` @@ -1172,7 +1183,7 @@ export default class BattleScene extends SceneBase { this.lockModifierTiers = false; this.pokeballCounts = Object.fromEntries( - Utils.getEnumValues(PokeballType) + getEnumValues(PokeballType) .filter(p => p <= PokeballType.MASTER_BALL) .map(t => [t, 0]), ); @@ -1204,7 +1215,7 @@ export default class BattleScene extends SceneBase { // Reset RNG after end of game or save & quit. // This needs to happen after clearing this.currentBattle or the seed will be affected by the last wave played - this.setSeed(Overrides.SEED_OVERRIDE || Utils.randomString(24)); + this.setSeed(Overrides.SEED_OVERRIDE || randomString(24)); console.log("Seed:", this.seed); this.resetSeed(); @@ -1245,7 +1256,7 @@ export default class BattleScene extends SceneBase { ...allSpecies, ...allMoves, ...allAbilities, - ...Utils.getEnumValues(ModifierPoolType) + ...getEnumValues(ModifierPoolType) .map(mpt => getModifierPoolForType(mpt)) .flatMap(mp => Object.values(mp) @@ -1285,7 +1296,7 @@ export default class BattleScene extends SceneBase { } getDoubleBattleChance(newWaveIndex: number, playerField: PlayerPokemon[]) { - const doubleChance = new Utils.NumberHolder(newWaveIndex % 10 === 0 ? 32 : 8); + const doubleChance = new NumberHolder(newWaveIndex % 10 === 0 ? 32 : 8); this.applyModifiers(DoubleBattleChanceBoosterModifier, true, doubleChance); for (const p of playerField) { applyAbAttrs(DoubleBattleChanceAbAttr, p, null, false, doubleChance); @@ -1342,7 +1353,7 @@ export default class BattleScene extends SceneBase { if (trainerConfigs[trainerType].doubleOnly) { doubleTrainer = true; } else if (trainerConfigs[trainerType].hasDouble) { - doubleTrainer = !Utils.randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField)); + doubleTrainer = !randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField)); // Add a check that special trainers can't be double except for tate and liza - they should use the normal double chance if ( trainerConfigs[trainerType].trainerTypeDouble && @@ -1353,7 +1364,7 @@ export default class BattleScene extends SceneBase { } const variant = doubleTrainer ? TrainerVariant.DOUBLE - : Utils.randSeedInt(2) + : randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT; newTrainer = trainerData !== undefined ? trainerData.toTrainer() : new Trainer(trainerType, variant); @@ -1371,7 +1382,7 @@ export default class BattleScene extends SceneBase { if (double === undefined && newWaveIndex > 1) { if (newBattleType === BattleType.WILD && !this.gameMode.isWaveFinal(newWaveIndex)) { - newDouble = !Utils.randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField)); + newDouble = !randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField)); } else if (newBattleType === BattleType.TRAINER) { newDouble = newTrainer?.variant === TrainerVariant.DOUBLE; } @@ -1559,7 +1570,7 @@ export default class BattleScene extends SceneBase { scale: scale, x: (defaultWidth - scaledWidth) / 2, y: defaultHeight - scaledHeight, - duration: !instant ? Utils.fixedInt(Math.abs(this.field.scale - scale) * 200) : 0, + duration: !instant ? fixedInt(Math.abs(this.field.scale - scale) * 200) : 0, ease: "Sine.easeInOut", onComplete: () => resolve(), }); @@ -1656,12 +1667,12 @@ export default class BattleScene extends SceneBase { case Species.SQUAWKABILLY: case Species.TATSUGIRI: case Species.PALDEA_TAUROS: - return Utils.randSeedInt(species.forms.length); + return randSeedInt(species.forms.length); case Species.PIKACHU: if (this.currentBattle?.battleType === BattleType.TRAINER && this.currentBattle?.waveIndex < 30) { return 0; // Ban Cosplay and Partner Pika from Trainers before wave 30 } - return Utils.randSeedInt(8); + return randSeedInt(8); case Species.EEVEE: if ( this.currentBattle?.battleType === BattleType.TRAINER && @@ -1670,22 +1681,22 @@ export default class BattleScene extends SceneBase { ) { return 0; // No Partner Eevee for Wave 12 Preschoolers } - return Utils.randSeedInt(2); + return randSeedInt(2); case Species.FROAKIE: case Species.FROGADIER: case Species.GRENINJA: if (this.currentBattle?.battleType === BattleType.TRAINER && !isEggPhase) { return 0; // Don't give trainers Battle Bond Greninja, Froakie or Frogadier } - return Utils.randSeedInt(2); + return randSeedInt(2); case Species.URSHIFU: - return Utils.randSeedInt(2); + return randSeedInt(2); case Species.ZYGARDE: - return Utils.randSeedInt(4); + return randSeedInt(4); case Species.MINIOR: - return Utils.randSeedInt(7); + return randSeedInt(7); case Species.ALCREMIE: - return Utils.randSeedInt(9); + return randSeedInt(9); case Species.MEOWSTIC: case Species.INDEEDEE: case Species.BASCULEGION: @@ -1716,7 +1727,7 @@ export default class BattleScene extends SceneBase { if (this.gameMode.hasMysteryEncounters && !isEggPhase) { return 1; // Wandering form } - return Utils.randSeedInt(species.forms.length); + return randSeedInt(species.forms.length); } if (ignoreArena) { @@ -1725,7 +1736,7 @@ export default class BattleScene extends SceneBase { case Species.WORMADAM: case Species.ROTOM: case Species.LYCANROC: - return Utils.randSeedInt(species.forms.length); + return randSeedInt(species.forms.length); } return 0; } @@ -1737,7 +1748,7 @@ export default class BattleScene extends SceneBase { let ret = false; this.executeWithSeedOffset( () => { - ret = !Utils.randSeedInt(2); + ret = !randSeedInt(2); }, 0, this.seed.toString(), @@ -1749,7 +1760,7 @@ export default class BattleScene extends SceneBase { let ret = 0; this.executeWithSeedOffset( () => { - ret = Utils.randSeedInt(8) * 5; + ret = randSeedInt(8) * 5; }, 0, this.seed.toString(), @@ -1778,7 +1789,7 @@ export default class BattleScene extends SceneBase { isBoss = waveIndex % 10 === 0 || (this.gameMode.hasRandomBosses && - Utils.randSeedInt(100) < Math.min(Math.max(Math.ceil((waveIndex - 250) / 50), 0) * 2, 30)); + randSeedInt(100) < Math.min(Math.max(Math.ceil((waveIndex - 250) / 50), 0) * 2, 30)); }, waveIndex << 2); } if (!isBoss) { @@ -1805,7 +1816,7 @@ export default class BattleScene extends SceneBase { const infectedIndexes: number[] = []; const spread = (index: number, spreadTo: number) => { const partyMember = party[index + spreadTo]; - if (!partyMember.pokerus && !Utils.randSeedInt(10)) { + if (!partyMember.pokerus && !randSeedInt(10)) { partyMember.pokerus = true; infectedIndexes.push(index + spreadTo); } @@ -1831,7 +1842,7 @@ export default class BattleScene extends SceneBase { resetSeed(waveIndex?: number): void { const wave = waveIndex || this.currentBattle?.waveIndex || 0; - this.waveSeed = Utils.shiftCharCodes(this.seed, wave); + this.waveSeed = shiftCharCodes(this.seed, wave); Phaser.Math.RND.sow([this.waveSeed]); console.log("Wave Seed:", this.waveSeed, wave); this.rngCounter = 0; @@ -1850,7 +1861,7 @@ export default class BattleScene extends SceneBase { const tempRngOffset = this.rngOffset; const tempRngSeedOverride = this.rngSeedOverride; const state = Phaser.Math.RND.state(); - Phaser.Math.RND.sow([Utils.shiftCharCodes(seedOverride || this.seed, offset)]); + Phaser.Math.RND.sow([shiftCharCodes(seedOverride || this.seed, offset)]); this.rngCounter = 0; this.rngOffset = offset; this.rngSeedOverride = seedOverride || ""; @@ -1995,7 +2006,7 @@ export default class BattleScene extends SceneBase { if (this.money === undefined) { return; } - const formattedMoney = Utils.formatMoney(this.moneyFormat, this.money); + const formattedMoney = formatMoney(this.moneyFormat, this.money); this.moneyText.setText(i18next.t("battleScene:moneyOwned", { formattedMoney })); this.fieldUI.moveAbove(this.moneyText, this.luckText); if (forceVisible) { @@ -2152,12 +2163,12 @@ export default class BattleScene extends SceneBase { ), ] : allSpecies.filter(s => s.isCatchable()); - return filteredSpecies[Utils.randSeedInt(filteredSpecies.length)]; + return filteredSpecies[randSeedInt(filteredSpecies.length)]; } generateRandomBiome(waveIndex: number): Biome { const relWave = waveIndex % 250; - const biomes = Utils.getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END); + const biomes = getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END); const maxDepth = biomeDepths[Biome.END][0] - 2; const depthWeights = new Array(maxDepth + 1) .fill(null) @@ -2169,7 +2180,7 @@ export default class BattleScene extends SceneBase { biomeThresholds.push(totalWeight); } - const randInt = Utils.randSeedInt(totalWeight); + const randInt = randSeedInt(totalWeight); for (let i = 0; i < biomes.length; i++) { if (randInt < biomeThresholds[i]) { @@ -2177,7 +2188,7 @@ export default class BattleScene extends SceneBase { } } - return biomes[Utils.randSeedInt(biomes.length)]; + return biomes[randSeedInt(biomes.length)]; } isBgmPlaying(): boolean { @@ -2362,7 +2373,7 @@ export default class BattleScene extends SceneBase { this.bgmResumeTimer.destroy(); } if (resumeBgm) { - this.bgmResumeTimer = this.time.delayedCall(pauseDuration || Utils.fixedInt(sound.totalDuration * 1000), () => { + this.bgmResumeTimer = this.time.delayedCall(pauseDuration || fixedInt(sound.totalDuration * 1000), () => { this.resumeBgm(); this.bgmResumeTimer = null; }); @@ -2955,7 +2966,7 @@ export default class BattleScene extends SceneBase { const args: unknown[] = []; if (modifier instanceof PokemonHpRestoreModifier) { if (!(modifier as PokemonHpRestoreModifier).fainted) { - const hpRestoreMultiplier = new Utils.NumberHolder(1); + const hpRestoreMultiplier = new NumberHolder(1); this.applyModifiers(HealingBoosterModifier, true, hpRestoreMultiplier); args.push(hpRestoreMultiplier.value); } else { @@ -2963,7 +2974,7 @@ export default class BattleScene extends SceneBase { } } else if (modifier instanceof FusePokemonModifier) { args.push(this.getPokemonById(modifier.fusePokemonId) as PlayerPokemon); - } else if (modifier instanceof RememberMoveModifier && !Utils.isNullOrUndefined(cost)) { + } else if (modifier instanceof RememberMoveModifier && !isNullOrUndefined(cost)) { args.push(cost); } @@ -3032,7 +3043,7 @@ export default class BattleScene extends SceneBase { itemLost = true, ): boolean { const source = itemModifier.pokemonId ? itemModifier.getPokemon() : null; - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); if (source && source.isPlayer() !== target.isPlayer()) { applyAbAttrs(BlockItemTheftAbAttr, source, cancelled); @@ -3101,7 +3112,7 @@ export default class BattleScene extends SceneBase { canTransferHeldItemModifier(itemModifier: PokemonHeldItemModifier, target: Pokemon, transferQuantity = 1): boolean { const mod = itemModifier.clone() as PokemonHeldItemModifier; const source = mod.pokemonId ? mod.getPokemon() : null; - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); if (source && source.isPlayer() !== target.isPlayer()) { applyAbAttrs(BlockItemTheftAbAttr, source, cancelled); @@ -3195,7 +3206,7 @@ export default class BattleScene extends SceneBase { } let count = 0; for (let c = 0; c < chances; c++) { - if (!Utils.randSeedInt(this.gameMode.getEnemyModifierChance(isBoss))) { + if (!randSeedInt(this.gameMode.getEnemyModifierChance(isBoss))) { count++; } } @@ -3371,7 +3382,7 @@ export default class BattleScene extends SceneBase { if (mods.length < 1) { return mods; } - const rand = Utils.randSeedInt(mods.length); + const rand = randSeedInt(mods.length); return [mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand))]; }; modifiers = shuffleModifiers(modifiers); @@ -3597,7 +3608,7 @@ export default class BattleScene extends SceneBase { */ initFinalBossPhaseTwo(pokemon: Pokemon): void { if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) { - this.fadeOutBgm(Utils.fixedInt(2000), false); + this.fadeOutBgm(fixedInt(2000), false); this.ui.showDialogue( battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin, pokemon.species.name, @@ -3700,7 +3711,7 @@ export default class BattleScene extends SceneBase { if (Overrides.XP_MULTIPLIER_OVERRIDE !== null) { expMultiplier = Overrides.XP_MULTIPLIER_OVERRIDE; } - const pokemonExp = new Utils.NumberHolder(expValue * expMultiplier); + const pokemonExp = new NumberHolder(expValue * expMultiplier); this.applyModifiers(PokemonExpBoosterModifier, true, partyMember, pokemonExp); partyMemberExp.push(Math.floor(pokemonExp.value)); } @@ -3849,7 +3860,7 @@ export default class BattleScene extends SceneBase { while (i < this.mysteryEncounterSaveData.queuedEncounters.length && !!encounter) { const candidate = this.mysteryEncounterSaveData.queuedEncounters[i]; const forcedChance = candidate.spawnPercent; - if (Utils.randSeedInt(100) < forcedChance) { + if (randSeedInt(100) < forcedChance) { encounter = allMysteryEncounters[candidate.type]; } @@ -3882,7 +3893,7 @@ export default class BattleScene extends SceneBase { } const totalWeight = tierWeights.reduce((a, b) => a + b); - const tierValue = Utils.randSeedInt(totalWeight); + const tierValue = randSeedInt(totalWeight); const commonThreshold = totalWeight - tierWeights[0]; const greatThreshold = totalWeight - tierWeights[0] - tierWeights[1]; const ultraThreshold = totalWeight - tierWeights[0] - tierWeights[1] - tierWeights[2]; @@ -3974,7 +3985,7 @@ export default class BattleScene extends SceneBase { console.log("No Mystery Encounters found, falling back to Mysterious Challengers."); return allMysteryEncounters[MysteryEncounterType.MYSTERIOUS_CHALLENGERS]; } - encounter = availableEncounters[Utils.randSeedInt(availableEncounters.length)]; + encounter = availableEncounters[randSeedInt(availableEncounters.length)]; // New encounter object to not dirty flags encounter = new MysteryEncounter(encounter); encounter.populateDialogueTokensFromRequirements(); diff --git a/src/battle.ts b/src/battle.ts index 367c52568dc..fb5af223b8f 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -1,6 +1,14 @@ import { globalScene } from "#app/global-scene"; import type { Command } from "./ui/command-ui-handler"; -import * as Utils from "./utils"; +import { + randomString, + getEnumValues, + NumberHolder, + randSeedInt, + shiftCharCodes, + randSeedItem, + randInt, +} from "#app/utils"; import Trainer, { TrainerVariant } from "./field/trainer"; import type { GameMode } from "./game-mode"; import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier"; @@ -99,7 +107,7 @@ export default class Battle { public postBattleLoot: PokemonHeldItemModifier[] = []; public escapeAttempts = 0; public lastMove: Moves; - public battleSeed: string = Utils.randomString(16, true); + public battleSeed: string = randomString(16, true); private battleSeedState: string | null = null; public moneyScattered = 0; /** Primarily for double battles, keeps track of last enemy and player pokemon that triggered its ability or used a move */ @@ -181,8 +189,8 @@ export default class Battle { incrementTurn(): void { this.turn++; - this.turnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [bt, null])); - this.preTurnCommands = Object.fromEntries(Utils.getEnumValues(BattlerIndex).map(bt => [bt, null])); + this.turnCommands = Object.fromEntries(getEnumValues(BattlerIndex).map(bt => [bt, null])); + this.preTurnCommands = Object.fromEntries(getEnumValues(BattlerIndex).map(bt => [bt, null])); this.battleSeedState = null; } @@ -211,7 +219,7 @@ export default class Battle { } pickUpScatteredMoney(): void { - const moneyAmount = new Utils.NumberHolder(globalScene.currentBattle.moneyScattered); + const moneyAmount = new NumberHolder(globalScene.currentBattle.moneyScattered); globalScene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount); if (globalScene.arena.getTag(ArenaTagType.HAPPY_HOUR)) { @@ -448,7 +456,7 @@ export default class Battle { } /** - * Generates a random number using the current battle's seed. Calls {@linkcode Utils.randSeedInt} + * Generates a random number using the current battle's seed. Calls {@linkcode 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) @@ -463,12 +471,12 @@ export default class Battle { if (this.battleSeedState) { Phaser.Math.RND.state(this.battleSeedState); } else { - Phaser.Math.RND.sow([Utils.shiftCharCodes(this.battleSeed, this.turn << 6)]); + Phaser.Math.RND.sow([shiftCharCodes(this.battleSeed, this.turn << 6)]); console.log("Battle Seed:", this.battleSeed); } globalScene.rngCounter = this.rngCounter++; globalScene.rngSeedOverride = this.battleSeed; - const ret = Utils.randSeedInt(range, min); + const ret = randSeedInt(range, min); this.battleSeedState = Phaser.Math.RND.state(); Phaser.Math.RND.state(state); globalScene.rngCounter = tempRngCounter; @@ -554,19 +562,19 @@ export function getRandomTrainerFunc( seedOffset = 0, ): GetTrainerFunc { return () => { - const rand = Utils.randSeedInt(trainerPool.length); + const rand = randSeedInt(trainerPool.length); const trainerTypes: TrainerType[] = []; globalScene.executeWithSeedOffset(() => { for (const trainerPoolEntry of trainerPool) { - const trainerType = Array.isArray(trainerPoolEntry) ? Utils.randSeedItem(trainerPoolEntry) : trainerPoolEntry; + const trainerType = Array.isArray(trainerPoolEntry) ? randSeedItem(trainerPoolEntry) : trainerPoolEntry; trainerTypes.push(trainerType); } }, seedOffset); let trainerGender = TrainerVariant.DEFAULT; if (randomGender) { - trainerGender = Utils.randInt(2) === 0 ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT; + trainerGender = randInt(2) === 0 ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT; } /* 1/3 chance for evil team grunts to be double battles */ @@ -585,7 +593,7 @@ export function getRandomTrainerFunc( const isEvilTeamGrunt = evilTeamGrunts.includes(trainerTypes[rand]); if (trainerConfigs[trainerTypes[rand]].hasDouble && isEvilTeamGrunt) { - return new Trainer(trainerTypes[rand], Utils.randInt(3) === 0 ? TrainerVariant.DOUBLE : trainerGender); + return new Trainer(trainerTypes[rand], randInt(3) === 0 ? TrainerVariant.DOUBLE : trainerGender); } return new Trainer(trainerTypes[rand], trainerGender); @@ -608,7 +616,7 @@ export const classicFixedBattles: FixedBattleConfigs = { [ClassicFixedBossWaves.TOWN_YOUNGSTER]: new FixedBattleConfig() .setBattleType(BattleType.TRAINER) .setGetTrainerFunc( - () => new Trainer(TrainerType.YOUNGSTER, Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT), + () => new Trainer(TrainerType.YOUNGSTER, randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT), ), [ClassicFixedBossWaves.RIVAL_1]: new FixedBattleConfig() .setBattleType(BattleType.TRAINER) diff --git a/src/data/ability.ts b/src/data/ability.ts index f8c9b4cb8fe..b07f13c18e9 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2,8 +2,7 @@ import type { EnemyPokemon, PokemonMove } from "../field/pokemon"; import type Pokemon from "../field/pokemon"; import { HitResult, MoveResult, PlayerPokemon } from "../field/pokemon"; import { PokemonType } from "#enums/pokemon-type"; -import type { Constructor } from "#app/utils"; -import * as Utils from "../utils"; +import { BooleanHolder, NumberHolder, toDmgValue, isNullOrUndefined, randSeedItem, randSeedInt, type Constructor } from "#app/utils"; import { getPokemonNameWithAffix } from "../messages"; import type { Weather } from "#app/data/weather"; import type { BattlerTag } from "./battler-tags"; @@ -196,7 +195,7 @@ export abstract class AbAttr { * @param args - Extra args passed to the function. Handled by child classes. * @see {@linkcode canApply} */ - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): void {} + apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder | null, args: any[]): void {} getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null { return null; @@ -230,7 +229,7 @@ export class BlockRecoilDamageAttr extends AbAttr { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } @@ -251,10 +250,10 @@ export class DoubleBattleChanceAbAttr extends AbAttr { /** * Increases the chance of a double battle occurring - * @param args [0] {@linkcode Utils.NumberHolder} for double battle chance + * @param args [0] {@linkcode NumberHolder} for double battle chance */ - override apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: Utils.BooleanHolder, args: any[]): void { - const doubleBattleChance = args[0] as Utils.NumberHolder; + 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 doubleBattleChance.value = doubleBattleChance.value / 4; @@ -299,7 +298,7 @@ export class PostTeraFormChangeStatChangeAbAttr extends AbAttr { this.stages = stages; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder | null, args: any[]): void { const statStageChangePhases: StatStageChangePhase[] = []; if (!simulated) { @@ -331,7 +330,7 @@ export class ClearWeatherAbAttr extends AbAttr { return globalScene.arena.canSetWeather(WeatherType.NONE); } - public override apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: Utils.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); } @@ -357,7 +356,7 @@ export class ClearTerrainAbAttr extends AbAttr { return globalScene.arena.canSetTerrain(TerrainType.NONE); } - public override apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: Utils.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); } @@ -373,7 +372,7 @@ export class PreDefendAbAttr extends AbAttr { simulated: boolean, attacker: Pokemon, move: Move | null, - cancelled: Utils.BooleanHolder | null, + cancelled: BooleanHolder | null, args: any[]): boolean { return true; } @@ -384,19 +383,19 @@ export class PreDefendAbAttr extends AbAttr { simulated: boolean, attacker: Pokemon, move: Move | null, - cancelled: Utils.BooleanHolder | 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: Utils.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 pokemon.isFullHp() && pokemon.getMaxHp() > 1 //Checks if pokemon has wonder_guard (which forces 1hp) - && (args[0] as Utils.NumberHolder).value >= pokemon.hp; //Damage >= hp + && (args[0] as NumberHolder).value >= pokemon.hp; //Damage >= hp } - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.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); } @@ -404,7 +403,7 @@ export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { } export class BlockItemTheftAbAttr extends AbAttr { - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } @@ -422,11 +421,11 @@ export class StabBoostAbAttr extends AbAttr { } override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return (args[0] as Utils.NumberHolder).value > 1; + return (args[0] as NumberHolder).value > 1; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.NumberHolder).value += 0.5; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + (args[0] as NumberHolder).value += 0.5; } } @@ -441,12 +440,12 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr { this.damageMultiplier = damageMultiplier; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.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: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.NumberHolder).value = Utils.toDmgValue((args[0] as Utils.NumberHolder).value * this.damageMultiplier); + 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); } } @@ -465,11 +464,11 @@ export class AlliedFieldDamageReductionAbAttr extends PreDefendAbAttr { /** * Handles the damage reduction * @param args - * - `[0]` {@linkcode Utils.NumberHolder} - The damage being dealt + * - `[0]` {@linkcode NumberHolder} - The damage being dealt */ - override applyPreDefend(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _attacker: Pokemon, _move: Move, _cancelled: Utils.BooleanHolder, args: any[]): void { - const damage = args[0] as Utils.NumberHolder; - damage.value = Utils.toDmgValue(damage.value * this.damageMultiplier); + 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); } } @@ -496,7 +495,7 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { this.condition = condition ?? null; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + 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; } @@ -506,12 +505,12 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { * @param passive - Whether the ability is passive. * @param attacker {@linkcode Pokemon} The attacking Pokemon. * @param move {@linkcode Move} The attacking move. - * @param cancelled {@linkcode Utils.BooleanHolder} - A holder for a boolean value indicating if the move was cancelled. - * @param args [0] {@linkcode Utils.NumberHolder} gets set to 0 if move is immuned by an ability. + * @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: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.NumberHolder).value = 0; + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + (args[0] as NumberHolder).value = 0; } getImmuneType(): PokemonType | null { @@ -528,7 +527,7 @@ export class AttackTypeImmunityAbAttr extends TypeImmunityAbAttr { super(immuneType, condition); } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + 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); } @@ -538,7 +537,7 @@ 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: Utils.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); } @@ -549,16 +548,16 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr { super(immuneType); } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.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: Utils.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.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), - Utils.toDmgValue(pokemon.getMaxHp() / 4), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + toDmgValue(pokemon.getMaxHp() / 4), i18next.t("abilityTriggers:typeImmunityHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); cancelled.value = true; // Suppresses "No Effect" message } } @@ -575,11 +574,11 @@ class TypeImmunityStatStageChangeAbAttr extends TypeImmunityAbAttr { this.stages = stages; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.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: Utils.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) { @@ -599,11 +598,11 @@ class TypeImmunityAddBattlerTagAbAttr extends TypeImmunityAbAttr { this.turnCount = turnCount; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.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: Utils.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) { @@ -617,16 +616,16 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { super(null, condition); } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + 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 Utils.NumberHolder).value + ? (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: Utils.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 Utils.NumberHolder).value = 0; + (args[0] as NumberHolder).value = 0; } getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { @@ -644,9 +643,9 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { */ export class FullHpResistTypeAbAttr extends PreDefendAbAttr { - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.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 Utils.NumberHolder) && !(move && move.hasAttr(FixedDamageAttr)) && pokemon.isFullHp() && typeMultiplier.value > 0.5; + return (typeMultiplier && typeMultiplier instanceof NumberHolder) && !(move && move.hasAttr(FixedDamageAttr)) && pokemon.isFullHp() && typeMultiplier.value > 0.5; } /** @@ -665,7 +664,7 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr { simulated: boolean, attacker: Pokemon, move: Move | null, - cancelled: Utils.BooleanHolder | null, + cancelled: BooleanHolder | null, args: any[]): void { const typeMultiplier = args[0]; typeMultiplier.value = 0.5; @@ -704,11 +703,11 @@ export class PostDefendAbAttr extends AbAttr { export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { + 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: Utils.BooleanHolder, args: any[]): void { + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } } @@ -743,11 +742,11 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr { this.immuneCondition = immuneCondition; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.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: Utils.BooleanHolder, args: any[]): void { + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } @@ -768,13 +767,13 @@ export class WonderSkinAbAttr extends PreDefendAbAttr { super(false); } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { - const moveAccuracy = args[0] as Utils.NumberHolder; + 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: Utils.BooleanHolder, args: any[]): void { - const moveAccuracy = args[0] as Utils.NumberHolder; + 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; } } @@ -789,11 +788,11 @@ export class MoveImmunityStatStageChangeAbAttr extends MoveImmunityAbAttr { this.stages = stages; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.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: Utils.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.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); } @@ -855,7 +854,7 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr { if (this.allOthers) { const ally = pokemon.getAlly(); - const otherPokemon = !Utils.isNullOrUndefined(ally) ? pokemon.getOpponents().concat([ ally ]) : pokemon.getOpponents(); + 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)); } @@ -1090,8 +1089,8 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { - attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), { result: HitResult.INDIRECT }); - attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); + attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), { result: HitResult.INDIRECT }); + attacker.turnData.damageTaken += toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); } override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { @@ -1291,16 +1290,16 @@ export class MoveEffectChanceMultiplierAbAttr extends AbAttr { override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const exceptMoves = [ Moves.ORDER_UP, Moves.ELECTRO_SHOT ]; - return !((args[0] as Utils.NumberHolder).value <= 0 || exceptMoves.includes((args[1] as Move).id)); + return !((args[0] as NumberHolder).value <= 0 || exceptMoves.includes((args[1] as Move).id)); } /** - * @param args [0]: {@linkcode Utils.NumberHolder} Move additional effect chance. Has to be higher than or equal to 0. + * @param args [0]: {@linkcode NumberHolder} Move additional effect chance. Has to be higher than or equal to 0. * [1]: {@linkcode Moves } Move used by the ability user. */ - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.NumberHolder).value *= this.chanceMultiplier; - (args[0] as Utils.NumberHolder).value = Math.min((args[0] as Utils.NumberHolder).value, 100); + 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); } } @@ -1314,15 +1313,15 @@ export class IgnoreMoveEffectsAbAttr extends PreDefendAbAttr { super(showAbility); } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[]): boolean { - return (args[0] as Utils.NumberHolder).value > 0; + 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 Utils.NumberHolder} Move additional effect chance. + * @param args [0]: {@linkcode NumberHolder} Move additional effect chance. */ - override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.NumberHolder).value = 0; + override applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: BooleanHolder, args: any[]): void { + (args[0] as NumberHolder).value = 0; } } @@ -1337,7 +1336,7 @@ export class FieldPreventExplosiveMovesAbAttr extends AbAttr { pokemon: Pokemon, passive: boolean, simulated: boolean, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, args: any[], ): void { cancelled.value = true; @@ -1349,7 +1348,7 @@ export class FieldPreventExplosiveMovesAbAttr extends AbAttr { * If this ability cannot stack, a BooleanHolder can be used to prevent this from stacking. * @see {@link applyFieldStatMultiplierAbAttrs} * @see {@link applyFieldStat} - * @see {@link Utils.BooleanHolder} + * @see {@link BooleanHolder} */ export class FieldMultiplyStatAbAttr extends AbAttr { private stat: Stat; @@ -1364,7 +1363,7 @@ export class FieldMultiplyStatAbAttr extends AbAttr { this.canStack = canStack; } - canApplyFieldStat(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.BooleanHolder, args: any[]): boolean { + 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); } @@ -1374,12 +1373,12 @@ export class FieldMultiplyStatAbAttr extends AbAttr { * @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 Utils.NumberHolder} the value 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 hasApplied {@linkcode Utils.BooleanHolder} whether or not another multiplier has been applied to this stat + * @param hasApplied {@linkcode BooleanHolder} whether or not another multiplier has been applied to this stat * @param args {any[]} unused */ - applyFieldStat(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: Stat, statValue: Utils.NumberHolder, checkedPokemon: Pokemon, hasApplied: Utils.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; } @@ -1401,10 +1400,10 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr { // TODO: Decouple this into two attributes (type change / power boost) override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { - if (args[0] && args[0] instanceof Utils.NumberHolder) { + if (args[0] && args[0] instanceof NumberHolder) { args[0].value = this.newType; } - if (args[1] && args[1] instanceof Utils.NumberHolder) { + if (args[1] && args[1] instanceof NumberHolder) { args[1].value *= this.powerMultiplier; } } @@ -1482,12 +1481,12 @@ export class AddSecondStrikeAbAttr extends PreAttackAbAttr { * @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 Utils.NumberHolder}) - * - `[1]` the damage multiplier for the current strike ({@linkcode Utils.NumberHolder}) + * - `[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 { - const hitCount = args[0] as Utils.NumberHolder; - const multiplier = args[1] as Utils.NumberHolder; + const hitCount = args[0] as NumberHolder; + const multiplier = args[1] as NumberHolder; if (hitCount?.value) { hitCount.value += 1; } @@ -1527,8 +1526,8 @@ export class DamageBoostAbAttr extends PreAttackAbAttr { * @param args Utils.NumberHolder as damage */ override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { - const power = args[0] as Utils.NumberHolder; - power.value = Utils.toDmgValue(power.value * this.damageMultiplier); + const power = args[0] as NumberHolder; + power.value = toDmgValue(power.value * this.damageMultiplier); } } @@ -1547,7 +1546,7 @@ export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr { } override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void { - (args[0] as Utils.NumberHolder).value *= this.powerMultiplier; + (args[0] as NumberHolder).value *= this.powerMultiplier; } } @@ -1590,7 +1589,7 @@ export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr { 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 Utils.NumberHolder).value *= multiplier; + (args[0] as NumberHolder).value *= multiplier; } } @@ -1619,7 +1618,7 @@ export class FieldMovePowerBoostAbAttr extends AbAttr { 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 Utils.NumberHolder).value *= this.powerMultiplier; + (args[0] as NumberHolder).value *= this.powerMultiplier; } } } @@ -1682,7 +1681,7 @@ export class StatMultiplierAbAttr extends AbAttr { _passive: boolean, simulated: boolean, stat: BattleStat, - statValue: Utils.NumberHolder, + statValue: NumberHolder, args: any[]): boolean { const move = (args[0] as Move); return stat === this.stat && (!this.condition || this.condition(pokemon, null, move)); @@ -1693,7 +1692,7 @@ export class StatMultiplierAbAttr extends AbAttr { _passive: boolean, simulated: boolean, stat: BattleStat, - statValue: Utils.NumberHolder, + statValue: NumberHolder, args: any[]): void { statValue.value *= this.multiplier; } @@ -1766,13 +1765,13 @@ export class AllyStatMultiplierAbAttr extends AbAttr { * @param passive - unused * @param _simulated - Whether the ability is being simulated (unused) * @param _stat - The type of the checked {@linkcode Stat} (unused) - * @param statValue - {@linkcode Utils.NumberHolder} containing the value of the checked stat + * @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 * @returns `true` if this changed the checked stat, `false` otherwise. */ - applyAllyStat(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _stat: BattleStat, statValue: Utils.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; } @@ -1782,13 +1781,13 @@ export class AllyStatMultiplierAbAttr extends AbAttr { * @param passive - unused * @param simulated - Whether the ability is being simulated (unused) * @param stat - The type of the checked {@linkcode Stat} - * @param statValue - {@linkcode Utils.NumberHolder} containing the value of the checked stat + * @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 * @returns `true` if this can apply to the checked stat, `false` otherwise. */ - canApplyAllyStat(pokemon: Pokemon, _passive: boolean, simulated: boolean, stat: BattleStat, statValue: Utils.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); } } @@ -2220,8 +2219,8 @@ export class IgnoreOpponentStatStagesAbAttr extends AbAttr { * @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: Utils.BooleanHolder, args: any[]): void { - (args[1] as Utils.BooleanHolder).value = true; + override apply(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _cancelled: BooleanHolder, args: any[]): void { + (args[1] as BooleanHolder).value = true; } } @@ -2230,7 +2229,7 @@ export class IntimidateImmunityAbAttr extends AbAttr { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } @@ -2254,7 +2253,7 @@ export class PostIntimidateStatStageChangeAbAttr extends AbAttr { this.overwrites = !!overwrites; } - override apply(pokemon: Pokemon, passive: boolean, simulated:boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + 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)); } @@ -2461,7 +2460,7 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr { globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, this.stats, this.stages)); } else { for (const opponent of pokemon.getOpponents()) { - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); if (this.intimidate) { applyAbAttrs(IntimidateImmunityAbAttr, opponent, cancelled, simulated); applyAbAttrs(PostIntimidateStatStageChangeAbAttr, opponent, cancelled, simulated); @@ -2495,9 +2494,9 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr { override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const target = pokemon.getAlly(); - if (!simulated && !Utils.isNullOrUndefined(target)) { + if (!simulated && !isNullOrUndefined(target)) { globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), - Utils.toDmgValue(pokemon.getMaxHp() / this.healRatio), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim)); + toDmgValue(pokemon.getMaxHp() / this.healRatio), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim)); } } } @@ -2521,7 +2520,7 @@ export class PostSummonClearAllyStatStagesAbAttr extends PostSummonAbAttr { override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const target = pokemon.getAlly(); - if (!simulated && !Utils.isNullOrUndefined(target)) { + if (!simulated && !isNullOrUndefined(target)) { for (const s of BATTLE_STATS) { target.setStatStage(s, 0); } @@ -2639,12 +2638,12 @@ export class PostSummonHealStatusAbAttr extends PostSummonRemoveEffectAbAttr { public override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const status = pokemon.status?.effect; - return !Utils.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 { const status = pokemon.status?.effect; - if (!Utils.isNullOrUndefined(status)) { + if (!isNullOrUndefined(status)) { this.statusHealed = status; pokemon.resetStatus(false); pokemon.updateInfo(); @@ -2692,7 +2691,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { let target: Pokemon; if (targets.length > 1) { - globalScene.executeWithSeedOffset(() => target = Utils.randSeedItem(targets), globalScene.currentBattle.waveIndex); + globalScene.executeWithSeedOffset(() => target = randSeedItem(targets), globalScene.currentBattle.waveIndex); } else { target = targets[0]; } @@ -2779,7 +2778,7 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { } const ally = pokemon.getAlly(); - if (Utils.isNullOrUndefined(ally) || ally.getStatStages().every(s => s === 0)) { + if (isNullOrUndefined(ally) || ally.getStatStages().every(s => s === 0)) { return false; } @@ -2788,7 +2787,7 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr { override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const ally = pokemon.getAlly(); - if (!simulated && !Utils.isNullOrUndefined(ally)) { + if (!simulated && !isNullOrUndefined(ally)) { for (const s of BATTLE_STATS) { pokemon.setStatStage(s, ally.getStatStage(s)); } @@ -2825,7 +2824,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { target = targets[0]; return; } - target = Utils.randSeedItem(targets); + target = randSeedItem(targets); }, globalScene.currentBattle.waveIndex); } else { target = targets[0]; @@ -2934,7 +2933,7 @@ export class CommanderAbAttr extends AbAttr { // TODO: Should this work with X + Dondozo fusions? const ally = pokemon.getAlly(); - return globalScene.currentBattle?.double && !Utils.isNullOrUndefined(ally) && ally.species.speciesId === Species.DONDOZO + return globalScene.currentBattle?.double && !isNullOrUndefined(ally) && ally.species.speciesId === Species.DONDOZO && !(ally.isFainted() || ally.getTag(BattlerTagType.COMMANDED)); } @@ -2970,7 +2969,7 @@ export class PreSwitchOutResetStatusAbAttr extends PreSwitchOutAbAttr { } override canApplyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return !Utils.isNullOrUndefined(pokemon.status); + return !isNullOrUndefined(pokemon.status); } override applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { @@ -3052,7 +3051,7 @@ export class PreSwitchOutHealAbAttr extends PreSwitchOutAbAttr { override applyPreSwitchOut(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { if (!simulated) { - const healAmount = Utils.toDmgValue(pokemon.getMaxHp() * 0.33); + const healAmount = toDmgValue(pokemon.getMaxHp() * 0.33); pokemon.heal(healAmount); pokemon.updateInfo(); } @@ -3166,7 +3165,7 @@ export class PreStatStageChangeAbAttr extends AbAttr { passive: boolean, simulated: boolean, stat: BattleStat, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, args: any[]): boolean { return true; } @@ -3176,7 +3175,7 @@ export class PreStatStageChangeAbAttr extends AbAttr { passive: boolean, simulated: boolean, stat: BattleStat, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, args: any[], ): void {} } @@ -3195,10 +3194,10 @@ export class ReflectStatStageChangeAbAttr extends PreStatStageChangeAbAttr { * @param _passive N/A * @param simulated `true` if the ability is being simulated by the AI * @param stat the {@linkcode BattleStat} being affected - * @param cancelled The {@linkcode Utils.BooleanHolder} that will be set to true due to reflection + * @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: Utils.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; @@ -3230,8 +3229,8 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { this.protectedStat = protectedStat; } - override canApplyPreStatStageChange(pokemon: Pokemon | null, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { - return Utils.isNullOrUndefined(this.protectedStat) || stat === this.protectedStat; + override canApplyPreStatStageChange(pokemon: Pokemon | null, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: BooleanHolder, args: any[]): boolean { + return isNullOrUndefined(this.protectedStat) || stat === this.protectedStat; } /** @@ -3240,10 +3239,10 @@ export class ProtectStatAbAttr extends PreStatStageChangeAbAttr { * @param _passive * @param simulated * @param stat the {@linkcode BattleStat} being affected - * @param cancelled The {@linkcode Utils.BooleanHolder} that will be set to true if the stat is protected + * @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: Utils.BooleanHolder, _args: any[]): void { + override applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, stat: BattleStat, cancelled: BooleanHolder, _args: any[]): void { cancelled.value = true; } @@ -3302,7 +3301,7 @@ export class PreSetStatusAbAttr extends AbAttr { passive: boolean, simulated: boolean, effect: StatusEffect | undefined, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, args: any[]): boolean { return true; } @@ -3312,7 +3311,7 @@ export class PreSetStatusAbAttr extends AbAttr { passive: boolean, simulated: boolean, effect: StatusEffect | undefined, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, args: any[], ): void {} } @@ -3332,7 +3331,7 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { this.immuneEffects = immuneEffects; } - override canApplyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean { + 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); } @@ -3345,7 +3344,7 @@ export class PreSetStatusEffectImmunityAbAttr extends PreSetStatusAbAttr { * @param cancelled - A holder for a boolean value indicating if the status application was cancelled. * @param args - n/a */ - override applyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): void { + override applyPreSetStatus(pokemon: Pokemon, passive: boolean, simulated: boolean, effect: StatusEffect, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } @@ -3400,7 +3399,7 @@ export class ConditionalUserFieldStatusEffectImmunityAbAttr extends UserFieldSta * @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: Utils.BooleanHolder, args: [Pokemon, Pokemon | null, ...any]): boolean { + 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]); } @@ -3438,12 +3437,12 @@ export class ConditionalUserFieldProtectStatAbAttr extends PreStatStageChangeAbA * @param args Args[0] is the target pokemon of the stat change. * @returns */ - override canApplyPreStatStageChange(pokemon: Pokemon, passive: boolean, simulated: boolean, stat: BattleStat, cancelled: Utils.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 && (Utils.isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) && this.condition(target); + return !cancelled.value && (isNullOrUndefined(this.protectedStat) || stat === this.protectedStat) && this.condition(target); } /** @@ -3455,7 +3454,7 @@ 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: Utils.BooleanHolder, _args: any[]): void { + override applyPreStatStageChange(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _stat: BattleStat, cancelled: BooleanHolder, _args: any[]): void { cancelled.value = true; } } @@ -3467,7 +3466,7 @@ export class PreApplyBattlerTagAbAttr extends AbAttr { passive: boolean, simulated: boolean, tag: BattlerTag, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, args: any[], ): boolean { return true; @@ -3478,7 +3477,7 @@ export class PreApplyBattlerTagAbAttr extends AbAttr { passive: boolean, simulated: boolean, tag: BattlerTag, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, args: any[], ): void {} } @@ -3496,13 +3495,13 @@ export class PreApplyBattlerTagImmunityAbAttr extends PreApplyBattlerTagAbAttr { this.immuneTagTypes = Array.isArray(immuneTagTypes) ? immuneTagTypes : [ immuneTagTypes ]; } - override canApplyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, simulated: boolean, tag: BattlerTag, cancelled: Utils.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: Utils.BooleanHolder, args: any[]): void { + override applyPreApplyBattlerTag(pokemon: Pokemon, passive: boolean, simulated: boolean, tag: BattlerTag, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } @@ -3540,7 +3539,7 @@ 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: Utils.BooleanHolder, args: [Pokemon, ...any]): boolean { + 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]); } @@ -3556,8 +3555,8 @@ export class BlockCritAbAttr extends AbAttr { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.BooleanHolder).value = true; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + (args[0] as BooleanHolder).value = true; } } @@ -3575,8 +3574,8 @@ export class BonusCritAbAttr extends AbAttr { * @param cancelled Unused * @param args Args[0] is a number holder containing the crit stage. */ - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: [Utils.NumberHolder, ...any]): void { - (args[0] as Utils.NumberHolder).value += 1; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: [NumberHolder, ...any]): void { + (args[0] as NumberHolder).value += 1; } } @@ -3590,12 +3589,12 @@ export class MultCritAbAttr extends AbAttr { } override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const critMult = args[0] as Utils.NumberHolder; + const critMult = args[0] as NumberHolder; return critMult.value > 1; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { - const critMult = args[0] as Utils.NumberHolder; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + const critMult = args[0] as NumberHolder; critMult.value *= this.multAmount; } } @@ -3622,12 +3621,12 @@ export class ConditionalCritAbAttr extends AbAttr { /** * @param pokemon {@linkcode Pokemon} user. - * @param args [0] {@linkcode Utils.BooleanHolder} If true critical hit is guaranteed. + * @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: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.BooleanHolder).value = true; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + (args[0] as BooleanHolder).value = true; } } @@ -3636,7 +3635,7 @@ export class BlockNonDirectDamageAbAttr extends AbAttr { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } } @@ -3666,16 +3665,16 @@ export class BlockStatusDamageAbAttr extends AbAttr { /** * @param {Pokemon} pokemon The pokemon with the ability * @param {boolean} passive N/A - * @param {Utils.BooleanHolder} cancelled Whether to cancel the status damage + * @param {BooleanHolder} cancelled Whether to cancel the status damage * @param {any[]} args N/A */ - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.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: Utils.BooleanHolder, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } } @@ -3704,8 +3703,8 @@ export class ChangeMovePriorityAbAttr extends AbAttr { return this.moveFunc(pokemon, args[0] as Move); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { - (args[1] as Utils.NumberHolder).value += this.changeAmount; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + (args[1] as NumberHolder).value += this.changeAmount; } } @@ -3717,7 +3716,7 @@ export class PreWeatherEffectAbAttr extends AbAttr { passive: Boolean, simulated: boolean, weather: Weather | null, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, args: any[]): boolean { return true; } @@ -3727,7 +3726,7 @@ export class PreWeatherEffectAbAttr extends AbAttr { passive: boolean, simulated: boolean, weather: Weather | null, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, args: any[], ): void {} } @@ -3743,11 +3742,11 @@ export class BlockWeatherDamageAttr extends PreWeatherDamageAbAttr { this.weatherTypes = weatherTypes; } - override canApplyPreWeatherEffect(pokemon: Pokemon, passive: Boolean, simulated: boolean, weather: Weather, cancelled: Utils.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: Utils.BooleanHolder, args: any[]): void { + override applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } } @@ -3761,11 +3760,11 @@ export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr { this.affectsImmutable = !!affectsImmutable; } - override canApplyPreWeatherEffect(pokemon: Pokemon, passive: Boolean, simulated: boolean, weather: Weather, cancelled: Utils.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: Utils.BooleanHolder, args: any[]): void { + override applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, simulated: boolean, weather: Weather, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } } @@ -4042,7 +4041,7 @@ export class PostWeatherLapseHealAbAttr extends PostWeatherLapseAbAttr { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; if (!simulated) { globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), - Utils.toDmgValue(pokemon.getMaxHp() / (16 / this.healFactor)), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + toDmgValue(pokemon.getMaxHp() / (16 / this.healFactor)), i18next.t("abilityTriggers:postWeatherLapseHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); } } } @@ -4064,7 +4063,7 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; globalScene.queueMessage(i18next.t("abilityTriggers:postWeatherLapseDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName })); - pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / (16 / this.damageFactor)), { result: HitResult.INDIRECT }); + pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / (16 / this.damageFactor)), { result: HitResult.INDIRECT }); } } } @@ -4132,7 +4131,7 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { } override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return !Utils.isNullOrUndefined(pokemon.status) && this.effects.includes(pokemon.status.effect) && !pokemon.isFullHp(); + return !isNullOrUndefined(pokemon.status) && this.effects.includes(pokemon.status.effect) && !pokemon.isFullHp(); } /** @@ -4144,7 +4143,7 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), - Utils.toDmgValue(pokemon.getMaxHp() / 8), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true)); + toDmgValue(pokemon.getMaxHp() / 8), i18next.t("abilityTriggers:poisonHeal", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }), true)); } } } @@ -4168,7 +4167,7 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { } else { this.target = pokemon; } - return !Utils.isNullOrUndefined(this.target?.status); + return !isNullOrUndefined(this.target?.status); } override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { @@ -4224,7 +4223,7 @@ export class PostTurnLootAbAttr extends PostTurnAbAttr { return true; } - const randomIdx = Utils.randSeedInt(berriesEaten.length); + const randomIdx = randSeedInt(berriesEaten.length); const chosenBerryType = berriesEaten[randomIdx]; const chosenBerry = new BerryModifierType(chosenBerryType); berriesEaten.splice(randomIdx); // Remove berry from memory @@ -4312,7 +4311,7 @@ export class PostTurnHealAbAttr extends PostTurnAbAttr { if (!simulated) { const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; globalScene.unshiftPhase(new PokemonHealPhase(pokemon.getBattlerIndex(), - Utils.toDmgValue(pokemon.getMaxHp() / 16), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); + toDmgValue(pokemon.getMaxHp() / 16), i18next.t("abilityTriggers:postTurnHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true)); } } } @@ -4356,7 +4355,7 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr { for (const opp of pokemon.getOpponents()) { if ((opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(Abilities.COMATOSE)) && !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !opp.switchOutStatus) { if (!simulated) { - opp.damageAndUpdate(Utils.toDmgValue(opp.getMaxHp() / 8), { result: HitResult.INDIRECT }); + opp.damageAndUpdate(toDmgValue(opp.getMaxHp() / 8), { result: HitResult.INDIRECT }); globalScene.queueMessage(i18next.t("abilityTriggers:badDreams", { pokemonName: getPokemonNameWithAffix(opp) })); } } @@ -4375,7 +4374,7 @@ export class FetchBallAbAttr extends PostTurnAbAttr { } override canApplyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return !simulated && !Utils.isNullOrUndefined(globalScene.currentBattle.lastUsedPokeball) && !!pokemon.isPlayer; + return !simulated && !isNullOrUndefined(globalScene.currentBattle.lastUsedPokeball) && !!pokemon.isPlayer; } /** @@ -4407,7 +4406,7 @@ export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr { return ((globalScene.arena.weather?.isImmutable() ?? false) && globalScene.arena.canSetWeather(this.weatherType)); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.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); } @@ -4427,7 +4426,7 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr { return globalScene.arena.canSetTerrain(this.terrainType); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.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); } @@ -4562,8 +4561,8 @@ export class StatStageChangeMultiplierAbAttr extends AbAttr { this.multiplier = multiplier; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.NumberHolder).value *= this.multiplier; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + (args[0] as NumberHolder).value *= this.multiplier; } } @@ -4572,7 +4571,7 @@ export class StatStageChangeCopyAbAttr extends AbAttr { pokemon: Pokemon, passive: boolean, simulated: boolean, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, args: any[], ): void { if (!simulated) { @@ -4586,7 +4585,7 @@ export class BypassBurnDamageReductionAbAttr extends AbAttr { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } } @@ -4605,21 +4604,21 @@ export class ReduceBurnDamageAbAttr extends AbAttr { * @param pokemon N/A * @param passive N/A * @param cancelled N/A - * @param args `[0]` {@linkcode Utils.NumberHolder} The damage value being modified + * @param args `[0]` {@linkcode NumberHolder} The damage value being modified */ - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.NumberHolder).value = Utils.toDmgValue((args[0] as Utils.NumberHolder).value * this.multiplier); + 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: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.NumberHolder).value *= 2; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + (args[0] as NumberHolder).value *= 2; } } export class PreventBerryUseAbAttr extends AbAttr { - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } } @@ -4640,13 +4639,13 @@ export class HealFromBerryUseAbAttr extends AbAttr { this.healPercent = Math.max(Math.min(healPercent, 1), 0); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, ...args: [Utils.BooleanHolder, any[]]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, ...args: [BooleanHolder, any[]]): void { const { name: abilityName } = passive ? pokemon.getPassiveAbility() : pokemon.getAbility(); if (!simulated) { globalScene.unshiftPhase( new PokemonHealPhase( pokemon.getBattlerIndex(), - Utils.toDmgValue(pokemon.getMaxHp() * this.healPercent), + toDmgValue(pokemon.getMaxHp() * this.healPercent), i18next.t("abilityTriggers:healFromBerryUse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }), true ) @@ -4656,8 +4655,8 @@ export class HealFromBerryUseAbAttr extends AbAttr { } export class RunSuccessAbAttr extends AbAttr { - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.NumberHolder).value = 256; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + (args[0] as NumberHolder).value = 256; } } @@ -4681,7 +4680,7 @@ export class CheckTrappedAbAttr extends AbAttr { pokemon: Pokemon, passive: boolean, simulated: boolean, - trapped: Utils.BooleanHolder, + trapped: BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean { return true; @@ -4691,7 +4690,7 @@ export class CheckTrappedAbAttr extends AbAttr { pokemon: Pokemon, passive: boolean, simulated: boolean, - trapped: Utils.BooleanHolder, + trapped: BooleanHolder, otherPokemon: Pokemon, args: any[], ): void {} @@ -4704,7 +4703,7 @@ export class CheckTrappedAbAttr extends AbAttr { * @see {@linkcode applyCheckTrapped} */ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { - override canApplyCheckTrapped(pokemon: Pokemon, passive: boolean, simulated: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): boolean { + 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(Abilities.RUN_AWAY); @@ -4718,11 +4717,11 @@ export class ArenaTrapAbAttr extends CheckTrappedAbAttr { * 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 trapped {@link Utils.BooleanHolder} indicating whether the other Pokemon is trapped or not + * @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 */ - override applyCheckTrapped(pokemon: Pokemon, passive: boolean, simulated: boolean, trapped: Utils.BooleanHolder, otherPokemon: Pokemon, args: any[]): void { + override applyCheckTrapped(pokemon: Pokemon, passive: boolean, simulated: boolean, trapped: BooleanHolder, otherPokemon: Pokemon, args: any[]): void { trapped.value = true; } @@ -4736,8 +4735,8 @@ export class MaxMultiHitAbAttr extends AbAttr { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.NumberHolder).value = 0; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + (args[0] as NumberHolder).value = 0; } } @@ -4759,7 +4758,7 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr { override canApplyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const postBattleLoot = globalScene.currentBattle.postBattleLoot; if (!simulated && postBattleLoot.length && args[0]) { - this.randItem = Utils.randSeedItem(postBattleLoot); + this.randItem = randSeedItem(postBattleLoot); return globalScene.canTransferHeldItemModifier(this.randItem, pokemon, 1); } return false; @@ -4771,7 +4770,7 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr { override applyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { const postBattleLoot = globalScene.currentBattle.postBattleLoot; if (!this.randItem) { - this.randItem = Utils.randSeedItem(postBattleLoot); + this.randItem = randSeedItem(postBattleLoot); } if (globalScene.tryTransferHeldItemModifier(this.randItem, pokemon, true, 1, true, undefined, false)) { @@ -4828,7 +4827,7 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { 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 Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); globalScene.getField(true).map(p => applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled, simulated)); if (!diedToDirectDamage || cancelled.value || attacker!.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { return false; @@ -4839,8 +4838,8 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr { override applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): void { if (!simulated) { - attacker!.damageAndUpdate(Utils.toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)), { result: HitResult.INDIRECT }); - attacker!.turnData.damageTaken += Utils.toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)); + attacker!.damageAndUpdate(toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)), { result: HitResult.INDIRECT }); + attacker!.turnData.damageTaken += toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)); } } @@ -4886,13 +4885,13 @@ export class RedirectMoveAbAttr extends AbAttr { if (!this.canRedirect(args[0] as Moves, args[2] as Pokemon)) { return false; } - const target = args[1] as Utils.NumberHolder; + const target = args[1] as NumberHolder; const newTarget = pokemon.getBattlerIndex(); return target.value !== newTarget; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { - const target = args[1] as Utils.NumberHolder; + 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; } @@ -4933,7 +4932,7 @@ export class ReduceStatusEffectDurationAbAttr extends AbAttr { } override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return args[1] instanceof Utils.NumberHolder && args[0] === this.statusEffect; + return args[1] instanceof NumberHolder && args[0] === this.statusEffect; } /** @@ -4942,7 +4941,7 @@ 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: Utils.BooleanHolder, args: any[]): void { + override apply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: BooleanHolder, args: any[]): void { args[1].value -= 1; } } @@ -4966,7 +4965,7 @@ export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr { this.stages = stages; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + 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)); } @@ -4976,7 +4975,7 @@ export class FlinchStatStageChangeAbAttr extends FlinchEffectAbAttr { export class IncreasePpAbAttr extends AbAttr { } export class ForceSwitchOutImmunityAbAttr extends AbAttr { - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } } @@ -4991,7 +4990,7 @@ export class ReduceBerryUseThresholdAbAttr extends AbAttr { return args[0].value < hpRatio; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { args[0].value *= 2; } } @@ -5009,8 +5008,8 @@ export class WeightMultiplierAbAttr extends AbAttr { this.multiplier = multiplier; } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { - (args[0] as Utils.NumberHolder).value *= this.multiplier; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + (args[0] as NumberHolder).value *= this.multiplier; } } @@ -5019,7 +5018,7 @@ export class SyncEncounterNatureAbAttr extends AbAttr { super(false); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { (args[0] as Pokemon).setNature(pokemon.getNature()); } } @@ -5037,7 +5036,7 @@ export class MoveAbilityBypassAbAttr extends AbAttr { return this.moveIgnoreFunc(pokemon, (args[0] as Move)); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } } @@ -5057,7 +5056,7 @@ export class InfiltratorAbAttr extends AbAttr { } override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return args[0] instanceof Utils.BooleanHolder; + return args[0] instanceof BooleanHolder; } /** @@ -5066,7 +5065,7 @@ export class InfiltratorAbAttr extends AbAttr { * @param passive n/a * @param simulated n/a * @param cancelled n/a - * @param args `[0]` a {@linkcode Utils.BooleanHolder | BooleanHolder} containing the flag + * @param args `[0]` a {@linkcode BooleanHolder | BooleanHolder} containing the flag */ override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: null, args: any[]): void { const bypassed = args[0]; @@ -5107,7 +5106,7 @@ export class IgnoreTypeImmunityAbAttr extends AbAttr { return this.defenderType === (args[1] as PokemonType) && this.allowedMoveTypes.includes(args[0] as PokemonType); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } } @@ -5130,7 +5129,7 @@ export class IgnoreTypeStatusEffectImmunityAbAttr extends AbAttr { return this.statusEffect.includes(args[0] as StatusEffect) && this.defenderType.includes(args[1] as PokemonType); } - override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: Utils.BooleanHolder, args: any[]): void { + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { cancelled.value = true; } } @@ -5223,7 +5222,7 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { this.triggerMessageFunc = triggerMessageFunc; } - override canApplyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.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); } @@ -5238,9 +5237,9 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { * @param _cancelled n/a * @param args Additional arguments. */ - override applyPreDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _cancelled: Utils.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 Utils.NumberHolder).value = this.multiplier; + (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 }); @@ -5277,7 +5276,7 @@ export class BypassSpeedChanceAbAttr extends AbAttr { } override canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - const bypassSpeed = args[0] as Utils.BooleanHolder; + 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; @@ -5289,11 +5288,11 @@ export class BypassSpeedChanceAbAttr extends AbAttr { * 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 {Utils.BooleanHolder} cancelled N/A - * @param {any[]} args [0] {@linkcode Utils.BooleanHolder} set to true when the ability activated + * @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: Utils.BooleanHolder, args: any[]): void { - const bypassSpeed = args[0] as Utils.BooleanHolder; + override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder, args: any[]): void { + const bypassSpeed = args[0] as BooleanHolder; bypassSpeed.value = true; } @@ -5328,9 +5327,9 @@ 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: Utils.BooleanHolder, args: any[]): void { - const bypassSpeed = args[0] as Utils.BooleanHolder; - const canCheckHeldItems = args[1] as Utils.BooleanHolder; + 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; canCheckHeldItems.value = false; } @@ -5349,7 +5348,7 @@ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr { return !pokemon.isTerastallized; } - override apply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: Utils.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) { @@ -5400,7 +5399,7 @@ export class TerrainEventTypeChangeAbAttr extends PostSummonAbAttr { * Checks if the Pokemon should change types if summoned into an active terrain */ override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { - this.apply(pokemon, passive, simulated, new Utils.BooleanHolder(false), []); + this.apply(pokemon, passive, simulated, new BooleanHolder(false), []); } override getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]) { @@ -5527,7 +5526,7 @@ class ForceSwitchOutHelper { if (switchOutTarget.hp > 0) { switchOutTarget.leaveField(false); globalScene.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500); - if (globalScene.currentBattle.double && !Utils.isNullOrUndefined(allyPokemon)) { + if (globalScene.currentBattle.double && !isNullOrUndefined(allyPokemon)) { globalScene.redirectPokemonMoves(switchOutTarget, allyPokemon); } } @@ -5556,7 +5555,7 @@ class ForceSwitchOutHelper { const player = switchOutTarget instanceof PlayerPokemon; if (player) { - const blockedByAbility = new Utils.BooleanHolder(false); + const blockedByAbility = new BooleanHolder(false); applyAbAttrs(ForceSwitchOutImmunityAbAttr, opponent, blockedByAbility); return !blockedByAbility.value; } @@ -5584,7 +5583,7 @@ class ForceSwitchOutHelper { * @returns The failure message, or `null` if no failure. */ public getFailedText(target: Pokemon): string | null { - const blockedByAbility = new Utils.BooleanHolder(false); + const blockedByAbility = new BooleanHolder(false); applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility); return blockedByAbility.value ? i18next.t("moveTriggers:cannotBeSwitchedOut", { pokemonName: getPokemonNameWithAffix(target) }) : null; } @@ -5603,7 +5602,7 @@ class ForceSwitchOutHelper { function calculateShellBellRecovery(pokemon: Pokemon): number { const shellBellModifier = pokemon.getHeldItems().find(m => m instanceof HitHealModifier); if (shellBellModifier) { - return Utils.toDmgValue(pokemon.turnData.totalDamageDealt / 8) * shellBellModifier.stackCount; + return toDmgValue(pokemon.turnData.totalDamageDealt / 8) * shellBellModifier.stackCount; } return 0; } @@ -5743,7 +5742,7 @@ function applyAbAttrsInternal( export function applyAbAttrs( attrType: Constructor, pokemon: Pokemon, - cancelled: Utils.BooleanHolder | null, + cancelled: BooleanHolder | null, simulated = false, ...args: any[] ): void { @@ -5778,7 +5777,7 @@ export function applyPreDefendAbAttrs( pokemon: Pokemon, attacker: Pokemon, move: Move | null, - cancelled: Utils.BooleanHolder | null, + cancelled: BooleanHolder | null, simulated = false, ...args: any[] ): void { @@ -5833,7 +5832,7 @@ export function applyStatMultiplierAbAttrs( attrType: Constructor, pokemon: Pokemon, stat: BattleStat, - statValue: Utils.NumberHolder, + statValue: NumberHolder, simulated = false, ...args: any[] ): void { @@ -5851,13 +5850,13 @@ export function applyStatMultiplierAbAttrs( * @param attrType - {@linkcode AllyStatMultiplierAbAttr} should always be AllyStatMultiplierAbAttr for the time being * @param pokemon - The {@linkcode Pokemon} with the ability * @param stat - The type of the checked {@linkcode Stat} - * @param statValue - {@linkcode Utils.NumberHolder} containing the value of the checked stat + * @param statValue - {@linkcode NumberHolder} containing the value of the checked stat * @param checkedPokemon - The {@linkcode Pokemon} with the checked stat * @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: Utils.NumberHolder, simulated: boolean = false, checkedPokemon: Pokemon, ignoreAbility: boolean, ...args: any[] + pokemon: Pokemon, stat: BattleStat, statValue: NumberHolder, simulated: boolean = false, checkedPokemon: Pokemon, ignoreAbility: boolean, ...args: any[] ): void { return applyAbAttrsInternal( attrType, @@ -5910,18 +5909,18 @@ export function applyPostDamageAbAttrs( * @param attrType {@linkcode FieldMultiplyStatAbAttr} should always be FieldMultiplyBattleStatAbAttr for the time being * @param pokemon {@linkcode Pokemon} the Pokemon applying this ability * @param stat {@linkcode Stat} the type of the checked stat - * @param statValue {@linkcode Utils.NumberHolder} the value of the checked stat + * @param statValue {@linkcode NumberHolder} the value of the checked stat * @param checkedPokemon {@linkcode Pokemon} the Pokemon with the checked stat - * @param hasApplied {@linkcode Utils.BooleanHolder} whether or not a FieldMultiplyBattleStatAbAttr has already affected this stat + * @param hasApplied {@linkcode BooleanHolder} whether or not a FieldMultiplyBattleStatAbAttr has already affected this stat * @param args unused */ export function applyFieldStatMultiplierAbAttrs( attrType: Constructor, pokemon: Pokemon, stat: Stat, - statValue: Utils.NumberHolder, + statValue: NumberHolder, checkedPokemon: Pokemon, - hasApplied: Utils.BooleanHolder, + hasApplied: BooleanHolder, simulated = false, ...args: any[] ): void { @@ -6055,7 +6054,7 @@ export function applyPreStatStageChangeAbAttrs, pokemon: Pokemon | null, stat: BattleStat, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, simulated = false, ...args: any[] ): void { @@ -6091,7 +6090,7 @@ export function applyPreSetStatusAbAttrs( attrType: Constructor, pokemon: Pokemon, effect: StatusEffect | undefined, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, simulated = false, ...args: any[] ): void { @@ -6109,7 +6108,7 @@ export function applyPreApplyBattlerTagAbAttrs( attrType: Constructor, pokemon: Pokemon, tag: BattlerTag, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, simulated = false, ...args: any[] ): void { @@ -6127,7 +6126,7 @@ export function applyPreWeatherEffectAbAttrs( attrType: Constructor, pokemon: Pokemon, weather: Weather | null, - cancelled: Utils.BooleanHolder, + cancelled: BooleanHolder, simulated = false, ...args: any[] ): void { @@ -6211,7 +6210,7 @@ export function applyPostTerrainChangeAbAttrs( export function applyCheckTrappedAbAttrs( attrType: Constructor, pokemon: Pokemon, - trapped: Utils.BooleanHolder, + trapped: BooleanHolder, otherPokemon: Pokemon, messages: string[], simulated = false, @@ -6539,7 +6538,7 @@ export function initAbilities() { .bypassFaint() .ignorable(), new Ability(Abilities.SHED_SKIN, 3) - .conditionalAttr(pokemon => !Utils.randSeedInt(3), PostTurnResetStatusAbAttr), + .conditionalAttr(pokemon => !randSeedInt(3), PostTurnResetStatusAbAttr), new Ability(Abilities.GUTS, 3) .attr(BypassBurnDamageReductionAbAttr) .conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), StatMultiplierAbAttr, Stat.ATK, 1.5), @@ -6661,7 +6660,7 @@ export function initAbilities() { .attr(ChangeMovePriorityAbAttr, (pokemon, move: Move) => true, -0.2), new Ability(Abilities.TECHNICIAN, 4) .attr(MovePowerBoostAbAttr, (user, target, move) => { - const power = new Utils.NumberHolder(move.power); + const power = new NumberHolder(move.power); applyMoveAttrs(VariablePowerAttr, user, target, move, power); return power.value <= 60; }, 1.5), @@ -6755,7 +6754,7 @@ export function initAbilities() { .attr(PostDefendMoveDisableAbAttr, 30) .bypassFaint(), new Ability(Abilities.HEALER, 5) - .conditionalAttr(pokemon => !Utils.isNullOrUndefined(pokemon.getAlly()) && Utils.randSeedInt(10) < 3, PostTurnResetStatusAbAttr, true), + .conditionalAttr(pokemon => !isNullOrUndefined(pokemon.getAlly()) && randSeedInt(10) < 3, PostTurnResetStatusAbAttr, true), new Ability(Abilities.FRIEND_GUARD, 5) .attr(AlliedFieldDamageReductionAbAttr, 0.75) .ignorable(), @@ -6809,7 +6808,7 @@ export function initAbilities() { new Ability(Abilities.ANALYTIC, 5) .attr(MovePowerBoostAbAttr, (user, target, move) => { const movePhase = globalScene.findPhase((phase) => phase instanceof MovePhase && phase.pokemon.id !== user?.id); - return Utils.isNullOrUndefined(movePhase); + return isNullOrUndefined(movePhase); }, 1.3), new Ability(Abilities.ILLUSION, 5) .uncopiable() @@ -7032,7 +7031,7 @@ export function initAbilities() { .attr(FormBlockDamageAbAttr, (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getMoveEffectiveness(user, move) > 0, 0, BattlerTagType.DISGUISE, (pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }), - (pokemon) => Utils.toDmgValue(pokemon.getMaxHp() / 8)) + (pokemon) => toDmgValue(pokemon.getMaxHp() / 8)) .attr(PostBattleInitFormChangeAbAttr, () => 0) .uncopiable() .unreplaceable() diff --git a/src/data/balance/biomes.ts b/src/data/balance/biomes.ts index 3dff1722af6..c722291c66d 100644 --- a/src/data/balance/biomes.ts +++ b/src/data/balance/biomes.ts @@ -1,5 +1,5 @@ import { PokemonType } from "#enums/pokemon-type"; -import * as Utils from "#app/utils"; +import { randSeedInt, getEnumValues } from "#app/utils"; import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import i18next from "i18next"; @@ -7710,7 +7710,7 @@ export function initBiomes() { if (biome === Biome.END) { const biomeList = Object.keys(Biome).filter(key => !Number.isNaN(Number(key))); biomeList.pop(); // Removes Biome.END from the list - const randIndex = Utils.randSeedInt(biomeList.length, 1); // Will never be Biome.TOWN + const randIndex = randSeedInt(biomeList.length, 1); // Will never be Biome.TOWN biome = Biome[biomeList[randIndex]]; } const linkedBiomes: (Biome | [ Biome, number ])[] = Array.isArray(biomeLinks[biome]) @@ -7733,15 +7733,15 @@ export function initBiomes() { traverseBiome(Biome.TOWN, 0); biomeDepths[Biome.END] = [ Object.values(biomeDepths).map(d => d[0]).reduce((max: number, value: number) => Math.max(max, value), 0) + 1, 1 ]; - for (const biome of Utils.getEnumValues(Biome)) { + for (const biome of getEnumValues(Biome)) { biomePokemonPools[biome] = {}; biomeTrainerPools[biome] = {}; - for (const tier of Utils.getEnumValues(BiomePoolTier)) { + for (const tier of getEnumValues(BiomePoolTier)) { biomePokemonPools[biome][tier] = {}; biomeTrainerPools[biome][tier] = []; - for (const tod of Utils.getEnumValues(TimeOfDay)) { + for (const tod of getEnumValues(TimeOfDay)) { biomePokemonPools[biome][tier][tod] = []; } } diff --git a/src/data/balance/egg-moves.ts b/src/data/balance/egg-moves.ts index 19038ad824c..74f6a2c1afb 100644 --- a/src/data/balance/egg-moves.ts +++ b/src/data/balance/egg-moves.ts @@ -1,5 +1,5 @@ import { allMoves } from "#app/data/moves/move"; -import * as Utils from "#app/utils"; +import { getEnumKeys, getEnumValues } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -587,8 +587,8 @@ export const speciesEggMoves = { function parseEggMoves(content: string): void { let output = ""; - const speciesNames = Utils.getEnumKeys(Species); - const speciesValues = Utils.getEnumValues(Species); + const speciesNames = getEnumKeys(Species); + const speciesValues = getEnumValues(Species); const lines = content.split(/\n/g); for (const line of lines) { diff --git a/src/data/balance/pokemon-evolutions.ts b/src/data/balance/pokemon-evolutions.ts index e49bd049cd6..17f71f3c3c9 100644 --- a/src/data/balance/pokemon-evolutions.ts +++ b/src/data/balance/pokemon-evolutions.ts @@ -3,7 +3,7 @@ import { Gender } from "#app/data/gender"; import { PokeballType } from "#enums/pokeball"; import type Pokemon from "#app/field/pokemon"; import { PokemonType } from "#enums/pokemon-type"; -import * as Utils from "#app/utils"; +import { randSeedInt } from "#app/utils"; import { WeatherType } from "#enums/weather-type"; import { Nature } from "#enums/nature"; import { Biome } from "#enums/biome"; @@ -333,7 +333,7 @@ class DunsparceEvolutionCondition extends SpeciesEvolutionCondition { super(p => { let ret = false; if (p.moveset.filter(m => m.moveId === Moves.HYPER_DRILL).length > 0) { - globalScene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id); + globalScene.executeWithSeedOffset(() => ret = !randSeedInt(4), p.id); } return ret; }); @@ -346,7 +346,7 @@ class TandemausEvolutionCondition extends SpeciesEvolutionCondition { constructor() { super(p => { let ret = false; - globalScene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id); + globalScene.executeWithSeedOffset(() => ret = !randSeedInt(4), p.id); return ret; }); } diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 546dbb4a3db..76e91485460 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -42,7 +42,7 @@ import { Species } from "#enums/species"; import { EFFECTIVE_STATS, getStatKey, Stat, type BattleStat, type EffectiveStat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; import { WeatherType } from "#enums/weather-type"; -import * as Utils from "../utils"; +import { isNullOrUndefined } from "#app/utils"; export enum BattlerTagLapseType { FAINT, @@ -302,7 +302,7 @@ export class DisabledTag extends MoveRestrictionBattlerTag { super.onAdd(pokemon); const move = pokemon.getLastXMoves(-1).find(m => !m.virtual); - if (Utils.isNullOrUndefined(move) || move.move === Moves.STRUGGLE || move.move === Moves.NONE) { + if (isNullOrUndefined(move) || move.move === Moves.STRUGGLE || move.move === Moves.NONE) { return; } diff --git a/src/data/berry.ts b/src/data/berry.ts index 13820b1277b..8a58d337aa4 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -2,7 +2,7 @@ import { getPokemonNameWithAffix } from "../messages"; import type Pokemon from "../field/pokemon"; import { HitResult } from "../field/pokemon"; import { getStatusEffectHealText } from "./status-effect"; -import * as Utils from "../utils"; +import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils"; import { DoubleBerryEffectAbAttr, PostItemLostAbAttr, @@ -43,7 +43,7 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate { case BerryType.APICOT: case BerryType.SALAC: return (pokemon: Pokemon) => { - const threshold = new Utils.NumberHolder(0.25); + const threshold = new NumberHolder(0.25); // Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth const stat: BattleStat = berryType - BerryType.ENIGMA; applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); @@ -51,19 +51,19 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate { }; case BerryType.LANSAT: return (pokemon: Pokemon) => { - const threshold = new Utils.NumberHolder(0.25); + const threshold = new NumberHolder(0.25); applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); return pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST); }; case BerryType.STARF: return (pokemon: Pokemon) => { - const threshold = new Utils.NumberHolder(0.25); + const threshold = new NumberHolder(0.25); applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); return pokemon.getHpRatio() < 0.25; }; case BerryType.LEPPA: return (pokemon: Pokemon) => { - const threshold = new Utils.NumberHolder(0.25); + const threshold = new NumberHolder(0.25); applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); return !!pokemon.getMoveset().find(m => !m.getPpRatio()); }; @@ -80,7 +80,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { if (pokemon.battleData) { pokemon.battleData.berriesEaten.push(berryType); } - const hpHealed = new Utils.NumberHolder(Utils.toDmgValue(pokemon.getMaxHp() / 4)); + const hpHealed = new NumberHolder(toDmgValue(pokemon.getMaxHp() / 4)); applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed); globalScene.unshiftPhase( new PokemonHealPhase( @@ -118,7 +118,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { } // Offset BerryType such that LIECHI -> Stat.ATK = 1, GANLON -> Stat.DEF = 2, so on and so forth const stat: BattleStat = berryType - BerryType.ENIGMA; - const statStages = new Utils.NumberHolder(1); + const statStages = new NumberHolder(1); applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statStages); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [stat], statStages.value)); applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false); @@ -136,8 +136,8 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { if (pokemon.battleData) { pokemon.battleData.berriesEaten.push(berryType); } - const randStat = Utils.randSeedInt(Stat.SPD, Stat.ATK); - const stages = new Utils.NumberHolder(2); + const randStat = randSeedInt(Stat.SPD, Stat.ATK); + const stages = new NumberHolder(2); applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, stages); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [randStat], stages.value)); applyPostItemLostAbAttrs(PostItemLostAbAttr, berryOwner ?? pokemon, false); diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 868fc7d2e60..51616c3f00f 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -1,4 +1,4 @@ -import * as Utils from "#app/utils"; +import { BooleanHolder, type NumberHolder, randSeedItem, deepCopy } from "#app/utils"; import i18next from "i18next"; import type { DexAttrProps, GameData } from "#app/system/game-data"; import { defaultStarterSpecies } from "#app/system/game-data"; @@ -283,30 +283,30 @@ export abstract class Challenge { /** * An apply function for STARTER_CHOICE challenges. Derived classes should alter this. * @param _pokemon {@link PokemonSpecies} The pokemon to check the validity of. - * @param _valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. + * @param _valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. * @param _dexAttr {@link DexAttrProps} The dex attributes of the pokemon. * @returns {@link boolean} Whether this function did anything. */ - applyStarterChoice(_pokemon: PokemonSpecies, _valid: Utils.BooleanHolder, _dexAttr: DexAttrProps): boolean { + applyStarterChoice(_pokemon: PokemonSpecies, _valid: BooleanHolder, _dexAttr: DexAttrProps): boolean { return false; } /** * An apply function for STARTER_POINTS challenges. Derived classes should alter this. - * @param _points {@link Utils.NumberHolder} The amount of points you have available. + * @param _points {@link NumberHolder} The amount of points you have available. * @returns {@link boolean} Whether this function did anything. */ - applyStarterPoints(_points: Utils.NumberHolder): boolean { + applyStarterPoints(_points: NumberHolder): boolean { return false; } /** * An apply function for STARTER_COST challenges. Derived classes should alter this. * @param _species {@link Species} The pokemon to change the cost of. - * @param _cost {@link Utils.NumberHolder} The cost of the starter. + * @param _cost {@link NumberHolder} The cost of the starter. * @returns {@link boolean} Whether this function did anything. */ - applyStarterCost(_species: Species, _cost: Utils.NumberHolder): boolean { + applyStarterCost(_species: Species, _cost: NumberHolder): boolean { return false; } @@ -322,10 +322,10 @@ export abstract class Challenge { /** * An apply function for POKEMON_IN_BATTLE challenges. Derived classes should alter this. * @param _pokemon {@link Pokemon} The pokemon to check the validity of. - * @param _valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. + * @param _valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. * @returns {@link boolean} Whether this function did anything. */ - applyPokemonInBattle(_pokemon: Pokemon, _valid: Utils.BooleanHolder): boolean { + applyPokemonInBattle(_pokemon: Pokemon, _valid: BooleanHolder): boolean { return false; } @@ -341,42 +341,42 @@ export abstract class Challenge { /** * An apply function for TYPE_EFFECTIVENESS challenges. Derived classes should alter this. - * @param _effectiveness {@linkcode Utils.NumberHolder} The current effectiveness of the move. + * @param _effectiveness {@linkcode NumberHolder} The current effectiveness of the move. * @returns Whether this function did anything. */ - applyTypeEffectiveness(_effectiveness: Utils.NumberHolder): boolean { + applyTypeEffectiveness(_effectiveness: NumberHolder): boolean { return false; } /** * An apply function for AI_LEVEL challenges. Derived classes should alter this. - * @param _level {@link Utils.NumberHolder} The generated level. + * @param _level {@link NumberHolder} The generated level. * @param _levelCap {@link Number} The current level cap. * @param _isTrainer {@link Boolean} Whether this is a trainer pokemon. * @param _isBoss {@link Boolean} Whether this is a non-trainer boss pokemon. * @returns {@link boolean} Whether this function did anything. */ - applyLevelChange(_level: Utils.NumberHolder, _levelCap: number, _isTrainer: boolean, _isBoss: boolean): boolean { + applyLevelChange(_level: NumberHolder, _levelCap: number, _isTrainer: boolean, _isBoss: boolean): boolean { return false; } /** * An apply function for AI_MOVE_SLOTS challenges. Derived classes should alter this. * @param pokemon {@link Pokemon} The pokemon that is being considered. - * @param moveSlots {@link Utils.NumberHolder} The amount of move slots. + * @param moveSlots {@link NumberHolder} The amount of move slots. * @returns {@link boolean} Whether this function did anything. */ - applyMoveSlot(_pokemon: Pokemon, _moveSlots: Utils.NumberHolder): boolean { + applyMoveSlot(_pokemon: Pokemon, _moveSlots: NumberHolder): boolean { return false; } /** * An apply function for PASSIVE_ACCESS challenges. Derived classes should alter this. * @param pokemon {@link Pokemon} The pokemon to change. - * @param hasPassive {@link Utils.BooleanHolder} Whether it should have its passive. + * @param hasPassive {@link BooleanHolder} Whether it should have its passive. * @returns {@link boolean} Whether this function did anything. */ - applyPassiveAccess(_pokemon: Pokemon, _hasPassive: Utils.BooleanHolder): boolean { + applyPassiveAccess(_pokemon: Pokemon, _hasPassive: BooleanHolder): boolean { return false; } @@ -393,15 +393,10 @@ export abstract class Challenge { * @param _pokemon {@link Pokemon} What pokemon would learn the move. * @param _moveSource {@link MoveSourceType} What source the pokemon would get the move from. * @param _move {@link Moves} The move in question. - * @param _level {@link Utils.NumberHolder} The level threshold for access. + * @param _level {@link NumberHolder} The level threshold for access. * @returns {@link boolean} Whether this function did anything. */ - applyMoveAccessLevel( - _pokemon: Pokemon, - _moveSource: MoveSourceType, - _move: Moves, - _level: Utils.NumberHolder, - ): boolean { + applyMoveAccessLevel(_pokemon: Pokemon, _moveSource: MoveSourceType, _move: Moves, _level: NumberHolder): boolean { return false; } @@ -410,10 +405,10 @@ export abstract class Challenge { * @param _pokemon {@link Pokemon} What pokemon would learn the move. * @param _moveSource {@link MoveSourceType} What source the pokemon would get the move from. * @param _move {@link Moves} The move in question. - * @param _weight {@link Utils.NumberHolder} The base weight of the move + * @param _weight {@link NumberHolder} The base weight of the move * @returns {@link boolean} Whether this function did anything. */ - applyMoveWeight(_pokemon: Pokemon, _moveSource: MoveSourceType, _move: Moves, _level: Utils.NumberHolder): boolean { + applyMoveWeight(_pokemon: Pokemon, _moveSource: MoveSourceType, _move: Moves, _level: NumberHolder): boolean { return false; } @@ -438,7 +433,7 @@ export class SingleGenerationChallenge extends Challenge { super(Challenges.SINGLE_GENERATION, 9); } - applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder): boolean { + applyStarterChoice(pokemon: PokemonSpecies, valid: BooleanHolder): boolean { if (pokemon.generation !== this.value) { valid.value = false; return true; @@ -446,7 +441,7 @@ export class SingleGenerationChallenge extends Challenge { return false; } - applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean { + applyPokemonInBattle(pokemon: Pokemon, valid: BooleanHolder): boolean { const baseGeneration = getPokemonSpecies(pokemon.species.speciesId).generation; const fusionGeneration = pokemon.isFusion() ? getPokemonSpecies(pokemon.fusionSpecies!.speciesId).generation : 0; if ( @@ -575,7 +570,7 @@ export class SingleGenerationChallenge extends Challenge { TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, - Utils.randSeedItem([TrainerType.HALA, TrainerType.MOLAYNE]), + randSeedItem([TrainerType.HALA, TrainerType.MOLAYNE]), TrainerType.MARNIE_ELITE, TrainerType.RIKA, ]; @@ -602,7 +597,7 @@ export class SingleGenerationChallenge extends Challenge { TrainerType.GRIMSLEY, TrainerType.WIKSTROM, TrainerType.ACEROLA, - Utils.randSeedItem([TrainerType.BEA_ELITE, TrainerType.ALLISTER_ELITE]), + randSeedItem([TrainerType.BEA_ELITE, TrainerType.ALLISTER_ELITE]), TrainerType.LARRY_ELITE, ]; break; @@ -622,14 +617,14 @@ export class SingleGenerationChallenge extends Challenge { case ClassicFixedBossWaves.CHAMPION: trainerTypes = [ TrainerType.BLUE, - Utils.randSeedItem([TrainerType.RED, TrainerType.LANCE_CHAMPION]), - Utils.randSeedItem([TrainerType.STEVEN, TrainerType.WALLACE]), + randSeedItem([TrainerType.RED, TrainerType.LANCE_CHAMPION]), + randSeedItem([TrainerType.STEVEN, TrainerType.WALLACE]), TrainerType.CYNTHIA, - Utils.randSeedItem([TrainerType.ALDER, TrainerType.IRIS]), + randSeedItem([TrainerType.ALDER, TrainerType.IRIS]), TrainerType.DIANTHA, - Utils.randSeedItem([TrainerType.KUKUI, TrainerType.HAU]), - Utils.randSeedItem([TrainerType.LEON, TrainerType.MUSTARD]), - Utils.randSeedItem([TrainerType.GEETA, TrainerType.NEMONA]), + randSeedItem([TrainerType.KUKUI, TrainerType.HAU]), + randSeedItem([TrainerType.LEON, TrainerType.MUSTARD]), + randSeedItem([TrainerType.GEETA, TrainerType.NEMONA]), ]; break; } @@ -718,7 +713,7 @@ export class SingleTypeChallenge extends Challenge { super(Challenges.SINGLE_TYPE, 18); } - override applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps): boolean { + override applyStarterChoice(pokemon: PokemonSpecies, valid: BooleanHolder, dexAttr: DexAttrProps): boolean { const speciesForm = getPokemonSpeciesForm(pokemon.speciesId, dexAttr.formIndex); const types = [speciesForm.type1, speciesForm.type2]; if (!types.includes(this.value - 1)) { @@ -728,7 +723,7 @@ export class SingleTypeChallenge extends Challenge { return false; } - applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean { + applyPokemonInBattle(pokemon: Pokemon, valid: BooleanHolder): boolean { if ( pokemon.isPlayer() && !pokemon.isOfType(this.value - 1, false, false, true) && @@ -798,7 +793,7 @@ export class FreshStartChallenge extends Challenge { super(Challenges.FRESH_START, 1); } - applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder): boolean { + applyStarterChoice(pokemon: PokemonSpecies, valid: BooleanHolder): boolean { if (!defaultStarterSpecies.includes(pokemon.speciesId)) { valid.value = false; return true; @@ -806,7 +801,7 @@ export class FreshStartChallenge extends Challenge { return false; } - applyStarterCost(species: Species, cost: Utils.NumberHolder): boolean { + applyStarterCost(species: Species, cost: NumberHolder): boolean { if (defaultStarterSpecies.includes(species)) { cost.value = speciesStarterCosts[species]; return true; @@ -864,7 +859,7 @@ export class InverseBattleChallenge extends Challenge { return 0; } - applyTypeEffectiveness(effectiveness: Utils.NumberHolder): boolean { + applyTypeEffectiveness(effectiveness: NumberHolder): boolean { if (effectiveness.value < 1) { effectiveness.value = 2; return true; @@ -887,7 +882,7 @@ export class FlipStatChallenge extends Challenge { } override applyFlipStat(_pokemon: Pokemon, baseStats: number[]) { - const origStats = Utils.deepCopy(baseStats); + const origStats = deepCopy(baseStats); baseStats[0] = origStats[5]; baseStats[1] = origStats[4]; baseStats[2] = origStats[3]; @@ -923,7 +918,7 @@ export class LowerStarterMaxCostChallenge extends Challenge { return (DEFAULT_PARTY_MAX_COST - overrideValue).toString(); } - applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder): boolean { + applyStarterChoice(pokemon: PokemonSpecies, valid: BooleanHolder): boolean { if (speciesStarterCosts[pokemon.speciesId] > DEFAULT_PARTY_MAX_COST - this.value) { valid.value = false; return true; @@ -957,7 +952,7 @@ export class LowerStarterPointsChallenge extends Challenge { return (DEFAULT_PARTY_MAX_COST - overrideValue).toString(); } - applyStarterPoints(points: Utils.NumberHolder): boolean { + applyStarterPoints(points: NumberHolder): boolean { points.value -= this.value; return true; } @@ -974,34 +969,34 @@ export class LowerStarterPointsChallenge extends Challenge { * Apply all challenges that modify starter choice. * @param challengeType {@link ChallengeType} ChallengeType.STARTER_CHOICE * @param pokemon {@link PokemonSpecies} The pokemon to check the validity of. - * @param valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. + * @param valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. * @param dexAttr {@link DexAttrProps} The dex attributes of the pokemon. * @returns True if any challenge was successfully applied. */ export function applyChallenges( challengeType: ChallengeType.STARTER_CHOICE, pokemon: PokemonSpecies, - valid: Utils.BooleanHolder, + valid: BooleanHolder, dexAttr: DexAttrProps, ): boolean; /** * Apply all challenges that modify available total starter points. * @param challengeType {@link ChallengeType} ChallengeType.STARTER_POINTS - * @param points {@link Utils.NumberHolder} The amount of points you have available. + * @param points {@link NumberHolder} The amount of points you have available. * @returns True if any challenge was successfully applied. */ -export function applyChallenges(challengeType: ChallengeType.STARTER_POINTS, points: Utils.NumberHolder): boolean; +export function applyChallenges(challengeType: ChallengeType.STARTER_POINTS, points: NumberHolder): boolean; /** * Apply all challenges that modify the cost of a starter. * @param challengeType {@link ChallengeType} ChallengeType.STARTER_COST * @param species {@link Species} The pokemon to change the cost of. - * @param points {@link Utils.NumberHolder} The cost of the pokemon. + * @param points {@link NumberHolder} The cost of the pokemon. * @returns True if any challenge was successfully applied. */ export function applyChallenges( challengeType: ChallengeType.STARTER_COST, species: Species, - cost: Utils.NumberHolder, + cost: NumberHolder, ): boolean; /** * Apply all challenges that modify a starter after selection. @@ -1014,13 +1009,13 @@ export function applyChallenges(challengeType: ChallengeType.STARTER_MODIFY, pok * Apply all challenges that what pokemon you can have in battle. * @param challengeType {@link ChallengeType} ChallengeType.POKEMON_IN_BATTLE * @param pokemon {@link Pokemon} The pokemon to check the validity of. - * @param valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. + * @param valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed. * @returns True if any challenge was successfully applied. */ export function applyChallenges( challengeType: ChallengeType.POKEMON_IN_BATTLE, pokemon: Pokemon, - valid: Utils.BooleanHolder, + valid: BooleanHolder, ): boolean; /** * Apply all challenges that modify what fixed battles there are. @@ -1037,17 +1032,14 @@ export function applyChallenges( /** * Apply all challenges that modify type effectiveness. * @param challengeType {@linkcode ChallengeType} ChallengeType.TYPE_EFFECTIVENESS - * @param effectiveness {@linkcode Utils.NumberHolder} The current effectiveness of the move. + * @param effectiveness {@linkcode NumberHolder} The current effectiveness of the move. * @returns True if any challenge was successfully applied. */ -export function applyChallenges( - challengeType: ChallengeType.TYPE_EFFECTIVENESS, - effectiveness: Utils.NumberHolder, -): boolean; +export function applyChallenges(challengeType: ChallengeType.TYPE_EFFECTIVENESS, effectiveness: NumberHolder): boolean; /** * Apply all challenges that modify what level AI are. * @param challengeType {@link ChallengeType} ChallengeType.AI_LEVEL - * @param level {@link Utils.NumberHolder} The generated level of the pokemon. + * @param level {@link NumberHolder} The generated level of the pokemon. * @param levelCap {@link Number} The maximum level cap for the current wave. * @param isTrainer {@link Boolean} Whether this is a trainer pokemon. * @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon. @@ -1055,7 +1047,7 @@ export function applyChallenges( */ export function applyChallenges( challengeType: ChallengeType.AI_LEVEL, - level: Utils.NumberHolder, + level: NumberHolder, levelCap: number, isTrainer: boolean, isBoss: boolean, @@ -1064,25 +1056,25 @@ export function applyChallenges( * Apply all challenges that modify how many move slots the AI has. * @param challengeType {@link ChallengeType} ChallengeType.AI_MOVE_SLOTS * @param pokemon {@link Pokemon} The pokemon being considered. - * @param moveSlots {@link Utils.NumberHolder} The amount of move slots. + * @param moveSlots {@link NumberHolder} The amount of move slots. * @returns True if any challenge was successfully applied. */ export function applyChallenges( challengeType: ChallengeType.AI_MOVE_SLOTS, pokemon: Pokemon, - moveSlots: Utils.NumberHolder, + moveSlots: NumberHolder, ): boolean; /** * Apply all challenges that modify whether a pokemon has its passive. * @param challengeType {@link ChallengeType} ChallengeType.PASSIVE_ACCESS * @param pokemon {@link Pokemon} The pokemon to modify. - * @param hasPassive {@link Utils.BooleanHolder} Whether it has its passive. + * @param hasPassive {@link BooleanHolder} Whether it has its passive. * @returns True if any challenge was successfully applied. */ export function applyChallenges( challengeType: ChallengeType.PASSIVE_ACCESS, pokemon: Pokemon, - hasPassive: Utils.BooleanHolder, + hasPassive: BooleanHolder, ): boolean; /** * Apply all challenges that modify the game modes settings. @@ -1096,7 +1088,7 @@ export function applyChallenges(challengeType: ChallengeType.GAME_MODE_MODIFY): * @param pokemon {@link Pokemon} What pokemon would learn the move. * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. * @param move {@link Moves} The move in question. - * @param level {@link Utils.NumberHolder} The level threshold for access. + * @param level {@link NumberHolder} The level threshold for access. * @returns True if any challenge was successfully applied. */ export function applyChallenges( @@ -1104,7 +1096,7 @@ export function applyChallenges( pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, - level: Utils.NumberHolder, + level: NumberHolder, ): boolean; /** * Apply all challenges that modify what weight a pokemon gives to move generation @@ -1112,7 +1104,7 @@ export function applyChallenges( * @param pokemon {@link Pokemon} What pokemon would learn the move. * @param moveSource {@link MoveSourceType} What source the pokemon would get the move from. * @param move {@link Moves} The move in question. - * @param weight {@link Utils.NumberHolder} The weight of the move. + * @param weight {@link NumberHolder} The weight of the move. * @returns True if any challenge was successfully applied. */ export function applyChallenges( @@ -1120,7 +1112,7 @@ export function applyChallenges( pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, - weight: Utils.NumberHolder, + weight: NumberHolder, ): boolean; export function applyChallenges(challengeType: ChallengeType.FLIP_STAT, pokemon: Pokemon, baseStats: number[]): boolean; @@ -1225,7 +1217,7 @@ export function initChallenges() { */ export function checkStarterValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) { if (!soft) { - const isValidForChallenge = new Utils.BooleanHolder(true); + const isValidForChallenge = new BooleanHolder(true); applyChallenges(ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props); return isValidForChallenge.value; } @@ -1263,7 +1255,7 @@ export function checkStarterValidForChallenge(species: PokemonSpecies, props: De * @returns `true` if the species is considered valid. */ function checkSpeciesValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) { - const isValidForChallenge = new Utils.BooleanHolder(true); + const isValidForChallenge = new BooleanHolder(true); applyChallenges(ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props); if (!soft || !pokemonFormChanges.hasOwnProperty(species.speciesId)) { return isValidForChallenge.value; @@ -1282,7 +1274,7 @@ function checkSpeciesValidForChallenge(species: PokemonSpecies, props: DexAttrPr return species.forms.some((f2, formIndex) => { if (f1.formKey === f2.formKey) { const formProps = { ...props, formIndex }; - const isFormValidForChallenge = new Utils.BooleanHolder(true); + const isFormValidForChallenge = new BooleanHolder(true); applyChallenges(ChallengeType.STARTER_CHOICE, species, isFormValidForChallenge, formProps); return isFormValidForChallenge.value; } diff --git a/src/data/daily-run.ts b/src/data/daily-run.ts index 22fb7db10ae..3438510d613 100644 --- a/src/data/daily-run.ts +++ b/src/data/daily-run.ts @@ -3,7 +3,7 @@ import type { Species } from "#enums/species"; import { globalScene } from "#app/global-scene"; import { PlayerPokemon } from "#app/field/pokemon"; import type { Starter } from "#app/ui/starter-select-ui-handler"; -import * as Utils from "#app/utils"; +import { randSeedGauss, randSeedInt, randSeedItem, getEnumValues } from "#app/utils"; import type { PokemonSpeciesForm } from "#app/data/pokemon-species"; import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; import { speciesStarterCosts } from "#app/data/balance/starters"; @@ -43,8 +43,8 @@ export function getDailyRunStarters(seed: string): Starter[] { } const starterCosts: number[] = []; - starterCosts.push(Math.min(Math.round(3.5 + Math.abs(Utils.randSeedGauss(1))), 8)); - starterCosts.push(Utils.randSeedInt(9 - starterCosts[0], 1)); + starterCosts.push(Math.min(Math.round(3.5 + Math.abs(randSeedGauss(1))), 8)); + starterCosts.push(randSeedInt(9 - starterCosts[0], 1)); starterCosts.push(10 - (starterCosts[0] + starterCosts[1])); for (let c = 0; c < starterCosts.length; c++) { @@ -52,7 +52,7 @@ export function getDailyRunStarters(seed: string): Starter[] { const costSpecies = Object.keys(speciesStarterCosts) .map(s => Number.parseInt(s) as Species) .filter(s => speciesStarterCosts[s] === cost); - const randPkmSpecies = getPokemonSpecies(Utils.randSeedItem(costSpecies)); + const randPkmSpecies = getPokemonSpecies(randSeedItem(costSpecies)); const starterSpecies = getPokemonSpecies( randPkmSpecies.getTrainerSpeciesForLevel(startingLevel, true, PartyMemberStrength.STRONGER), ); @@ -143,7 +143,7 @@ const dailyBiomeWeights: BiomeWeights = { }; export function getDailyStartingBiome(): Biome { - const biomes = Utils.getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END); + const biomes = getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END); let totalWeight = 0; const biomeThresholds: number[] = []; @@ -155,7 +155,7 @@ export function getDailyStartingBiome(): Biome { biomeThresholds.push(totalWeight); } - const randInt = Utils.randSeedInt(totalWeight); + const randInt = randSeedInt(totalWeight); for (let i = 0; i < biomes.length; i++) { if (randInt < biomeThresholds[i]) { @@ -164,5 +164,5 @@ export function getDailyStartingBiome(): Biome { } // Fallback in case something went wrong - return biomes[Utils.randSeedInt(biomes.length)]; + return biomes[randSeedInt(biomes.length)]; } diff --git a/src/data/egg.ts b/src/data/egg.ts index 0dabf8f1119..13ab0bec479 100644 --- a/src/data/egg.ts +++ b/src/data/egg.ts @@ -4,7 +4,7 @@ import type PokemonSpecies from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { speciesStarterCosts } from "#app/data/balance/starters"; import { VariantTier } from "#enums/variant-tier"; -import * as Utils from "#app/utils"; +import { randInt, randomString, randSeedInt, getIvsFromId } from "#app/utils"; import Overrides from "#app/overrides"; import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import type { PlayerPokemon } from "#app/field/pokemon"; @@ -171,7 +171,7 @@ export class Egg { this.checkForPityTierOverrides(); } - this._id = eggOptions?.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier); + this._id = eggOptions?.id ?? randInt(EGG_SEED, EGG_SEED * this._tier); this._sourceType = eggOptions?.sourceType ?? undefined; this._hatchWaves = eggOptions?.hatchWaves ?? this.getEggTierDefaultHatchWaves(); @@ -203,7 +203,7 @@ export class Egg { } }; - const seedOverride = Utils.randomString(24); + const seedOverride = randomString(24); globalScene.executeWithSeedOffset( () => { generateEggProperties(eggOptions); @@ -248,18 +248,15 @@ export class Egg { let pokemonSpecies = getPokemonSpecies(this._species); // Special condition to have Phione eggs also have a chance of generating Manaphy if (this._species === Species.PHIONE && this._sourceType === EggSourceType.SAME_SPECIES_EGG) { - pokemonSpecies = getPokemonSpecies( - Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE) ? Species.PHIONE : Species.MANAPHY, - ); + pokemonSpecies = getPokemonSpecies(randSeedInt(MANAPHY_EGG_MANAPHY_RATE) ? Species.PHIONE : Species.MANAPHY); } // Sets the hidden ability if a hidden ability exists and // the override is set or the egg hits the chance let abilityIndex: number | undefined = undefined; const sameSpeciesEggHACheck = - this._sourceType === EggSourceType.SAME_SPECIES_EGG && !Utils.randSeedInt(SAME_SPECIES_EGG_HA_RATE); - const gachaEggHACheck = - !(this._sourceType === EggSourceType.SAME_SPECIES_EGG) && !Utils.randSeedInt(GACHA_EGG_HA_RATE); + this._sourceType === EggSourceType.SAME_SPECIES_EGG && !randSeedInt(SAME_SPECIES_EGG_HA_RATE); + const gachaEggHACheck = !(this._sourceType === EggSourceType.SAME_SPECIES_EGG) && !randSeedInt(GACHA_EGG_HA_RATE); if (pokemonSpecies.abilityHidden && (this._overrideHiddenAbility || sameSpeciesEggHACheck || gachaEggHACheck)) { abilityIndex = 2; } @@ -269,7 +266,7 @@ export class Egg { ret.shiny = this._isShiny; ret.variant = this._variantTier; - const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967295)); + const secondaryIvs = getIvsFromId(randSeedInt(4294967295)); for (let s = 0; s < ret.ivs.length; s++) { ret.ivs[s] = Math.max(ret.ivs[s], secondaryIvs[s]); @@ -370,7 +367,7 @@ export class Egg { } const tierMultiplier = this.isManaphyEgg() ? 2 : Math.pow(2, 3 - this.tier); - return Utils.randSeedInt(baseChance * tierMultiplier) ? Utils.randSeedInt(3) : 3; + return randSeedInt(baseChance * tierMultiplier) ? randSeedInt(3) : 3; } private getEggTierDefaultHatchWaves(eggTier?: EggTier): number { @@ -392,7 +389,7 @@ export class Egg { private rollEggTier(): EggTier { const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0; - const tierValue = Utils.randInt(256); + const tierValue = randInt(256); return tierValue >= GACHA_DEFAULT_COMMON_EGG_THRESHOLD + tierValueOffset ? EggTier.COMMON : tierValue >= GACHA_DEFAULT_RARE_EGG_THRESHOLD + tierValueOffset @@ -417,11 +414,11 @@ export class Egg { * when Utils.randSeedInt(8) = 1, and by making the generatePlayerPokemon() species * check pass when Utils.randSeedInt(8) = 0, we can tell them apart during tests. */ - const rand = Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE) !== 1; + const rand = randSeedInt(MANAPHY_EGG_MANAPHY_RATE) !== 1; return rand ? Species.PHIONE : Species.MANAPHY; } if (this.tier === EggTier.LEGENDARY && this._sourceType === EggSourceType.GACHA_LEGENDARY) { - if (!Utils.randSeedInt(2)) { + if (!randSeedInt(2)) { return getLegendaryGachaSpeciesForTimestamp(this.timestamp); } } @@ -501,7 +498,7 @@ export class Egg { let species: Species; - const rand = Utils.randSeedInt(totalWeight); + const rand = randSeedInt(totalWeight); for (let s = 0; s < speciesWeights.length; s++) { if (rand < speciesWeights[s]) { species = speciesPool[s]; @@ -539,7 +536,7 @@ export class Egg { break; } - return !Utils.randSeedInt(shinyChance); + return !randSeedInt(shinyChance); } // Uses the same logic as pokemon.generateVariant(). I would like to only have this logic in one @@ -550,7 +547,7 @@ export class Egg { return VariantTier.STANDARD; } - const rand = Utils.randSeedInt(10); + const rand = randSeedInt(10); if (rand >= SHINY_VARIANT_CHANCE) { return VariantTier.STANDARD; // 6/10 } diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 1af4be4fdf0..a0f68dcd5cb 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -29,9 +29,7 @@ import { } from "../status-effect"; import { getTypeDamageMultiplier } from "../type"; import { PokemonType } from "#enums/pokemon-type"; -import type { Constructor } from "#app/utils"; -import { NumberHolder } from "#app/utils"; -import * as Utils from "../../utils"; +import { BooleanHolder, NumberHolder, isNullOrUndefined, toDmgValue, randSeedItem, randSeedInt, getEnumValues, toReadableString, type Constructor } from "#app/utils"; import { WeatherType } from "#enums/weather-type"; import type { ArenaTrapTag } from "../arena-tag"; import { ArenaTagSide, WeakenMoveTypeTag } from "../arena-tag"; @@ -352,7 +350,7 @@ export default class Move implements Localizable { return false; } - const bypassed = new Utils.BooleanHolder(false); + const bypassed = new BooleanHolder(false); // TODO: Allow this to be simulated applyAbAttrs(InfiltratorAbAttr, user, null, false, bypassed); @@ -651,7 +649,7 @@ export default class Move implements Localizable { break; case MoveFlags.IGNORE_ABILITIES: if (user.hasAbilityWithAttr(MoveAbilityBypassAbAttr)) { - const abilityEffectsIgnored = new Utils.BooleanHolder(false); + const abilityEffectsIgnored = new BooleanHolder(false); applyAbAttrs(MoveAbilityBypassAbAttr, user, abilityEffectsIgnored, false, this); if (abilityEffectsIgnored.value) { return true; @@ -755,7 +753,7 @@ export default class Move implements Localizable { * @returns The calculated accuracy of the move. */ calculateBattleAccuracy(user: Pokemon, target: Pokemon, simulated: boolean = false) { - const moveAccuracy = new Utils.NumberHolder(this.accuracy); + const moveAccuracy = new NumberHolder(this.accuracy); applyMoveAttrs(VariableAccuracyAttr, user, target, this, moveAccuracy); applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, this, { value: false }, simulated, moveAccuracy); @@ -797,8 +795,8 @@ export default class Move implements Localizable { return -1; } - const power = new Utils.NumberHolder(this.power); - const typeChangeMovePowerMultiplier = new Utils.NumberHolder(1); + const power = new NumberHolder(this.power); + const typeChangeMovePowerMultiplier = new NumberHolder(1); applyPreAttackAbAttrs(MoveTypeChangeAbAttr, source, target, this, true, null, typeChangeMovePowerMultiplier); @@ -809,7 +807,7 @@ export default class Move implements Localizable { applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, target, this, simulated, power); const ally = source.getAlly(); - if (!Utils.isNullOrUndefined(ally)) { + if (!isNullOrUndefined(ally)) { applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, ally, target, this, simulated, power); } @@ -850,7 +848,7 @@ export default class Move implements Localizable { } getPriority(user: Pokemon, simulated: boolean = true) { - const priority = new Utils.NumberHolder(this.priority); + const priority = new NumberHolder(this.priority); applyMoveAttrs(IncrementMovePriorityAttr, user, null, this, priority); applyAbAttrs(ChangeMovePriorityAbAttr, user, null, simulated, this, priority); @@ -932,7 +930,7 @@ export default class Move implements Localizable { // ...and cannot enhance Pollen Puff when targeting an ally. const ally = user.getAlly(); - const exceptPollenPuffAlly: boolean = this.id === Moves.POLLEN_PUFF && !Utils.isNullOrUndefined(ally) && targets.includes(ally.getBattlerIndex()) + const exceptPollenPuffAlly: boolean = this.id === Moves.POLLEN_PUFF && !isNullOrUndefined(ally) && targets.includes(ally.getBattlerIndex()) return (!restrictSpread || !isMultiTarget) && !this.isChargingMove() @@ -971,7 +969,7 @@ export class AttackMove extends Move { const effectiveness = target.getAttackTypeEffectiveness(this.type, user, undefined, undefined, this); attackScore = Math.pow(effectiveness - 1, 2) * (effectiveness < 1 ? -2 : 2); const [ thisStat, offStat ]: EffectiveStat[] = this.category === MoveCategory.PHYSICAL ? [ Stat.ATK, Stat.SPATK ] : [ Stat.SPATK, Stat.ATK ]; - const statHolder = new Utils.NumberHolder(user.getEffectiveStat(thisStat, target)); + const statHolder = new NumberHolder(user.getEffectiveStat(thisStat, target)); const offStatValue = user.getEffectiveStat(offStat, target); applyMoveAttrs(VariableAtkAttr, user, target, move, statHolder); const statRatio = offStatValue / statHolder.value; @@ -981,7 +979,7 @@ export class AttackMove extends Move { attackScore *= 1.5; } - const power = new Utils.NumberHolder(this.calculateEffectivePower()); + const power = new NumberHolder(this.calculateEffectivePower()); applyMoveAttrs(VariablePowerAttr, user, target, move, power); attackScore += Math.floor(power.value / 5); @@ -1252,7 +1250,7 @@ export class MoveEffectAttr extends MoveAttr { * @returns Move effect chance value. */ getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): number { - const moveChance = new Utils.NumberHolder(this.effectChanceOverride ?? move.chance); + const moveChance = new NumberHolder(this.effectChanceOverride ?? move.chance); applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, !showAbility, moveChance, move); @@ -1415,7 +1413,7 @@ export class RespectAttackTypeImmunityAttr extends MoveAttr { } export class IgnoreOpponentStatStagesAttr extends MoveAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.BooleanHolder).value = true; + (args[0] as BooleanHolder).value = true; return true; } @@ -1423,7 +1421,7 @@ export class IgnoreOpponentStatStagesAttr extends MoveAttr { export class HighCritAttr extends MoveAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value++; + (args[0] as NumberHolder).value++; return true; } @@ -1435,7 +1433,7 @@ export class HighCritAttr extends MoveAttr { export class CritOnlyAttr extends MoveAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.BooleanHolder).value = true; + (args[0] as BooleanHolder).value = true; return true; } @@ -1455,7 +1453,7 @@ export class FixedDamageAttr extends MoveAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = this.getDamage(user, target, move); + (args[0] as NumberHolder).value = this.getDamage(user, target, move); return true; } @@ -1471,7 +1469,7 @@ export class UserHpDamageAttr extends FixedDamageAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = user.hp; + (args[0] as NumberHolder).value = user.hp; return true; } @@ -1492,7 +1490,7 @@ export class TargetHalfHpDamageAttr extends FixedDamageAttr { const lensCount = user.getHeldItems().find(i => i instanceof PokemonMultiHitModifier)?.getStackCount() ?? 0; if (lensCount <= 0) { // no multi lenses; we can just halve the target's hp and call it a day - (args[0] as Utils.NumberHolder).value = Utils.toDmgValue(target.hp / 2); + (args[0] as NumberHolder).value = toDmgValue(target.hp / 2); return true; } @@ -1503,11 +1501,11 @@ export class TargetHalfHpDamageAttr extends FixedDamageAttr { this.initialHp = target.hp; default: // multi lens added hit; use initialHp tracker to ensure correct damage - (args[0] as Utils.NumberHolder).value = Utils.toDmgValue(this.initialHp / 2); + (args[0] as NumberHolder).value = toDmgValue(this.initialHp / 2); return true; case lensCount + 1: // parental bond added hit; calc damage as normal - (args[0] as Utils.NumberHolder).value = Utils.toDmgValue(target.hp / 2); + (args[0] as NumberHolder).value = toDmgValue(target.hp / 2); return true; } } @@ -1523,7 +1521,7 @@ export class MatchHpAttr extends FixedDamageAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = target.hp - user.hp; + (args[0] as NumberHolder).value = target.hp - user.hp; return true; } @@ -1553,7 +1551,7 @@ export class CounterDamageAttr extends FixedDamageAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const damage = user.turnData.attacksReceived.filter(ar => this.moveFilter(allMoves[ar.move])).reduce((total: number, ar: AttackMoveResult) => total + ar.damage, 0); - (args[0] as Utils.NumberHolder).value = Utils.toDmgValue(damage * this.multiplier); + (args[0] as NumberHolder).value = toDmgValue(damage * this.multiplier); return true; } @@ -1579,13 +1577,13 @@ export class RandomLevelDamageAttr extends FixedDamageAttr { } getDamage(user: Pokemon, target: Pokemon, move: Move): number { - return Utils.toDmgValue(user.level * (user.randSeedIntRange(50, 150) * 0.01)); + return toDmgValue(user.level * (user.randSeedIntRange(50, 150) * 0.01)); } } export class ModifiedDamageAttr extends MoveAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const initialDamage = args[0] as Utils.NumberHolder; + const initialDamage = args[0] as NumberHolder; initialDamage.value = this.getModifiedDamage(user, target, move, initialDamage.value); return true; @@ -1638,7 +1636,7 @@ export class RecoilAttr extends MoveEffectAttr { return false; } - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); if (!this.unblockable) { applyAbAttrs(BlockRecoilDamageAttr, user, cancelled); applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled); @@ -1655,7 +1653,7 @@ export class RecoilAttr extends MoveEffectAttr { const damageValue = (!this.useHp ? user.turnData.totalDamageDealt : user.getMaxHp()) * this.damageRatio; const minValue = user.turnData.totalDamageDealt ? 1 : 0; - const recoilDamage = Utils.toDmgValue(damageValue, minValue); + const recoilDamage = toDmgValue(damageValue, minValue); if (!recoilDamage) { return false; } @@ -1772,11 +1770,11 @@ export class HalfSacrificialAttr extends MoveEffectAttr { return false; } - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); // Check to see if the Pokemon has an ability that blocks non-direct damage applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled); if (!cancelled.value) { - user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT, ignoreSegments: true }); + user.damageAndUpdate(toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT, ignoreSegments: true }); globalScene.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", { pokemonName: getPokemonNameWithAffix(user) })); // Queue recoil message } return true; @@ -1882,7 +1880,7 @@ export class HealAttr extends MoveEffectAttr { */ addHealPhase(target: Pokemon, healRatio: number) { globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), - Utils.toDmgValue(target.getMaxHp() * healRatio), i18next.t("moveTriggers:healHp", { pokemonName: getPokemonNameWithAffix(target) }), true, !this.showAnim)); + toDmgValue(target.getMaxHp() * healRatio), i18next.t("moveTriggers:healHp", { pokemonName: getPokemonNameWithAffix(target) }), true, !this.showAnim)); } getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { @@ -1965,9 +1963,9 @@ export class FlameBurstAttr extends MoveEffectAttr { */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const targetAlly = target.getAlly(); - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); - if (!Utils.isNullOrUndefined(targetAlly)) { + if (!isNullOrUndefined(targetAlly)) { applyAbAttrs(BlockNonDirectDamageAbAttr, targetAlly, cancelled); } @@ -1980,7 +1978,7 @@ export class FlameBurstAttr extends MoveEffectAttr { } getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { - return !Utils.isNullOrUndefined(target.getAlly()) ? -5 : 0; + return !isNullOrUndefined(target.getAlly()) ? -5 : 0; } } @@ -2048,11 +2046,11 @@ export class IgnoreWeatherTypeDebuffAttr extends MoveAttr { * @param user {@linkcode Pokemon} that used the move * @param target N/A * @param move {@linkcode Move} with this attribute - * @param args [0] {@linkcode Utils.NumberHolder} for arenaAttackTypeMultiplier + * @param args [0] {@linkcode NumberHolder} for arenaAttackTypeMultiplier * @returns true if the function succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const weatherModifier = args[0] as Utils.NumberHolder; + const weatherModifier = args[0] as NumberHolder; //If the type-based attack power modifier due to weather (e.g. Water moves in Sun) is below 1, set it to 1 if (globalScene.arena.weather?.weatherType === this.weather) { weatherModifier.value = Math.max(weatherModifier.value, 1); @@ -2203,7 +2201,7 @@ export class HitHealAttr extends MoveEffectAttr { message = i18next.t("battle:drainMessage", { pokemonName: getPokemonNameWithAffix(target) }); } else { // Default healing formula used by draining moves like Absorb, Draining Kiss, Bitter Blade, etc. - healAmount = Utils.toDmgValue(user.turnData.singleHitDamageDealt * this.healRatio); + healAmount = toDmgValue(user.turnData.singleHitDamageDealt * this.healRatio); message = i18next.t("battle:regainHealth", { pokemonName: getPokemonNameWithAffix(user) }); } if (reverseDrain) { @@ -2261,7 +2259,7 @@ export class IncrementMovePriorityAttr extends MoveAttr { * @param user {@linkcode Pokemon} using this move * @param target {@linkcode Pokemon} target of this move * @param move {@linkcode Move} being used - * @param args [0] {@linkcode Utils.NumberHolder} for move priority. + * @param args [0] {@linkcode NumberHolder} for move priority. * @returns true if function succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { @@ -2269,7 +2267,7 @@ export class IncrementMovePriorityAttr extends MoveAttr { return false; } - (args[0] as Utils.NumberHolder).value += this.increaseAmount; + (args[0] as NumberHolder).value += this.increaseAmount; return true; } } @@ -2307,15 +2305,15 @@ export class MultiHitAttr extends MoveAttr { * @param user {@linkcode Pokemon} that used the attack * @param target {@linkcode Pokemon} targeted by the attack * @param move {@linkcode Move} being used - * @param args [0] {@linkcode Utils.NumberHolder} storing the hit count of the attack + * @param args [0] {@linkcode NumberHolder} storing the hit count of the attack * @returns True */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const hitType = new Utils.NumberHolder(this.intrinsicMultiHitType); + const hitType = new NumberHolder(this.intrinsicMultiHitType); applyMoveAttrs(ChangeMultiHitTypeAttr, user, target, move, hitType); this.multiHitType = hitType.value; - (args[0] as Utils.NumberHolder).value = this.getHitCount(user, target); + (args[0] as NumberHolder).value = this.getHitCount(user, target); return true; } @@ -2336,7 +2334,7 @@ export class MultiHitAttr extends MoveAttr { case MultiHitType._2_TO_5: { const rand = user.randSeedInt(20); - const hitValue = new Utils.NumberHolder(rand); + const hitValue = new NumberHolder(rand); applyAbAttrs(MaxMultiHitAbAttr, user, null, false, hitValue); if (hitValue.value >= 13) { return 2; @@ -2414,7 +2412,7 @@ export class ChangeMultiHitTypeAttr extends MoveAttr { export class WaterShurikenMultiHitTypeAttr extends ChangeMultiHitTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (user.species.speciesId === Species.GRENINJA && user.hasAbility(Abilities.BATTLE_BOND) && user.formIndex === 2) { - (args[0] as Utils.NumberHolder).value = MultiHitType._3; + (args[0] as NumberHolder).value = MultiHitType._3; return true; } return false; @@ -2480,7 +2478,7 @@ export class MultiStatusEffectAttr extends StatusEffectAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - this.effect = Utils.randSeedItem(this.effects); + this.effect = randSeedItem(this.effects); const result = super.apply(user, target, move, args); return result; } @@ -2612,7 +2610,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { return false; } - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); applyAbAttrs(BlockItemTheftAbAttr, target, cancelled); // Check for abilities that block item theft if (cancelled.value === true) { @@ -2686,7 +2684,7 @@ export class EatBerryAttr extends MoveEffectAttr { return false; } this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)]; - const preserve = new Utils.BooleanHolder(false); + const preserve = new BooleanHolder(false); globalScene.applyModifiers(PreserveBerryModifier, target.isPlayer(), target, preserve); // check for berry pouch preservation if (!preserve.value) { this.reduceBerryModifier(target); @@ -2709,7 +2707,7 @@ export class EatBerryAttr extends MoveEffectAttr { eatBerry(consumer: Pokemon, berryOwner?: Pokemon) { getBerryEffectFunc(this.chosenBerry!.berryType)(consumer, berryOwner); // consumer eats the berry - applyAbAttrs(HealFromBerryUseAbAttr, consumer, new Utils.BooleanHolder(false)); + applyAbAttrs(HealFromBerryUseAbAttr, consumer, new BooleanHolder(false)); } } @@ -2733,7 +2731,7 @@ export class StealEatBerryAttr extends EatBerryAttr { if (move.hitsSubstitute(user, target)) { return false; } - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); applyAbAttrs(BlockItemTheftAbAttr, target, cancelled); // check for abilities that block item theft if (cancelled.value === true) { return false; @@ -2846,11 +2844,11 @@ export class BypassBurnDamageReductionAttr extends MoveAttr { * @param user N/A * @param target N/A * @param move {@linkcode Move} with this attribute - * @param args [0] {@linkcode Utils.BooleanHolder} for burnDamageReductionCancelled + * @param args [0] {@linkcode BooleanHolder} for burnDamageReductionCancelled * @returns true if the function succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.BooleanHolder).value = true; + (args[0] as BooleanHolder).value = true; return true; } @@ -2931,14 +2929,14 @@ export class OneHitKOAttr extends MoveAttr { return false; } - (args[0] as Utils.BooleanHolder).value = true; + (args[0] as BooleanHolder).value = true; return true; } getCondition(): MoveConditionFunc { return (user, target, move) => { - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); applyAbAttrs(BlockOneHitKOAbAttr, target, cancelled); return !cancelled.value && user.level >= target.level; }; @@ -2965,12 +2963,12 @@ export class InstantChargeAttr extends MoveAttr { * @param target n/a * @param move the {@linkcode Move} associated with this attribute * @param args - * - `[0]` a {@linkcode Utils.BooleanHolder | BooleanHolder} for the "instant charge" flag + * - `[0]` a {@linkcode BooleanHolder | BooleanHolder} for the "instant charge" flag * @returns `true` if the instant charge condition is met; `false` otherwise. */ override apply(user: Pokemon, target: Pokemon | null, move: Move, args: any[]): boolean { const instantCharge = args[0]; - if (!(instantCharge instanceof Utils.BooleanHolder)) { + if (!(instantCharge instanceof BooleanHolder)) { return false; } @@ -2992,7 +2990,7 @@ export class WeatherInstantChargeAttr extends InstantChargeAttr { super((user, move) => { const currentWeather = globalScene.arena.weather; - if (Utils.isNullOrUndefined(currentWeather?.weatherType)) { + if (isNullOrUndefined(currentWeather?.weatherType)) { return false; } else { return !currentWeather?.isEffectSuppressed() @@ -3035,7 +3033,7 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { return true; } - const overridden = args[0] as Utils.BooleanHolder; + const overridden = args[0] as BooleanHolder; const virtual = args[1] as boolean; if (!virtual) { @@ -3069,7 +3067,7 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr { * @param target n/a * @param move the {@linkcode Move} being used * @param args - * - [0] a {@linkcode Utils.BooleanHolder} indicating whether the move's base + * - [0] a {@linkcode BooleanHolder} indicating whether the move's base * effects should be overridden this turn. * @returns `true` if base move effects were overridden; `false` otherwise */ @@ -3080,7 +3078,7 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr { return false; } - const overridden = args[0] as Utils.BooleanHolder; + const overridden = args[0] as BooleanHolder; const allyMovePhase = globalScene.findPhase((phase) => phase instanceof MovePhase && phase.pokemon.isPlayer() === user.isPlayer()); if (allyMovePhase) { @@ -3451,7 +3449,7 @@ export class CutHpStatStageBoostAttr extends StatStageChangeAttr { this.messageCallback = messageCallback; } override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / this.cutRatio), { result: HitResult.INDIRECT }); + user.damageAndUpdate(toDmgValue(user.getMaxHp() / this.cutRatio), { result: HitResult.INDIRECT }); user.updateInfo(); const ret = super.apply(user, target, move, args); if (this.messageCallback) { @@ -3663,7 +3661,7 @@ export class LessPPMorePowerAttr extends VariablePowerAttr { * @param user {@linkcode Pokemon} using this move * @param target {@linkcode Pokemon} target of this move * @param move {@linkcode Move} being used - * @param args [0] {@linkcode Utils.NumberHolder} of power + * @param args [0] {@linkcode NumberHolder} of power * @returns true if the function succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { @@ -3676,7 +3674,7 @@ export class LessPPMorePowerAttr extends VariablePowerAttr { ppRemains = 0; } - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; switch (ppRemains) { case 0: @@ -3709,7 +3707,7 @@ export class MovePowerMultiplierAttr extends VariablePowerAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; power.value *= this.powerMultiplierFunc(user, target, move); return true; @@ -3749,7 +3747,7 @@ export class BeatUpAttr extends VariablePowerAttr { * @returns true if the function succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; const party = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); const allyCount = party.filter(pokemon => { @@ -3764,7 +3762,7 @@ export class BeatUpAttr extends VariablePowerAttr { const doublePowerChanceMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => { let message: string = ""; globalScene.executeWithSeedOffset(() => { - const rand = Utils.randSeedInt(100); + const rand = randSeedInt(100); if (rand < move.chance) { message = i18next.t("moveTriggers:goingAllOutForAttack", { pokemonName: getPokemonNameWithAffix(user) }); } @@ -3775,9 +3773,9 @@ const doublePowerChanceMessageFunc = (user: Pokemon, target: Pokemon, move: Move export class DoublePowerChanceAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { let rand: number; - globalScene.executeWithSeedOffset(() => rand = Utils.randSeedInt(100), globalScene.currentBattle.turn << 6, globalScene.waveSeed); + globalScene.executeWithSeedOffset(() => rand = randSeedInt(100), globalScene.currentBattle.turn << 6, globalScene.waveSeed); if (rand! < move.chance) { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; power.value *= 2; return true; } @@ -3831,7 +3829,7 @@ export class ConsecutiveUseMultiBasePowerAttr extends ConsecutiveUsePowerMultipl export class WeightPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; const targetWeight = target.getWeight(); const weightThresholds = [ 10, 25, 50, 100, 200 ]; @@ -3865,7 +3863,7 @@ export class ElectroBallPowerAttr extends VariablePowerAttr { * @returns true if the function succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; const statRatio = target.getEffectiveStat(Stat.SPD) / user.getEffectiveStat(Stat.SPD); const statThresholds = [ 0.25, 1 / 3, 0.5, 1, -1 ]; @@ -3900,7 +3898,7 @@ export class GyroBallPowerAttr extends VariablePowerAttr { * @returns true if the function succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; const userSpeed = user.getEffectiveStat(Stat.SPD); if (userSpeed < 1) { // Gen 6+ always have 1 base power @@ -3915,7 +3913,7 @@ export class GyroBallPowerAttr extends VariablePowerAttr { export class LowHpPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; const hpRatio = user.getHpRatio(); switch (true) { @@ -3945,7 +3943,7 @@ export class LowHpPowerAttr extends VariablePowerAttr { export class CompareWeightPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; const userWeight = user.getWeight(); const targetWeight = target.getWeight(); @@ -3979,7 +3977,7 @@ export class CompareWeightPowerAttr extends VariablePowerAttr { export class HpPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = Utils.toDmgValue(150 * user.getHpRatio()); + (args[0] as NumberHolder).value = toDmgValue(150 * user.getHpRatio()); return true; } @@ -4007,7 +4005,7 @@ export class OpponentHighHpPowerAttr extends VariablePowerAttr { * @returns true */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = Utils.toDmgValue(this.maxBasePower * target.getHpRatio()); + (args[0] as NumberHolder).value = toDmgValue(this.maxBasePower * target.getHpRatio()); return true; } @@ -4017,7 +4015,7 @@ export class FirstAttackDoublePowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { console.log(target.getLastXMoves(1), globalScene.currentBattle.turn); if (!target.getLastXMoves(1).find(m => m.turn === globalScene.currentBattle.turn)) { - (args[0] as Utils.NumberHolder).value *= 2; + (args[0] as NumberHolder).value *= 2; return true; } @@ -4029,7 +4027,7 @@ export class FirstAttackDoublePowerAttr extends VariablePowerAttr { export class TurnDamagedDoublePowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (user.turnData.attacksReceived.find(r => r.damage && r.sourceId === target.id)) { - (args[0] as Utils.NumberHolder).value *= 2; + (args[0] as NumberHolder).value *= 2; return true; } @@ -4042,7 +4040,7 @@ const magnitudeMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => { globalScene.executeWithSeedOffset(() => { const magnitudeThresholds = [ 5, 15, 35, 65, 75, 95 ]; - const rand = Utils.randSeedInt(100); + const rand = randSeedInt(100); let m = 0; for (; m < magnitudeThresholds.length; m++) { @@ -4058,14 +4056,14 @@ const magnitudeMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => { export class MagnitudePowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; const magnitudeThresholds = [ 5, 15, 35, 65, 75, 95 ]; const magnitudePowers = [ 10, 30, 50, 70, 90, 100, 110, 150 ]; let rand: number; - globalScene.executeWithSeedOffset(() => rand = Utils.randSeedInt(100), globalScene.currentBattle.turn << 6, globalScene.waveSeed); + globalScene.executeWithSeedOffset(() => rand = randSeedInt(100), globalScene.currentBattle.turn << 6, globalScene.waveSeed); let m = 0; for (; m < magnitudeThresholds.length; m++) { @@ -4083,7 +4081,7 @@ export class MagnitudePowerAttr extends VariablePowerAttr { export class AntiSunlightPowerDecreaseAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!globalScene.arena.weather?.isEffectSuppressed()) { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; const weatherType = globalScene.arena.weather?.weatherType || WeatherType.NONE; switch (weatherType) { case WeatherType.RAIN: @@ -4110,7 +4108,7 @@ export class FriendshipPowerAttr extends VariablePowerAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; const friendshipPower = Math.floor(Math.min(user instanceof PlayerPokemon ? user.friendship : user.species.baseFriendship, 255) / 2.5); power.value = Math.max(!this.invert ? friendshipPower : 102 - friendshipPower, 1); @@ -4126,7 +4124,7 @@ export class FriendshipPowerAttr extends VariablePowerAttr { export class RageFistPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const { hitCount, prevHitCount } = user.battleData; - const basePower: Utils.NumberHolder = args[0]; + const basePower: NumberHolder = args[0]; this.updateHitReceivedCount(user, hitCount, prevHitCount); @@ -4171,7 +4169,7 @@ export class PositiveStatStagePowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const positiveStatStages: number = countPositiveStatStages(user); - (args[0] as Utils.NumberHolder).value += positiveStatStages * 20; + (args[0] as NumberHolder).value += positiveStatStages * 20; return true; } } @@ -4194,7 +4192,7 @@ export class PunishmentPowerAttr extends VariablePowerAttr { */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const positiveStatStages: number = countPositiveStatStages(target); - (args[0] as Utils.NumberHolder).value = Math.min( + (args[0] as NumberHolder).value = Math.min( this.PUNISHMENT_MAX_BASE_POWER, this.PUNISHMENT_MIN_BASE_POWER + positiveStatStages * 20 ); @@ -4210,18 +4208,18 @@ export class PresentPowerAttr extends VariablePowerAttr { */ const firstHit = (user.turnData.hitCount === user.turnData.hitsLeft); - const powerSeed = Utils.randSeedInt(firstHit ? 100 : 80); + const powerSeed = randSeedInt(firstHit ? 100 : 80); if (powerSeed <= 40) { - (args[0] as Utils.NumberHolder).value = 40; + (args[0] as NumberHolder).value = 40; } else if (40 < powerSeed && powerSeed <= 70) { - (args[0] as Utils.NumberHolder).value = 80; + (args[0] as NumberHolder).value = 80; } else if (70 < powerSeed && powerSeed <= 80) { - (args[0] as Utils.NumberHolder).value = 120; + (args[0] as NumberHolder).value = 120; } else if (80 < powerSeed && powerSeed <= 100) { // If this move is multi-hit, disable all other hits user.stopMultiHit(); globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), - Utils.toDmgValue(target.getMaxHp() / 4), i18next.t("moveTriggers:regainedHealth", { pokemonName: getPokemonNameWithAffix(target) }), true)); + toDmgValue(target.getMaxHp() / 4), i18next.t("moveTriggers:regainedHealth", { pokemonName: getPokemonNameWithAffix(target) }), true)); } return true; @@ -4231,7 +4229,7 @@ export class PresentPowerAttr extends VariablePowerAttr { export class WaterShurikenPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (user.species.speciesId === Species.GRENINJA && user.hasAbility(Abilities.BATTLE_BOND) && user.formIndex === 2) { - (args[0] as Utils.NumberHolder).value = 20; + (args[0] as NumberHolder).value = 20; return true; } return false; @@ -4253,7 +4251,7 @@ export class SpitUpPowerAttr extends VariablePowerAttr { const stockpilingTag = user.getTag(StockpilingTag); if (stockpilingTag && stockpilingTag.stockpiledCount > 0) { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; power.value = this.multiplier * stockpilingTag.stockpiledCount; return true; } @@ -4321,12 +4319,12 @@ export class MultiHitPowerIncrementAttr extends VariablePowerAttr { * @param user {@linkcode Pokemon} that used the move * @param target {@linkcode Pokemon} that the move was used on * @param move {@linkcode Move} with this attribute - * @param args [0] {@linkcode Utils.NumberHolder} for final calculated power of move + * @param args [0] {@linkcode NumberHolder} for final calculated power of move * @returns true if attribute application succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const hitsTotal = user.turnData.hitCount - Math.max(user.turnData.hitsLeft, 0); - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; power.value = move.power * (1 + hitsTotal % this.maxHits); @@ -4358,11 +4356,11 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr { * @param user {@linkcode Pokemon} that used the move * @param target N/A * @param move N/A - * @param args [0] {@linkcode Utils.NumberHolder} that holds the resulting power of the move + * @param args [0] {@linkcode NumberHolder} that holds the resulting power of the move * @returns true if attribute application succeeds, false otherwise */ apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; const enemy = user.getOpponent(0); const pokemonActed: Pokemon[] = []; @@ -4374,10 +4372,10 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr { const userAlly = user.getAlly(); const enemyAlly = enemy?.getAlly(); - if (!Utils.isNullOrUndefined(userAlly) && userAlly.turnData.acted) { + if (!isNullOrUndefined(userAlly) && userAlly.turnData.acted) { pokemonActed.push(userAlly); } - if (!Utils.isNullOrUndefined(enemyAlly) && enemyAlly.turnData.acted) { + if (!isNullOrUndefined(enemyAlly) && enemyAlly.turnData.acted) { pokemonActed.push(enemyAlly); } } @@ -4407,7 +4405,7 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr { export class CombinedPledgePowerAttr extends VariablePowerAttr { override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const power = args[0]; - if (!(power instanceof Utils.NumberHolder)) { + if (!(power instanceof NumberHolder)) { return false; } const combinedPledgeMove = user.turnData.combiningPledge; @@ -4426,7 +4424,7 @@ export class CombinedPledgePowerAttr extends VariablePowerAttr { export class CombinedPledgeStabBoostAttr extends MoveAttr { override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const stabMultiplier = args[0]; - if (!(stabMultiplier instanceof Utils.NumberHolder)) { + if (!(stabMultiplier instanceof NumberHolder)) { return false; } const combinedPledgeMove = user.turnData.combiningPledge; @@ -4447,7 +4445,7 @@ export class CombinedPledgeStabBoostAttr extends MoveAttr { export class RoundPowerAttr extends VariablePowerAttr { override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const power = args[0]; - if (!(power instanceof Utils.NumberHolder)) { + if (!(power instanceof NumberHolder)) { return false; } @@ -4572,7 +4570,7 @@ export class TargetAtkUserAtkAttr extends VariableAtkAttr { super(); } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = target.getEffectiveStat(Stat.ATK, target); + (args[0] as NumberHolder).value = target.getEffectiveStat(Stat.ATK, target); return true; } } @@ -4583,7 +4581,7 @@ export class DefAtkAttr extends VariableAtkAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = user.getEffectiveStat(Stat.DEF, target); + (args[0] as NumberHolder).value = user.getEffectiveStat(Stat.DEF, target); return true; } } @@ -4605,7 +4603,7 @@ export class DefDefAttr extends VariableDefAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = target.getEffectiveStat(Stat.DEF, user); + (args[0] as NumberHolder).value = target.getEffectiveStat(Stat.DEF, user); return true; } } @@ -4623,7 +4621,7 @@ export class VariableAccuracyAttr extends MoveAttr { export class ThunderAccuracyAttr extends VariableAccuracyAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!globalScene.arena.weather?.isEffectSuppressed()) { - const accuracy = args[0] as Utils.NumberHolder; + const accuracy = args[0] as NumberHolder; const weatherType = globalScene.arena.weather?.weatherType || WeatherType.NONE; switch (weatherType) { case WeatherType.SUNNY: @@ -4649,7 +4647,7 @@ export class ThunderAccuracyAttr extends VariableAccuracyAttr { export class StormAccuracyAttr extends VariableAccuracyAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!globalScene.arena.weather?.isEffectSuppressed()) { - const accuracy = args[0] as Utils.NumberHolder; + const accuracy = args[0] as NumberHolder; const weatherType = globalScene.arena.weather?.weatherType || WeatherType.NONE; switch (weatherType) { case WeatherType.RAIN: @@ -4680,7 +4678,7 @@ export class AlwaysHitMinimizeAttr extends VariableAccuracyAttr { */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (target.getTag(BattlerTagType.MINIMIZED)) { - const accuracy = args[0] as Utils.NumberHolder; + const accuracy = args[0] as NumberHolder; accuracy.value = -1; return true; @@ -4693,7 +4691,7 @@ export class AlwaysHitMinimizeAttr extends VariableAccuracyAttr { export class ToxicAccuracyAttr extends VariableAccuracyAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (user.isOfType(PokemonType.POISON)) { - const accuracy = args[0] as Utils.NumberHolder; + const accuracy = args[0] as NumberHolder; accuracy.value = -1; return true; } @@ -4705,7 +4703,7 @@ export class ToxicAccuracyAttr extends VariableAccuracyAttr { export class BlizzardAccuracyAttr extends VariableAccuracyAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!globalScene.arena.weather?.isEffectSuppressed()) { - const accuracy = args[0] as Utils.NumberHolder; + const accuracy = args[0] as NumberHolder; const weatherType = globalScene.arena.weather?.weatherType || WeatherType.NONE; if (weatherType === WeatherType.HAIL || weatherType === WeatherType.SNOW) { accuracy.value = -1; @@ -4725,7 +4723,7 @@ export class VariableMoveCategoryAttr extends MoveAttr { export class PhotonGeyserCategoryAttr extends VariableMoveCategoryAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const category = (args[0] as Utils.NumberHolder); + const category = (args[0] as NumberHolder); if (user.getEffectiveStat(Stat.ATK, target, move) > user.getEffectiveStat(Stat.SPATK, target, move)) { category.value = MoveCategory.PHYSICAL; @@ -4745,7 +4743,7 @@ export class PhotonGeyserCategoryAttr extends VariableMoveCategoryAttr { */ export class TeraMoveCategoryAttr extends VariableMoveCategoryAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const category = (args[0] as Utils.NumberHolder); + const category = (args[0] as NumberHolder); if (user.isTerastallized && user.getEffectiveStat(Stat.ATK, target, move, true, true, false, false, true) > user.getEffectiveStat(Stat.SPATK, target, move, true, true, false, false, true)) { @@ -4769,12 +4767,12 @@ export class TeraBlastPowerAttr extends VariablePowerAttr { * @param target n/a * @param move {@linkcode Move} the Move with this attribute (i.e. Tera Blast) * @param args - * - [0] {@linkcode Utils.NumberHolder} the applied move's power, factoring in + * - [0] {@linkcode NumberHolder} the applied move's power, factoring in * previously applied power modifiers. * @returns */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const power = args[0] as Utils.NumberHolder; + const power = args[0] as NumberHolder; if (user.isTerastallized && user.getTeraType() === PokemonType.STELLAR) { power.value = 100; return true; @@ -4794,11 +4792,11 @@ export class StatusCategoryOnAllyAttr extends VariableMoveCategoryAttr { * @param user {@linkcode Pokemon} using the move * @param target {@linkcode Pokemon} target of the move * @param move {@linkcode Move} with this attribute - * @param args [0] {@linkcode Utils.NumberHolder} The category of the move + * @param args [0] {@linkcode NumberHolder} The category of the move * @returns true if the function succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const category = (args[0] as Utils.NumberHolder); + const category = (args[0] as NumberHolder); if (user.getAlly() === target) { category.value = MoveCategory.STATUS; @@ -4811,7 +4809,7 @@ export class StatusCategoryOnAllyAttr extends VariableMoveCategoryAttr { export class ShellSideArmCategoryAttr extends VariableMoveCategoryAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const category = (args[0] as Utils.NumberHolder); + const category = (args[0] as NumberHolder); const predictedPhysDmg = target.getBaseDamage(user, move, MoveCategory.PHYSICAL, true, true, true, true); const predictedSpecDmg = target.getBaseDamage(user, move, MoveCategory.SPECIAL, true, true, true, true); @@ -4836,7 +4834,7 @@ export class VariableMoveTypeAttr extends MoveAttr { export class FormChangeItemTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; - if (!(moveType instanceof Utils.NumberHolder)) { + if (!(moveType instanceof NumberHolder)) { return false; } @@ -4854,7 +4852,7 @@ export class FormChangeItemTypeAttr extends VariableMoveTypeAttr { export class TechnoBlastTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; - if (!(moveType instanceof Utils.NumberHolder)) { + if (!(moveType instanceof NumberHolder)) { return false; } @@ -4888,7 +4886,7 @@ export class TechnoBlastTypeAttr extends VariableMoveTypeAttr { export class AuraWheelTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; - if (!(moveType instanceof Utils.NumberHolder)) { + if (!(moveType instanceof NumberHolder)) { return false; } @@ -4913,7 +4911,7 @@ export class AuraWheelTypeAttr extends VariableMoveTypeAttr { export class RagingBullTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; - if (!(moveType instanceof Utils.NumberHolder)) { + if (!(moveType instanceof NumberHolder)) { return false; } @@ -4941,7 +4939,7 @@ export class RagingBullTypeAttr extends VariableMoveTypeAttr { export class IvyCudgelTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; - if (!(moveType instanceof Utils.NumberHolder)) { + if (!(moveType instanceof NumberHolder)) { return false; } @@ -4976,7 +4974,7 @@ export class IvyCudgelTypeAttr extends VariableMoveTypeAttr { export class WeatherBallTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; - if (!(moveType instanceof Utils.NumberHolder)) { + if (!(moveType instanceof NumberHolder)) { return false; } @@ -5018,12 +5016,12 @@ export class TerrainPulseTypeAttr extends VariableMoveTypeAttr { * @param user {@linkcode Pokemon} using this move * @param target N/A * @param move N/A - * @param args [0] {@linkcode Utils.NumberHolder} The move's type to be modified + * @param args [0] {@linkcode NumberHolder} The move's type to be modified * @returns true if the function succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; - if (!(moveType instanceof Utils.NumberHolder)) { + if (!(moveType instanceof NumberHolder)) { return false; } @@ -5059,7 +5057,7 @@ export class TerrainPulseTypeAttr extends VariableMoveTypeAttr { export class HiddenPowerTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; - if (!(moveType instanceof Utils.NumberHolder)) { + if (!(moveType instanceof NumberHolder)) { return false; } @@ -5094,7 +5092,7 @@ export class TeraBlastTypeAttr extends VariableMoveTypeAttr { */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; - if (!(moveType instanceof Utils.NumberHolder)) { + if (!(moveType instanceof NumberHolder)) { return false; } @@ -5117,12 +5115,12 @@ export class TeraStarstormTypeAttr extends VariableMoveTypeAttr { * @param user the {@linkcode Pokemon} using the move * @param target n/a * @param move n/a - * @param args[0] {@linkcode Utils.NumberHolder} the move type + * @param args[0] {@linkcode NumberHolder} the move type * @returns `true` if the move type is changed to {@linkcode PokemonType.STELLAR}, `false` otherwise */ override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (user.isTerastallized && user.hasSpecies(Species.TERAPAGOS)) { - const moveType = args[0] as Utils.NumberHolder; + const moveType = args[0] as NumberHolder; moveType.value = PokemonType.STELLAR; return true; @@ -5134,7 +5132,7 @@ export class TeraStarstormTypeAttr extends VariableMoveTypeAttr { export class MatchUserTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; - if (!(moveType instanceof Utils.NumberHolder)) { + if (!(moveType instanceof NumberHolder)) { return false; } const userTypes = user.getTypes(true); @@ -5160,7 +5158,7 @@ export class MatchUserTypeAttr extends VariableMoveTypeAttr { export class CombinedPledgeTypeAttr extends VariableMoveTypeAttr { override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; - if (!(moveType instanceof Utils.NumberHolder)) { + if (!(moveType instanceof NumberHolder)) { return false; } @@ -5203,7 +5201,7 @@ export class VariableMoveTypeMultiplierAttr extends MoveAttr { export class NeutralDamageAgainstFlyingTypeMultiplierAttr extends VariableMoveTypeMultiplierAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!target.getTag(BattlerTagType.IGNORE_FLYING)) { - const multiplier = args[0] as Utils.NumberHolder; + const multiplier = args[0] as NumberHolder; //When a flying type is hit, the first hit is always 1x multiplier. if (target.isOfType(PokemonType.FLYING)) { multiplier.value = 1; @@ -5221,11 +5219,11 @@ export class IceNoEffectTypeAttr extends VariableMoveTypeMultiplierAttr { * @param user n/a * @param target The {@linkcode Pokemon} targeted by the move * @param move n/a - * @param args `[0]` a {@linkcode Utils.NumberHolder | NumberHolder} containing a type effectiveness multiplier + * @param args `[0]` a {@linkcode NumberHolder | NumberHolder} containing a type effectiveness multiplier * @returns `true` if this Ice-type immunity applies; `false` otherwise */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const multiplier = args[0] as Utils.NumberHolder; + const multiplier = args[0] as NumberHolder; if (target.isOfType(PokemonType.ICE)) { multiplier.value = 0; return true; @@ -5236,7 +5234,7 @@ export class IceNoEffectTypeAttr extends VariableMoveTypeMultiplierAttr { export class FlyingTypeMultiplierAttr extends VariableMoveTypeMultiplierAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const multiplier = args[0] as Utils.NumberHolder; + const multiplier = args[0] as NumberHolder; multiplier.value *= target.getAttackTypeEffectiveness(PokemonType.FLYING, user); return true; } @@ -5265,7 +5263,7 @@ export class VariableMoveTypeChartAttr extends MoveAttr { */ export class FreezeDryAttr extends VariableMoveTypeChartAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const multiplier = args[0] as Utils.NumberHolder; + const multiplier = args[0] as NumberHolder; const defType = args[1] as PokemonType; if (defType === PokemonType.WATER) { @@ -5279,7 +5277,7 @@ export class FreezeDryAttr extends VariableMoveTypeChartAttr { export class OneHitKOAccuracyAttr extends VariableAccuracyAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const accuracy = args[0] as Utils.NumberHolder; + const accuracy = args[0] as NumberHolder; if (user.level < target.level) { accuracy.value = 0; } else { @@ -5301,7 +5299,7 @@ export class SheerColdAccuracyAttr extends OneHitKOAccuracyAttr { * @returns Returns true if move is successful, false if misses. */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const accuracy = args[0] as Utils.NumberHolder; + const accuracy = args[0] as NumberHolder; if (user.level < target.level) { accuracy.value = 0; } else { @@ -5343,15 +5341,15 @@ export class NoEffectAttr extends MoveAttr { } const crashDamageFunc = (user: Pokemon, move: Move) => { - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled); if (cancelled.value) { return false; } - user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT }); + user.damageAndUpdate(toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT }); globalScene.queueMessage(i18next.t("moveTriggers:keptGoingAndCrashed", { pokemonName: getPokemonNameWithAffix(user) })); - user.turnData.damageTaken += Utils.toDmgValue(user.getMaxHp() / 2); + user.turnData.damageTaken += toDmgValue(user.getMaxHp() / 2); return true; }; @@ -6177,10 +6175,10 @@ export class RevivalBlessingAttr extends MoveEffectAttr { const pokemon = faintedPokemon[user.randSeedInt(faintedPokemon.length)]; const slotIndex = globalScene.getEnemyParty().findIndex((p) => pokemon.id === p.id); pokemon.resetStatus(); - pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); + pokemon.heal(Math.min(toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); globalScene.queueMessage(i18next.t("moveTriggers:revivalBlessing", { pokemonName: getPokemonNameWithAffix(pokemon) }), 0, true); const allyPokemon = user.getAlly(); - if (globalScene.currentBattle.double && globalScene.getEnemyParty().length > 1 && !Utils.isNullOrUndefined(allyPokemon)) { + 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 @@ -6366,7 +6364,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { globalScene.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500); // in double battles redirect potential moves off fled pokemon - if (globalScene.currentBattle.double && !Utils.isNullOrUndefined(allyPokemon)) { + if (globalScene.currentBattle.double && !isNullOrUndefined(allyPokemon)) { globalScene.redirectPokemonMoves(switchOutTarget, allyPokemon); } } @@ -6389,7 +6387,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { } getFailedText(_user: Pokemon, target: Pokemon, _move: Move): string | undefined { - const blockedByAbility = new Utils.BooleanHolder(false); + const blockedByAbility = new BooleanHolder(false); applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility); if (blockedByAbility.value) { return i18next.t("moveTriggers:cannotBeSwitchedOut", { pokemonName: getPokemonNameWithAffix(target) }); @@ -6417,7 +6415,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { return false; } - const blockedByAbility = new Utils.BooleanHolder(false); + const blockedByAbility = new BooleanHolder(false); applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility); return !blockedByAbility.value; } @@ -6776,7 +6774,7 @@ export class RandomMoveAttr extends CallMoveAttr { * @param args Unused */ override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const moveIds = Utils.getEnumValues(Moves).map(m => !this.invalidMoves.has(m) && !allMoves[m].name.endsWith(" (N)") ? m : Moves.NONE); + const moveIds = getEnumValues(Moves).map(m => !this.invalidMoves.has(m) && !allMoves[m].name.endsWith(" (N)") ? m : Moves.NONE); let moveId: Moves = Moves.NONE; do { moveId = this.getMoveOverride() ?? moveIds[user.randSeedInt(moveIds.length)]; @@ -7047,7 +7045,7 @@ export class RepeatMoveAttr extends MoveEffectAttr { const firstTarget = globalScene.getField()[moveTargets[0]]; if (globalScene.currentBattle.double && moveTargets.length === 1 && firstTarget.isFainted() && firstTarget !== target.getAlly()) { const ally = firstTarget.getAlly(); - if (!Utils.isNullOrUndefined(ally) && ally.isActive()) { // ally exists, is not dead and can sponge the blast + if (!isNullOrUndefined(ally) && ally.isActive()) { // ally exists, is not dead and can sponge the blast moveTargets = [ ally.getBattlerIndex() ]; } } @@ -7423,7 +7421,7 @@ export class AbilityCopyAttr extends MoveEffectAttr { user.setTempAbility(target.getAbility()); const ally = user.getAlly(); - if (this.copyToPartner && globalScene.currentBattle?.double && !Utils.isNullOrUndefined(ally) && ally.hp) { // TODO is this the best way to check that the ally is active? + 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 })); ally.setTempAbility(target.getAbility()); } @@ -7839,7 +7837,7 @@ export class VariableTargetAttr extends MoveAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const targetVal = args[0] as Utils.NumberHolder; + const targetVal = args[0] as NumberHolder; targetVal.value = this.targetChangeFunc(user, target, move); return true; } @@ -7930,7 +7928,7 @@ const failOnBossCondition: MoveConditionFunc = (user, target, move) => !target.i const failIfSingleBattle: MoveConditionFunc = (user, target, move) => globalScene.currentBattle.double; const failIfDampCondition: MoveConditionFunc = (user, target, move) => { - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); globalScene.getField(true).map(p=>applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled)); // Queue a message if an ability prevented usage of the move if (cancelled.value) { @@ -8065,7 +8063,7 @@ export class UpperHandCondition extends MoveCondition { export class hitsSameTypeAttr extends VariableMoveTypeMultiplierAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const multiplier = args[0] as Utils.NumberHolder; + const multiplier = args[0] as NumberHolder; if (!user.getTypes().some(type => target.getTypes().includes(type))) { multiplier.value = 0; return true; @@ -8116,7 +8114,7 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr { } const type = validTypes[user.randSeedInt(validTypes.length)]; user.summonData.types = [ type ]; - globalScene.queueMessage(i18next.t("battle:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), type: Utils.toReadableString(PokemonType[type]) })); + globalScene.queueMessage(i18next.t("battle:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), type: toReadableString(PokemonType[type]) })); user.updateInfo(); return true; @@ -8190,7 +8188,7 @@ export type MoveTargetSet = { }; export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveTarget): MoveTargetSet { - const variableTarget = new Utils.NumberHolder(0); + const variableTarget = new NumberHolder(0); user.getOpponents().forEach(p => applyMoveAttrs(VariableTargetAttr, user, p, allMoves[move], variableTarget)); let moveTarget: MoveTarget | undefined; @@ -8218,7 +8216,7 @@ export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveT case MoveTarget.OTHER: case MoveTarget.ALL_NEAR_OTHERS: case MoveTarget.ALL_OTHERS: - set = !Utils.isNullOrUndefined(ally) ? (opponents.concat([ ally ])) : opponents; + set = !isNullOrUndefined(ally) ? (opponents.concat([ ally ])) : opponents; multiple = moveTarget === MoveTarget.ALL_NEAR_OTHERS || moveTarget === MoveTarget.ALL_OTHERS; break; case MoveTarget.NEAR_ENEMY: @@ -8235,21 +8233,21 @@ export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveT return { targets: [ -1 as BattlerIndex ], multiple: false }; case MoveTarget.NEAR_ALLY: case MoveTarget.ALLY: - set = !Utils.isNullOrUndefined(ally) ? [ ally ] : []; + set = !isNullOrUndefined(ally) ? [ ally ] : []; break; case MoveTarget.USER_OR_NEAR_ALLY: case MoveTarget.USER_AND_ALLIES: case MoveTarget.USER_SIDE: - set = !Utils.isNullOrUndefined(ally) ? [ user, ally ] : [ user ]; + set = !isNullOrUndefined(ally) ? [ user, ally ] : [ user ]; multiple = moveTarget !== MoveTarget.USER_OR_NEAR_ALLY; break; case MoveTarget.ALL: case MoveTarget.BOTH_SIDES: - set = (!Utils.isNullOrUndefined(ally) ? [ user, ally ] : [ user ]).concat(opponents); + set = (!isNullOrUndefined(ally) ? [ user, ally ] : [ user ]).concat(opponents); multiple = true; break; case MoveTarget.CURSE: - const extraTargets = !Utils.isNullOrUndefined(ally) ? [ ally ] : []; + const extraTargets = !isNullOrUndefined(ally) ? [ ally ] : []; set = user.getTypes(true).includes(PokemonType.GHOST) ? (opponents.concat(extraTargets)) : [ user ]; break; } @@ -8408,7 +8406,7 @@ export function initMoves() { .attr(AddBattlerTagAttr, BattlerTagType.DISABLED, false, true) .condition((user, target, move) => { const lastRealMove = target.getLastXMoves(-1).find(m => !m.virtual); - return !Utils.isNullOrUndefined(lastRealMove) && lastRealMove.move !== Moves.NONE && lastRealMove.move !== Moves.STRUGGLE; + return !isNullOrUndefined(lastRealMove) && lastRealMove.move !== Moves.NONE && lastRealMove.move !== Moves.STRUGGLE; }) .ignoresSubstitute() .reflectable(), @@ -9716,7 +9714,7 @@ export function initMoves() { .condition(failOnGravityCondition) .condition((_user, target, _move) => ![ Species.DIGLETT, Species.DUGTRIO, Species.ALOLA_DIGLETT, Species.ALOLA_DUGTRIO, Species.SANDYGAST, Species.PALOSSAND, Species.WIGLETT, Species.WUGTRIO ].includes(target.species.speciesId)) .condition((_user, target, _move) => !(target.species.speciesId === Species.GENGAR && target.getFormKey() === "mega")) - .condition((_user, target, _move) => Utils.isNullOrUndefined(target.getTag(BattlerTagType.INGRAIN)) && Utils.isNullOrUndefined(target.getTag(BattlerTagType.IGNORE_FLYING))) + .condition((_user, target, _move) => isNullOrUndefined(target.getTag(BattlerTagType.INGRAIN)) && isNullOrUndefined(target.getTag(BattlerTagType.IGNORE_FLYING))) .attr(AddBattlerTagAttr, BattlerTagType.TELEKINESIS, false, true, 3) .attr(AddBattlerTagAttr, BattlerTagType.FLOATING, false, true, 3) .reflectable(), diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index 11924f93df4..5f88ca083c0 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -12,7 +12,7 @@ import { modifierTypes } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; import { globalScene } from "#app/global-scene"; -import * as Utils from "#app/utils"; +import { randSeedInt } from "#app/utils"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; @@ -46,7 +46,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter const normalConfig = trainerConfigs[normalTrainerType].clone(); let female = false; if (normalConfig.hasGenders) { - female = !!Utils.randSeedInt(2); + female = !!randSeedInt(2); } const normalSpriteKey = normalConfig.getSpriteKey(female, normalConfig.doubleOnly); encounter.enemyPartyConfigs.push({ @@ -76,7 +76,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter hardConfig.setPartyTemplates(hardTemplate); female = false; if (hardConfig.hasGenders) { - female = !!Utils.randSeedInt(2); + female = !!randSeedInt(2); } const hardSpriteKey = hardConfig.getSpriteKey(female, hardConfig.doubleOnly); encounter.enemyPartyConfigs.push({ @@ -96,7 +96,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounter brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func female = false; if (brutalConfig.hasGenders) { - female = !!Utils.randSeedInt(2); + female = !!randSeedInt(2); } const brutalSpriteKey = brutalConfig.getSpriteKey(female, brutalConfig.doubleOnly); encounter.enemyPartyConfigs.push({ diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 53e976cda8a..ff098d4d7dd 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -5,7 +5,7 @@ import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; import type { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro"; import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro"; -import * as Utils from "#app/utils"; +import { randSeedInt } from "#app/utils"; import type { StatusEffect } from "#enums/status-effect"; import type { OptionTextDisplay } from "./mystery-encounter-dialogue"; import type MysteryEncounterDialogue from "./mystery-encounter-dialogue"; @@ -378,13 +378,13 @@ export default class MysteryEncounter implements IMysteryEncounter { } if (truePrimaryPool.length > 0) { // Always choose from the non-overlapping pokemon first - this.primaryPokemon = truePrimaryPool[Utils.randSeedInt(truePrimaryPool.length, 0)]; + this.primaryPokemon = truePrimaryPool[randSeedInt(truePrimaryPool.length, 0)]; return true; } // If there are multiple overlapping pokemon, we're okay - just choose one and take it out of the primary pokemon pool if (overlap.length > 1 || this.secondaryPokemon.length - overlap.length >= 1) { // is this working? - this.primaryPokemon = overlap[Utils.randSeedInt(overlap.length, 0)]; + this.primaryPokemon = overlap[randSeedInt(overlap.length, 0)]; this.secondaryPokemon = this.secondaryPokemon.filter(supp => supp !== this.primaryPokemon); return true; } @@ -394,7 +394,7 @@ export default class MysteryEncounter implements IMysteryEncounter { return false; } // this means we CAN have the same pokemon be a primary and secondary pokemon, so just choose any qualifying one randomly. - this.primaryPokemon = qualified[Utils.randSeedInt(qualified.length, 0)]; + this.primaryPokemon = qualified[randSeedInt(qualified.length, 0)]; return true; } diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index f3a06242a13..a9f6b787878 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -30,8 +30,7 @@ import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-optio import type { PartyOption, PokemonSelectFilter } from "#app/ui/party-ui-handler"; import { PartyUiMode } from "#app/ui/party-ui-handler"; import { Mode } from "#app/ui/ui"; -import * as Utils from "#app/utils"; -import { isNullOrUndefined, randSeedInt, randSeedItem } from "#app/utils"; +import { isNullOrUndefined, randSeedInt, randomString, randSeedItem } from "#app/utils"; import type { BattlerTagType } from "#enums/battler-tag-type"; import { Biome } from "#enums/biome"; import type { TrainerType } from "#enums/trainer-type"; @@ -168,7 +167,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig): const doubleTrainer = trainerConfig.doubleOnly || (trainerConfig.hasDouble && !!partyConfig.doubleBattle); doubleBattle = doubleTrainer; - const trainerFemale = isNullOrUndefined(partyConfig.female) ? !!Utils.randSeedInt(2) : partyConfig.female; + const trainerFemale = isNullOrUndefined(partyConfig.female) ? !!randSeedInt(2) : partyConfig.female; const newTrainer = new Trainer( trainerConfig.trainerType, doubleTrainer ? TrainerVariant.DOUBLE : trainerFemale ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT, @@ -286,7 +285,7 @@ export async function initBattleWithEnemyConfig(partyConfig: EnemyPartyConfig): // Generate new id, reset status and HP in case using data source if (config.dataSource) { - enemyPokemon.id = Utils.randSeedInt(4294967296); + enemyPokemon.id = randSeedInt(4294967296); } // Set form @@ -1115,7 +1114,7 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) { const validMEfloorsByBiome = new Map(biomes.map(b => [b, 0])); let currentBiome = Biome.TOWN; let currentArena = globalScene.newArena(currentBiome); - globalScene.setSeed(Utils.randomString(24)); + globalScene.setSeed(randomString(24)); globalScene.resetSeed(); for (let i = 10; i < 180; i++) { // Boss @@ -1130,16 +1129,16 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) { globalScene.executeWithSeedOffset(() => { biomes = (biomeLinks[currentBiome] as (Biome | [Biome, number])[]) .filter(b => { - return !Array.isArray(b) || !Utils.randSeedInt(b[1]); + return !Array.isArray(b) || !randSeedInt(b[1]); }) .map(b => (!Array.isArray(b) ? b : b[0])); }, i * 100); if (biomes! && biomes.length > 0) { const specialBiomes = biomes.filter(b => alwaysPickTheseBiomes.includes(b)); if (specialBiomes.length > 0) { - currentBiome = specialBiomes[Utils.randSeedInt(specialBiomes.length)]; + currentBiome = specialBiomes[randSeedInt(specialBiomes.length)]; } else { - currentBiome = biomes[Utils.randSeedInt(biomes.length)]; + currentBiome = biomes[randSeedInt(biomes.length)]; } } } else if (biomeLinks.hasOwnProperty(currentBiome)) { @@ -1167,7 +1166,7 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) { // Otherwise, roll encounter - const roll = Utils.randSeedInt(256); + const roll = randSeedInt(256); validMEfloorsByBiome.set(Biome[currentBiome], (validMEfloorsByBiome.get(Biome[currentBiome]) ?? 0) + 1); // If total number of encounters is lower than expected for the run, slightly favor a new encounter @@ -1192,7 +1191,7 @@ export function calculateMEAggregateStats(baseSpawnWeight: number) { tierWeights[1] = tierWeights[1] - 4 * numEncounters[1]; const totalWeight = tierWeights.reduce((a, b) => a + b); - const tierValue = Utils.randSeedInt(totalWeight); + const tierValue = randSeedInt(totalWeight); const commonThreshold = totalWeight - tierWeights[0]; // 64 - 32 = 32 const uncommonThreshold = totalWeight - tierWeights[0] - tierWeights[1]; // 64 - 32 - 16 = 16 const rareThreshold = totalWeight - tierWeights[0] - tierWeights[1] - tierWeights[2]; // 64 - 32 - 16 - 10 = 6 @@ -1281,7 +1280,7 @@ export function calculateRareSpawnAggregateStats(luckValue: number) { const calculateNumRareEncounters = (): any[] => { const bossEncountersByRarity = [0, 0, 0, 0]; - globalScene.setSeed(Utils.randomString(24)); + globalScene.setSeed(randomString(24)); globalScene.resetSeed(); // There are 12 wild boss floors for (let i = 0; i < 12; i++) { @@ -1291,7 +1290,7 @@ export function calculateRareSpawnAggregateStats(luckValue: number) { if (!Number.isNaN(luckValue)) { luckModifier = luckValue * 0.5; } - const tierValue = Utils.randSeedInt(64 - luckModifier); + const tierValue = randSeedInt(64 - luckModifier); const tier = tierValue >= 20 ? BiomePoolTier.BOSS diff --git a/src/data/nature.ts b/src/data/nature.ts index e23d92c14b0..2ab4723c10d 100644 --- a/src/data/nature.ts +++ b/src/data/nature.ts @@ -1,4 +1,4 @@ -import * as Utils from "../utils"; +import { toReadableString } from "#app/utils"; import { TextStyle, getBBCodeFrag } from "../ui/text"; import { Nature } from "#enums/nature"; import { UiTheme } from "#enums/ui-theme"; @@ -12,7 +12,7 @@ export function getNatureName( ignoreBBCode = false, uiTheme: UiTheme = UiTheme.DEFAULT, ): string { - let ret = Utils.toReadableString(Nature[nature]); + let ret = toReadableString(Nature[nature]); //Translating nature if (i18next.exists(`nature:${ret}`)) { ret = i18next.t(`nature:${ret}` as any); diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index a8942a39880..a27c00121dc 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -8,7 +8,7 @@ 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 * as Utils from "#app/utils"; +import { isNullOrUndefined, capitalizeString, randSeedInt, randSeedGauss, randSeedItem } from "#app/utils"; import { uncatchableSpecies } from "#app/data/balance/biomes"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { GrowthRate } from "#app/data/exp"; @@ -290,7 +290,7 @@ export abstract class PokemonSpeciesForm { * @returns The id of the ability */ getPassiveAbility(formIndex?: number): Abilities { - if (Utils.isNullOrUndefined(formIndex)) { + if (isNullOrUndefined(formIndex)) { formIndex = this.formIndex; } let starterSpeciesId = this.speciesId; @@ -626,7 +626,7 @@ export abstract class PokemonSpeciesForm { const spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant, back) .replace("variant/", "") .replace(/_[1-3]$/, ""); - if (!Utils.isNullOrUndefined(variant)) { + if (!isNullOrUndefined(variant)) { loadPokemonVariantAssets(spriteKey, spritePath, variant).then(() => resolve()); } }); @@ -852,8 +852,8 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali */ getFormNameToDisplay(formIndex = 0, append = false): string { const formKey = this.forms?.[formIndex!]?.formKey; - const formText = Utils.capitalizeString(formKey, "-", false, false) || ""; - const speciesName = Utils.capitalizeString(Species[this.speciesId], "_", true, false); + const formText = capitalizeString(formKey, "-", false, false) || ""; + const speciesName = capitalizeString(Species[this.speciesId], "_", true, false); let ret = ""; const region = this.getRegion(); @@ -884,7 +884,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali if (i18next.exists(i18key)) { ret = i18next.t(i18key); } else { - const rootSpeciesName = Utils.capitalizeString(Species[this.getRootSpeciesId()], "_", true, false); + const rootSpeciesName = capitalizeString(Species[this.getRootSpeciesId()], "_", true, false); const i18RootKey = `pokemonForm:${rootSpeciesName}${formText}`; ret = i18next.exists(i18RootKey) ? i18next.t(i18RootKey) : formText; } @@ -1079,7 +1079,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali return this.speciesId; } - const randValue = evolutionPool.size === 1 ? 0 : Utils.randSeedInt(totalWeight); + const randValue = evolutionPool.size === 1 ? 0 : randSeedInt(totalWeight); for (const weight of evolutionPool.keys()) { if (randValue < weight) { @@ -1164,7 +1164,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali Math.min( Math.max( evolution?.level! + - Math.round(Utils.randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5) - + Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5) - 1, 2, evolution?.level!, @@ -1182,7 +1182,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali Math.min( Math.max( lastPrevolutionLevel + - Math.round(Utils.randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5), + Math.round(randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution?.wildDelay!, 0.5) * 5), lastPrevolutionLevel + 1, evolution?.level!, ), @@ -1367,7 +1367,7 @@ export function getPokerusStarters(): PokemonSpecies[] { globalScene.executeWithSeedOffset( () => { while (pokerusStarters.length < POKERUS_STARTER_COUNT) { - const randomSpeciesId = Number.parseInt(Utils.randSeedItem(Object.keys(speciesStarterCosts)), 10); + const randomSpeciesId = Number.parseInt(randSeedItem(Object.keys(speciesStarterCosts)), 10); const species = getPokemonSpecies(randomSpeciesId); if (!pokerusStarters.includes(species)) { pokerusStarters.push(species); diff --git a/src/data/trainer-names.ts b/src/data/trainer-names.ts index 26cea19070f..195e5041d28 100644 --- a/src/data/trainer-names.ts +++ b/src/data/trainer-names.ts @@ -1,12 +1,12 @@ import { TrainerType } from "#enums/trainer-type"; -import * as Utils from "../utils"; +import { toReadableString } from "#app/utils"; class TrainerNameConfig { public urls: string[]; public femaleUrls: string[] | null; constructor(type: TrainerType, ...urls: string[]) { - this.urls = urls.length ? urls : [Utils.toReadableString(TrainerType[type]).replace(/ /g, "_")]; + this.urls = urls.length ? urls : [toReadableString(TrainerType[type]).replace(/ /g, "_")]; } hasGenderVariant(...femaleUrls: string[]): TrainerNameConfig { diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index 5fab70971ec..0ab7119dab9 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import { modifierTypes } from "#app/modifier/modifier-type"; import { PokemonMove } from "#app/field/pokemon"; -import * as Utils from "#app/utils"; +import { toReadableString, isNullOrUndefined, randSeedItem, randSeedInt } from "#app/utils"; import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { tmSpecies } from "#app/data/balance/tms"; @@ -139,7 +139,7 @@ export class TrainerConfig { constructor(trainerType: TrainerType, allowLegendaries?: boolean) { this.trainerType = trainerType; this.trainerAI = new TrainerAI(); - this.name = Utils.toReadableString(TrainerType[this.getDerivedType()]); + this.name = toReadableString(TrainerType[this.getDerivedType()]); this.battleBgm = "battle_trainer"; this.mixedBattleBgm = "battle_trainer"; this.victoryBgm = "victory_trainer"; @@ -482,10 +482,10 @@ export class TrainerConfig { .fill(null) .map((_, i) => i) .filter(i => shedinjaCanTera || party[i].species.speciesId !== Species.SHEDINJA); // Shedinja can only Tera on Bug specialty type (or no specialty type) - const setPartySlot = !Utils.isNullOrUndefined(slot) ? Phaser.Math.Wrap(slot, 0, party.length) : -1; // If we have a tera slot defined, wrap it to party size. + const setPartySlot = !isNullOrUndefined(slot) ? Phaser.Math.Wrap(slot, 0, party.length) : -1; // If we have a tera slot defined, wrap it to party size. for (let t = 0; t < Math.min(count(), party.length); t++) { const randomIndex = - partyMemberIndexes.indexOf(setPartySlot) > -1 ? setPartySlot : Utils.randSeedItem(partyMemberIndexes); + partyMemberIndexes.indexOf(setPartySlot) > -1 ? setPartySlot : randSeedItem(partyMemberIndexes); partyMemberIndexes.splice(partyMemberIndexes.indexOf(randomIndex), 1); if (this.hasSpecialtyType()) { party[randomIndex].teraType = this.specialtyType; @@ -555,7 +555,7 @@ export class TrainerConfig { initI18n(); } - if (!Utils.isNullOrUndefined(specialtyType)) { + if (!isNullOrUndefined(specialtyType)) { this.setSpecialtyType(specialtyType); } @@ -636,7 +636,7 @@ export class TrainerConfig { } this.setPartyMemberFunc(-(s + 1), getRandomPartyMemberFunc(speciesPool)); }); - if (!Utils.isNullOrUndefined(specialtyType)) { + if (!isNullOrUndefined(specialtyType)) { this.setSpeciesFilter(p => p.isOfType(specialtyType)); this.setSpecialtyType(specialtyType); } @@ -749,7 +749,7 @@ export class TrainerConfig { }); // Set species filter and specialty type if provided, otherwise filter by base total. - if (!Utils.isNullOrUndefined(specialtyType)) { + if (!isNullOrUndefined(specialtyType)) { this.setSpeciesFilter(p => p.isOfType(specialtyType) && p.baseTotal >= ELITE_FOUR_MINIMUM_BST); this.setSpecialtyType(specialtyType); } else { @@ -927,7 +927,7 @@ export class TrainerConfig { * @returns true if specialtyType is defined and not Type.UNKNOWN */ hasSpecialtyType(): boolean { - return !Utils.isNullOrUndefined(this.specialtyType) && this.specialtyType !== PokemonType.UNKNOWN; + return !isNullOrUndefined(this.specialtyType) && this.specialtyType !== PokemonType.UNKNOWN; } /** @@ -1006,7 +1006,7 @@ export function getRandomPartyMemberFunc( postProcess?: (enemyPokemon: EnemyPokemon) => void, ) { return (level: number, strength: PartyMemberStrength) => { - let species = Utils.randSeedItem(speciesPool); + let species = randSeedItem(speciesPool); if (!ignoreEvolution) { species = getPokemonSpecies(species).getTrainerSpeciesForLevel( level, @@ -3549,7 +3549,7 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 5, getRandomPartyMemberFunc([Species.URSHIFU], TrainerSlot.TRAINER, true, p => { - p.formIndex = Utils.randSeedInt(2, 2); // Random G-Max Urshifu + p.formIndex = randSeedInt(2, 2); // Random G-Max Urshifu p.generateAndPopulateMoveset(); p.generateName(); p.gender = Gender.MALE; @@ -3659,10 +3659,10 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 4, getRandomPartyMemberFunc([Species.OGERPON], TrainerSlot.TRAINER, true, p => { - p.formIndex = Utils.randSeedInt(4); // Random Ogerpon Tera Mask + p.formIndex = randSeedInt(4); // Random Ogerpon Tera Mask p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; - if (!p.moveset.some(move => !Utils.isNullOrUndefined(move) && move.moveId === Moves.IVY_CUDGEL)) { + if (!p.moveset.some(move => !isNullOrUndefined(move) && move.moveId === Moves.IVY_CUDGEL)) { // Check if Ivy Cudgel is in the moveset, if not, replace the first move with Ivy Cudgel. p.moveset[0] = new PokemonMove(Moves.IVY_CUDGEL); } @@ -4713,10 +4713,10 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 2, getRandomPartyMemberFunc([Species.SILVALLY], TrainerSlot.TRAINER, true, p => { - p.formIndex = Utils.randSeedInt(18); // Random Silvally Form + p.formIndex = randSeedInt(18); // Random Silvally Form p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ROGUE_BALL; - if (!p.moveset.some(move => !Utils.isNullOrUndefined(move) && move.moveId === Moves.MULTI_ATTACK)) { + if (!p.moveset.some(move => !isNullOrUndefined(move) && move.moveId === Moves.MULTI_ATTACK)) { // Check if Multi Attack is in the moveset, if not, replace the first move with Multi Attack. p.moveset[0] = new PokemonMove(Moves.MULTI_ATTACK); } @@ -4833,8 +4833,8 @@ export const trainerConfigs: TrainerConfigs = { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; - p.formIndex = Utils.randSeedInt(4, 1); // Shock, Burn, Chill, or Douse Drive - if (!p.moveset.some(move => !Utils.isNullOrUndefined(move) && move.moveId === Moves.TECHNO_BLAST)) { + p.formIndex = randSeedInt(4, 1); // Shock, Burn, Chill, or Douse Drive + if (!p.moveset.some(move => !isNullOrUndefined(move) && move.moveId === Moves.TECHNO_BLAST)) { // Check if Techno Blast is in the moveset, if not, replace the first move with Techno Blast. p.moveset[2] = new PokemonMove(Moves.TECHNO_BLAST); } @@ -5006,7 +5006,7 @@ export const trainerConfigs: TrainerConfigs = { 1, getRandomPartyMemberFunc([Species.ROTOM], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); - p.formIndex = Utils.randSeedInt(5, 1); // Heat, Wash, Frost, Fan, or Mow + p.formIndex = randSeedInt(5, 1); // Heat, Wash, Frost, Fan, or Mow }), ) .setPartyMemberFunc( @@ -5019,7 +5019,7 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc( 3, getRandomPartyMemberFunc([Species.REVAVROOM], TrainerSlot.TRAINER, true, p => { - p.formIndex = Utils.randSeedInt(5, 1); // Random Starmobile form + p.formIndex = randSeedInt(5, 1); // Random Starmobile form p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ROGUE_BALL; }), diff --git a/src/data/weather.ts b/src/data/weather.ts index 34978232377..a8dd0a66492 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -5,7 +5,7 @@ import type Pokemon from "../field/pokemon"; import { PokemonType } from "#enums/pokemon-type"; import type Move from "./moves/move"; import { AttackMove } from "./moves/move"; -import * as Utils from "../utils"; +import { randSeedInt } from "#app/utils"; import { SuppressWeatherEffectAbAttr } from "./ability"; import { TerrainType, getTerrainName } from "./terrain"; import i18next from "i18next"; @@ -416,7 +416,7 @@ export function getRandomWeatherType(arena: Arena): WeatherType { totalWeight += w.weight; } - const rand = Utils.randSeedInt(totalWeight); + const rand = randSeedInt(totalWeight); let w = 0; for (const weather of weatherPool) { w += weather.weight; diff --git a/src/field/arena.ts b/src/field/arena.ts index cf48647e45e..adc3123ce81 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -1,8 +1,7 @@ import { globalScene } from "#app/global-scene"; import type { BiomeTierTrainerPools, PokemonPools } from "#app/data/balance/biomes"; import { biomePokemonPools, BiomePoolTier, biomeTrainerPools } from "#app/data/balance/biomes"; -import type { Constructor } from "#app/utils"; -import * as Utils from "#app/utils"; +import { randSeedInt, NumberHolder, isNullOrUndefined, type Constructor } from "#app/utils"; import type PokemonSpecies from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { @@ -124,7 +123,7 @@ export class Arena { if (typeof luckValue !== "undefined") { luckModifier = luckValue * (isBossSpecies ? 0.5 : 2); } - const tierValue = Utils.randSeedInt(randVal - luckModifier); + const tierValue = randSeedInt(randVal - luckModifier); let tier = !isBossSpecies ? tierValue >= 156 ? BiomePoolTier.COMMON @@ -153,7 +152,7 @@ export class Arena { if (!tierPool.length) { ret = globalScene.randomSpecies(waveIndex, level); } else { - const entry = tierPool[Utils.randSeedInt(tierPool.length)]; + const entry = tierPool[randSeedInt(tierPool.length)]; let species: Species; if (typeof entry === "number") { species = entry as Species; @@ -164,7 +163,7 @@ export class Arena { if (level >= levelThreshold) { const speciesIds = entry[levelThreshold]; if (speciesIds.length > 1) { - species = speciesIds[Utils.randSeedInt(speciesIds.length)]; + species = speciesIds[randSeedInt(speciesIds.length)]; } else { species = speciesIds[0]; } @@ -211,7 +210,7 @@ export class Arena { !!this.trainerPool[BiomePoolTier.BOSS].length && (globalScene.gameMode.isTrainerBoss(waveIndex, this.biomeType, globalScene.offsetGym) || isBoss); console.log(isBoss, this.trainerPool); - const tierValue = Utils.randSeedInt(!isTrainerBoss ? 512 : 64); + const tierValue = randSeedInt(!isTrainerBoss ? 512 : 64); let tier = !isTrainerBoss ? tierValue >= 156 ? BiomePoolTier.COMMON @@ -235,7 +234,7 @@ export class Arena { tier--; } const tierPool = this.trainerPool[tier] || []; - return !tierPool.length ? TrainerType.BREEDER : tierPool[Utils.randSeedInt(tierPool.length)]; + return !tierPool.length ? TrainerType.BREEDER : tierPool[randSeedInt(tierPool.length)]; } getSpeciesFormIndex(species: PokemonSpecies): number { @@ -336,9 +335,9 @@ export class Arena { return false; } - const weatherDuration = new Utils.NumberHolder(0); + const weatherDuration = new NumberHolder(0); - if (!Utils.isNullOrUndefined(user)) { + if (!isNullOrUndefined(user)) { weatherDuration.value = 5; globalScene.applyModifier(FieldEffectModifier, user.isPlayer(), user, weatherDuration); } @@ -417,9 +416,9 @@ export class Arena { const oldTerrainType = this.terrain?.terrainType || TerrainType.NONE; - const terrainDuration = new Utils.NumberHolder(0); + const terrainDuration = new NumberHolder(0); - if (!Utils.isNullOrUndefined(user)) { + if (!isNullOrUndefined(user)) { terrainDuration.value = 5; globalScene.applyModifier(FieldEffectModifier, user.isPlayer(), user, terrainDuration); } @@ -1013,7 +1012,7 @@ export class ArenaBase extends Phaser.GameObjects.Container { if (!this.player) { globalScene.executeWithSeedOffset( () => { - this.propValue = propValue === undefined ? (hasProps ? Utils.randSeedInt(8) : 0) : propValue; + this.propValue = propValue === undefined ? (hasProps ? randSeedInt(8) : 0) : propValue; this.props.forEach((prop, p) => { const propKey = `${biomeKey}_b${hasProps ? `_${p + 1}` : ""}`; prop.setTexture(propKey); diff --git a/src/field/damage-number-handler.ts b/src/field/damage-number-handler.ts index 9e0010a0c10..a527b148fff 100644 --- a/src/field/damage-number-handler.ts +++ b/src/field/damage-number-handler.ts @@ -2,7 +2,7 @@ import { TextStyle, addTextObject } from "../ui/text"; import type { DamageResult } from "./pokemon"; import type Pokemon from "./pokemon"; import { HitResult } from "./pokemon"; -import * as Utils from "../utils"; +import { formatStat, fixedInt } from "#app/utils"; import type { BattlerIndex } from "../battle"; import { globalScene } from "#app/global-scene"; @@ -30,7 +30,7 @@ export default class DamageNumberHandler { const damageNumber = addTextObject( target.x, -(globalScene.game.canvas.height / 6) + target.y - target.getSprite().height / 2, - Utils.formatStat(amount, true), + formatStat(amount, true), TextStyle.SUMMARY, ); damageNumber.setName("text-damage-number"); @@ -86,14 +86,14 @@ export default class DamageNumberHandler { if (globalScene.damageNumbersMode === 1) { globalScene.tweens.add({ targets: damageNumber, - duration: Utils.fixedInt(750), + duration: fixedInt(750), alpha: 1, y: "-=32", }); globalScene.tweens.add({ delay: 375, targets: damageNumber, - duration: Utils.fixedInt(625), + duration: fixedInt(625), alpha: 0, ease: "Sine.easeIn", onComplete: () => { @@ -110,7 +110,7 @@ export default class DamageNumberHandler { targets: damageNumber, tweens: [ { - duration: Utils.fixedInt(250), + duration: fixedInt(250), alpha: 1, scaleX: 0.75 * baseScale, scaleY: 1.25 * baseScale, @@ -118,7 +118,7 @@ export default class DamageNumberHandler { ease: "Cubic.easeOut", }, { - duration: Utils.fixedInt(175), + duration: fixedInt(175), alpha: 1, scaleX: 0.875 * baseScale, scaleY: 1.125 * baseScale, @@ -126,59 +126,59 @@ export default class DamageNumberHandler { ease: "Cubic.easeIn", }, { - duration: Utils.fixedInt(100), + duration: fixedInt(100), scaleX: 1.25 * baseScale, scaleY: 0.75 * baseScale, ease: "Cubic.easeOut", }, { - duration: Utils.fixedInt(175), + duration: fixedInt(175), scaleX: 0.875 * baseScale, scaleY: 1.125 * baseScale, y: "-=8", ease: "Cubic.easeOut", }, { - duration: Utils.fixedInt(50), + duration: fixedInt(50), scaleX: 0.925 * baseScale, scaleY: 1.075 * baseScale, y: "+=8", ease: "Cubic.easeIn", }, { - duration: Utils.fixedInt(100), + duration: fixedInt(100), scaleX: 1.125 * baseScale, scaleY: 0.875 * baseScale, ease: "Cubic.easeOut", }, { - duration: Utils.fixedInt(175), + duration: fixedInt(175), scaleX: 0.925 * baseScale, scaleY: 1.075 * baseScale, y: "-=4", ease: "Cubic.easeOut", }, { - duration: Utils.fixedInt(50), + duration: fixedInt(50), scaleX: 0.975 * baseScale, scaleY: 1.025 * baseScale, y: "+=4", ease: "Cubic.easeIn", }, { - duration: Utils.fixedInt(100), + duration: fixedInt(100), scaleX: 1.075 * baseScale, scaleY: 0.925 * baseScale, ease: "Cubic.easeOut", }, { - duration: Utils.fixedInt(25), + duration: fixedInt(25), scaleX: baseScale, scaleY: baseScale, ease: "Cubic.easeOut", }, { - delay: Utils.fixedInt(500), + delay: fixedInt(500), alpha: 0, onComplete: () => { this.damageNumbers diff --git a/src/field/pokemon-sprite-sparkle-handler.ts b/src/field/pokemon-sprite-sparkle-handler.ts index 0d5dcca7989..d2f69500258 100644 --- a/src/field/pokemon-sprite-sparkle-handler.ts +++ b/src/field/pokemon-sprite-sparkle-handler.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import Pokemon from "./pokemon"; -import * as Utils from "../utils"; +import { fixedInt, randInt } from "#app/utils"; export default class PokemonSpriteSparkleHandler { private sprites: Set; @@ -9,7 +9,7 @@ export default class PokemonSpriteSparkleHandler { this.sprites = new Set(); globalScene.tweens.addCounter({ - duration: Utils.fixedInt(200), + duration: fixedInt(200), from: 0, to: 1, yoyo: true, @@ -36,7 +36,7 @@ export default class PokemonSpriteSparkleHandler { const parent = (pokemon || s).parentContainer; const texture = s.texture; const [width, height] = [texture.source[0].width, texture.source[0].height]; - const [pixelX, pixelY] = [Utils.randInt(width), Utils.randInt(height)]; + const [pixelX, pixelY] = [randInt(width), randInt(height)]; const ratioX = s.width / width; const ratioY = s.height / height; const pixel = texture.manager.getPixel(pixelX, pixelY, texture.key, "__BASE"); @@ -51,7 +51,7 @@ export default class PokemonSpriteSparkleHandler { sparkle.setName("sprite-tera-sparkle"); sparkle.play("tera_sparkle"); parent.add(sparkle); - s.scene.time.delayedCall(Utils.fixedInt(Math.floor((1000 / 12) * 13)), () => sparkle.destroy()); + s.scene.time.delayedCall(fixedInt(Math.floor((1000 / 12) * 13)), () => sparkle.destroy()); } } } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 72da3f1ed6f..8fc75ca657d 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2,7 +2,7 @@ import Phaser from "phaser"; import type { AnySound } from "#app/battle-scene"; import type BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; -import type { Variant, VariantSet } from "#app/sprites/variant"; +import type { Variant } from "#app/sprites/variant"; import { populateVariantColors, variantColorCache } from "#app/sprites/variant"; import { variantData } from "#app/sprites/variant"; import BattleInfo, { @@ -55,9 +55,7 @@ import { getStarterValueFriendshipCap, speciesStarterCosts, } from "#app/data/balance/starters"; -import type { Constructor } from "#app/utils"; -import { isNullOrUndefined, randSeedInt, type nil } from "#app/utils"; -import * as Utils from "#app/utils"; +import { NumberHolder, randSeedInt, getIvsFromId, BooleanHolder, randSeedItem, isNullOrUndefined, getEnumValues, toDmgValue, fixedInt, rgbaToInt, rgbHexToRgba, rgbToHsv, deltaRgb, isBetween, type nil, type Constructor } from "#app/utils"; import type { TypeDamageMultiplier } from "#app/data/type"; import { getTypeDamageMultiplier, getTypeRgb } from "#app/data/type"; import { PokemonType } from "#enums/pokemon-type"; @@ -96,7 +94,6 @@ import { } from "#app/modifier/modifier"; import { PokeballType } from "#enums/pokeball"; import { Gender } from "#app/data/gender"; -import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; import { Status, getRandomStatus } from "#app/data/status-effect"; import type { SpeciesFormEvolution, @@ -176,10 +173,7 @@ import { MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, - CheckTrappedAbAttr, - PostSetStatusAbAttr, - applyPostSetStatusAbAttrs, - InfiltratorAbAttr, + CheckTrappedAbAttr, InfiltratorAbAttr, AlliedFieldDamageReductionAbAttr, PostDamageAbAttr, applyPostDamageAbAttrs, @@ -193,7 +187,7 @@ import { PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr, applyAllyStatMultiplierAbAttrs, AllyStatMultiplierAbAttr, - MoveAbilityBypassAbAttr, + MoveAbilityBypassAbAttr } from "#app/data/ability"; import type PokemonData from "#app/system/pokemon-data"; import { BattlerIndex } from "#app/battle"; @@ -220,8 +214,7 @@ import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeLapseTeraTrigger, SpeciesFormChangeMoveLearnedTrigger, - SpeciesFormChangePostMoveTrigger, - SpeciesFormChangeStatusEffectTrigger, + SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms"; import { TerrainType } from "#app/data/terrain"; import type { TrainerSlot } from "#enums/trainer-slot"; @@ -263,7 +256,6 @@ import { Nature } from "#enums/nature"; import { StatusEffect } from "#enums/status-effect"; import { doShinySparkleAnim } from "#app/field/anims"; import { MoveFlags } from "#enums/MoveFlags"; -import { hasExpSprite } from "#app/sprites/sprite-utils"; import { timedEventManager } from "#app/global-event-manager"; import { loadMoveAnimations } from "#app/sprites/pokemon-asset-loader"; import { ResetStatusPhase } from "#app/phases/reset-status-phase"; @@ -369,7 +361,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { throw `Cannot create a player Pokemon for species '${species.getName(formIndex)}'`; } - const hiddenAbilityChance = new Utils.NumberHolder( + const hiddenAbilityChance = new NumberHolder( BASE_HIDDEN_ABILITY_CHANCE, ); if (!this.hasTrainer()) { @@ -390,8 +382,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.abilityIndex = abilityIndex; // Use the provided ability index if it is defined } else { // If abilityIndex is not provided, determine it based on species and hidden ability - const hasHiddenAbility = !Utils.randSeedInt(hiddenAbilityChance.value); - const randAbilityIndex = Utils.randSeedInt(2); + const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value); + const randAbilityIndex = randSeedInt(2); if (species.abilityHidden && hasHiddenAbility) { // If the species has a hidden ability and the hidden ability is present this.abilityIndex = 2; @@ -467,8 +459,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.isTerastallized = dataSource.isTerastallized; this.stellarTypesBoosted = dataSource.stellarTypesBoosted ?? []; } else { - this.id = Utils.randSeedInt(4294967296); - this.ivs = ivs || Utils.getIvsFromId(this.id); + this.id = randSeedInt(4294967296); + this.ivs = ivs || getIvsFromId(this.id); if (this.gender === undefined) { this.generateGender(); @@ -511,7 +503,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.pokerus = false; if (level > 1) { - const fused = new Utils.BooleanHolder( + const fused = new BooleanHolder( globalScene.gameMode.isSplicedOnly, ); if (!fused.value && !this.isPlayer() && !this.hasTrainer()) { @@ -528,7 +520,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { (this.fusionShiny ? this.fusionVariant + 1 : 0); this.fusionLuck = this.luck; - this.teraType = Utils.randSeedItem(this.getTypes(false, false, true)); + this.teraType = randSeedItem(this.getTypes(false, false, true)); this.isTerastallized = false; this.stellarTypesBoosted = []; } @@ -636,7 +628,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns {boolean} `true` if pokemon is allowed in battle */ public isAllowedInChallenge(): boolean { - const challengeAllowed = new Utils.BooleanHolder(true); + const challengeAllowed = new BooleanHolder(true); applyChallenges( ChallengeType.POKEMON_IN_BATTLE, this, @@ -1315,7 +1307,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns the final critical-hit stage value */ getCritStage(source: Pokemon, move: Move): number { - const critStage = new Utils.NumberHolder(0); + const critStage = new NumberHolder(0); applyMoveAttrs(HighCritAttr, source, this, move, critStage); globalScene.applyModifiers( CritBoosterModifier, @@ -1370,7 +1362,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { simulated = true, ignoreHeldItems = false, ): number { - const statValue = new Utils.NumberHolder(this.getStat(stat, false)); + const statValue = new NumberHolder(this.getStat(stat, false)); if (!ignoreHeldItems) { globalScene.applyModifiers( StatBoosterModifier, @@ -1382,7 +1374,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } // The Ruin abilities here are never ignored, but they reveal themselves on summon anyway - const fieldApplied = new Utils.BooleanHolder(false); + const fieldApplied = new BooleanHolder(false); for (const pokemon of globalScene.getField(true)) { applyFieldStatMultiplierAbAttrs( FieldMultiplyStatAbAttr, @@ -1408,7 +1400,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const ally = this.getAlly(); - if (!Utils.isNullOrUndefined(ally)) { + if (!isNullOrUndefined(ally)) { applyAllyStatMultiplierAbAttrs(AllyStatMultiplierAbAttr, ally, stat, statValue, simulated, this, move?.hasFlag(MoveFlags.IGNORE_ABILITIES) || ignoreAllyAbility); } @@ -1495,7 +1487,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const baseStats = this.calculateBaseStats(); // Using base stats, calculate and store stats one by one for (const s of PERMANENT_STATS) { - const statHolder = new Utils.NumberHolder( + const statHolder = new NumberHolder( Math.floor((2 * baseStats[s] + this.ivs[s]) * this.level * 0.01), ); if (s === Stat.HP) { @@ -1520,7 +1512,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } else { statHolder.value += 5; - const natureStatMultiplier = new Utils.NumberHolder( + const natureStatMultiplier = new NumberHolder( getNatureStatMultiplier(this.getNature(), s), ); globalScene.applyModifier( @@ -1622,9 +1614,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { generateNature(naturePool?: Nature[]): void { if (naturePool === undefined) { - naturePool = Utils.getEnumValues(Nature); + naturePool = getEnumValues(Nature); } - const nature = naturePool[Utils.randSeedInt(naturePool.length)]; + const nature = naturePool[randSeedInt(naturePool.length)]; this.setNature(nature); } @@ -1708,7 +1700,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns `true` if the pokemon is the species or is fused with it, `false` otherwise */ hasSpecies(species: Species, formKey?: string): boolean { - if (Utils.isNullOrUndefined(formKey)) { + if (isNullOrUndefined(formKey)) { return ( this.species.speciesId === species || this.fusionSpecies?.speciesId === species @@ -1870,7 +1862,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if ( secondType === PokemonType.UNKNOWN && - Utils.isNullOrUndefined(fusionType2) + isNullOrUndefined(fusionType2) ) { // If second pokemon was monotype and shared its primary type secondType = @@ -2240,11 +2232,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public getWeight(): number { const autotomizedTag = this.getTag(AutotomizedTag); let weightRemoved = 0; - if (!Utils.isNullOrUndefined(autotomizedTag)) { + if (!isNullOrUndefined(autotomizedTag)) { weightRemoved = 100 * autotomizedTag!.autotomizeCount; } const minWeight = 0.1; - const weight = new Utils.NumberHolder(this.species.weight - weightRemoved); + const weight = new NumberHolder(this.species.weight - weightRemoved); // This will trigger the ability overlay so only call this function when necessary applyAbAttrs(WeightMultiplierAbAttr, this, null, false, weight); @@ -2315,7 +2307,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } - const trappedByAbility = new Utils.BooleanHolder(false); + const trappedByAbility = new BooleanHolder(false); /** * Contains opposing Pokemon (Enemy/Player Pokemon) depending on perspective * Afterwards, it filters out Pokemon that have been switched out of the field so trapped abilities/moves do not trigger @@ -2354,7 +2346,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns The {@linkcode PokemonType} of the move after attributes are applied */ public getMoveType(move: Move, simulated = true): PokemonType { - const moveTypeHolder = new Utils.NumberHolder(move.type); + const moveTypeHolder = new NumberHolder(move.type); applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder); applyPreAttackAbAttrs( @@ -2385,7 +2377,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param move {@linkcode Move} The move being used by the attacking Pokémon. * @param ignoreAbility Whether to ignore abilities that might affect type effectiveness or immunity (defaults to `false`). * @param simulated Whether to apply abilities via simulated calls (defaults to `true`) - * @param cancelled {@linkcode Utils.BooleanHolder} Stores whether the move was cancelled by a non-type-based immunity. + * @param cancelled {@linkcode BooleanHolder} Stores whether the move was cancelled by a non-type-based immunity. * Currently only used by {@linkcode Pokemon.apply} to determine whether a "No effect" message should be shown. * @returns The type damage multiplier, indicating the effectiveness of the move */ @@ -2394,9 +2386,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { move: Move, ignoreAbility = false, simulated = true, - cancelled?: Utils.BooleanHolder, + cancelled?: BooleanHolder, ): TypeDamageMultiplier { - if (!Utils.isNullOrUndefined(this.turnData?.moveEffectiveness)) { + if (!isNullOrUndefined(this.turnData?.moveEffectiveness)) { return this.turnData?.moveEffectiveness; } @@ -2405,7 +2397,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const moveType = source.getMoveType(move); - const typeMultiplier = new Utils.NumberHolder( + const typeMultiplier = new NumberHolder( move.category !== MoveCategory.STATUS || move.hasAttr(RespectAttackTypeImmunityAttr) ? this.getAttackTypeEffectiveness( @@ -2435,7 +2427,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { typeMultiplier.value *= 2; } - const cancelledHolder = cancelled ?? new Utils.BooleanHolder(false); + const cancelledHolder = cancelled ?? new BooleanHolder(false); if (!ignoreAbility) { applyPreDefendAbAttrs( TypeImmunityAbAttr, @@ -2549,7 +2541,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let multiplier = types .map(defType => { - const multiplier = new Utils.NumberHolder( + const multiplier = new NumberHolder( getTypeDamageMultiplier(moveType, defType), ); applyChallenges( @@ -2567,7 +2559,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ); } if (source) { - const ignoreImmunity = new Utils.BooleanHolder(false); + const ignoreImmunity = new BooleanHolder(false); if ( source.isActive(true) && source.hasAbilityWithAttr(IgnoreTypeImmunityAbAttr) @@ -2600,7 +2592,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { }) .reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier; - const typeMultiplierAgainstFlying = new Utils.NumberHolder( + const typeMultiplierAgainstFlying = new NumberHolder( getTypeDamageMultiplier(moveType, PokemonType.FLYING), ); applyChallenges( @@ -2943,7 +2935,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const E = globalScene.gameData.trainerId ^ globalScene.gameData.secretId; const F = rand1 ^ rand2; - const shinyThreshold = new Utils.NumberHolder(BASE_SHINY_CHANCE); + const shinyThreshold = new NumberHolder(BASE_SHINY_CHANCE); if (thresholdOverride === undefined) { if (timedEventManager.isEventActive()) { const tchance = timedEventManager.getClassicTrainerShinyChance(); @@ -2986,7 +2978,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { thresholdOverride?: number, applyModifiersToOverride?: boolean, ): boolean { - const shinyThreshold = new Utils.NumberHolder(BASE_SHINY_CHANCE); + const shinyThreshold = new NumberHolder(BASE_SHINY_CHANCE); if (thresholdOverride === undefined || applyModifiersToOverride) { if (thresholdOverride !== undefined && applyModifiersToOverride) { shinyThreshold.value = thresholdOverride; @@ -3041,10 +3033,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ) { return 0; } - const rand = new Utils.NumberHolder(0); + const rand = new NumberHolder(0); globalScene.executeWithSeedOffset( () => { - rand.value = Utils.randSeedInt(10); + rand.value = randSeedInt(10); }, this.id, globalScene.waveSeed, @@ -3074,7 +3066,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!this.species.abilityHidden) { return false; } - const haThreshold = new Utils.NumberHolder(BASE_HIDDEN_ABILITY_CHANCE); + const haThreshold = new NumberHolder(BASE_HIDDEN_ABILITY_CHANCE); if (thresholdOverride === undefined || applyModifiersToOverride) { if (thresholdOverride !== undefined && applyModifiersToOverride) { haThreshold.value = thresholdOverride; @@ -3098,7 +3090,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } public generateFusionSpecies(forStarter?: boolean): void { - const hiddenAbilityChance = new Utils.NumberHolder( + const hiddenAbilityChance = new NumberHolder( BASE_HIDDEN_ABILITY_CHANCE, ); if (!this.hasTrainer()) { @@ -3109,8 +3101,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ); } - const hasHiddenAbility = !Utils.randSeedInt(hiddenAbilityChance.value); - const randAbilityIndex = Utils.randSeedInt(2); + const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value); + const randAbilityIndex = randSeedInt(2); const filter = !forStarter ? this.species.getCompatibleFusionSpeciesFilter() @@ -3427,7 +3419,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (stabMovePool.length) { const totalWeight = stabMovePool.reduce((v, m) => v + m[1], 0); - let rand = Utils.randSeedInt(totalWeight); + let rand = randSeedInt(totalWeight); let index = 0; while (rand > stabMovePool[index][1]) { rand -= stabMovePool[index++][1]; @@ -3441,7 +3433,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ); if (attackMovePool.length) { const totalWeight = attackMovePool.reduce((v, m) => v + m[1], 0); - let rand = Utils.randSeedInt(totalWeight); + let rand = randSeedInt(totalWeight); let index = 0; while (rand > attackMovePool[index][1]) { rand -= attackMovePool[index++][1]; @@ -3493,7 +3485,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo.moveId)); } const totalWeight = movePool.reduce((v, m) => v + m[1], 0); - let rand = Utils.randSeedInt(totalWeight); + let rand = randSeedInt(totalWeight); let index = 0; while (rand > movePool[index][1]) { rand -= movePool[index++][1]; @@ -3717,8 +3709,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { simulated = true, ignoreHeldItems = false, ): number { - const statStage = new Utils.NumberHolder(this.getStatStage(stat)); - const ignoreStatStage = new Utils.BooleanHolder(false); + const statStage = new NumberHolder(this.getStatStage(stat)); + const ignoreStatStage = new BooleanHolder(false); if (opponent) { if (isCritical) { @@ -3755,7 +3747,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (!ignoreStatStage.value) { - const statStageMultiplier = new Utils.NumberHolder( + const statStageMultiplier = new NumberHolder( Math.max(2, 2 + statStage.value) / Math.max(2, 2 - statStage.value), ); if (!ignoreHeldItems) { @@ -3787,13 +3779,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return 1; } - const userAccStage = new Utils.NumberHolder(this.getStatStage(Stat.ACC)); - const targetEvaStage = new Utils.NumberHolder( + const userAccStage = new NumberHolder(this.getStatStage(Stat.ACC)); + const targetEvaStage = new NumberHolder( target.getStatStage(Stat.EVA), ); - const ignoreAccStatStage = new Utils.BooleanHolder(false); - const ignoreEvaStatStage = new Utils.BooleanHolder(false); + const ignoreAccStatStage = new BooleanHolder(false); + const ignoreEvaStatStage = new BooleanHolder(false); applyAbAttrs( IgnoreOpponentStatStagesAbAttr, @@ -3835,7 +3827,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { targetEvaStage.value = Math.min(0, targetEvaStage.value); } - const accuracyMultiplier = new Utils.NumberHolder(1); + const accuracyMultiplier = new NumberHolder(1); if (userAccStage.value !== targetEvaStage.value) { accuracyMultiplier.value = userAccStage.value > targetEvaStage.value @@ -3852,7 +3844,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { sourceMove, ); - const evasionMultiplier = new Utils.NumberHolder(1); + const evasionMultiplier = new NumberHolder(1); applyStatMultiplierAbAttrs( StatMultiplierAbAttr, target, @@ -3907,7 +3899,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * The attacker's offensive stat for the given move's category. * Critical hits cause negative stat stages to be ignored. */ - const sourceAtk = new Utils.NumberHolder( + const sourceAtk = new NumberHolder( source.getEffectiveStat( isPhysical ? Stat.ATK : Stat.SPATK, this, @@ -3925,7 +3917,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * This Pokemon's defensive stat for the given move's category. * Critical hits cause positive stat stages to be ignored. */ - const targetDef = new Utils.NumberHolder( + const targetDef = new NumberHolder( this.getEffectiveStat( isPhysical ? Stat.DEF : Stat.SPDEF, source, @@ -3986,12 +3978,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { isCritical = false, simulated = true, ): DamageCalculationResult { - const damage = new Utils.NumberHolder(0); + const damage = new NumberHolder(0); const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; - const variableCategory = new Utils.NumberHolder(move.category); + const variableCategory = new NumberHolder(move.category); applyMoveAttrs( VariableMoveCategoryAttr, source, @@ -4005,7 +3997,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const moveType = source.getMoveType(move); /** If `value` is `true`, cancels the move and suppresses "No Effect" messages */ - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); /** * The effectiveness of the move being used. Along with type matchups, this @@ -4025,7 +4017,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const isPhysical = moveCategory === MoveCategory.PHYSICAL; /** Combined damage multiplier from field effects such as weather, terrain, etc. */ - const arenaAttackTypeMultiplier = new Utils.NumberHolder( + const arenaAttackTypeMultiplier = new NumberHolder( globalScene.arena.getAttackTypeMultiplier(moveType, source.isGrounded()), ); applyMoveAttrs( @@ -4048,10 +4040,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } // If the attack deals fixed damage, return a result with that much damage - const fixedDamage = new Utils.NumberHolder(0); + const fixedDamage = new NumberHolder(0); applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage); if (fixedDamage.value) { - const multiLensMultiplier = new Utils.NumberHolder(1); + const multiLensMultiplier = new NumberHolder(1); globalScene.applyModifiers( PokemonMultiHitModifier, source.isPlayer(), @@ -4060,7 +4052,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { null, multiLensMultiplier, ); - fixedDamage.value = Utils.toDmgValue( + fixedDamage.value = toDmgValue( fixedDamage.value * multiLensMultiplier.value, ); @@ -4072,7 +4064,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } // If the attack is a one-hit KO move, return a result with damage equal to this Pokemon's HP - const isOneHitKo = new Utils.BooleanHolder(false); + const isOneHitKo = new BooleanHolder(false); applyMoveAttrs(OneHitKOAttr, source, this, move, isOneHitKo); if (isOneHitKo.value) { return { @@ -4104,7 +4096,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const targetMultiplier = numTargets > 1 ? 0.75 : 1; /** Multiplier for moves enhanced by Multi-Lens and/or Parental Bond */ - const multiStrikeEnhancementMultiplier = new Utils.NumberHolder(1); + const multiStrikeEnhancementMultiplier = new NumberHolder(1); globalScene.applyModifiers( PokemonMultiHitModifier, source.isPlayer(), @@ -4126,13 +4118,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** Doubles damage if this Pokemon's last move was Glaive Rush */ - const glaiveRushMultiplier = new Utils.NumberHolder(1); + const glaiveRushMultiplier = new NumberHolder(1); if (this.getTag(BattlerTagType.RECEIVE_DOUBLE_DAMAGE)) { glaiveRushMultiplier.value = 2; } /** The damage multiplier when the given move critically hits */ - const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1); + const criticalMultiplier = new NumberHolder(isCritical ? 1.5 : 1); applyAbAttrs(MultCritAbAttr, source, null, simulated, criticalMultiplier); /** @@ -4147,7 +4139,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const sourceTeraType = source.getTeraType(); const matchesSourceType = sourceTypes.includes(moveType); /** A damage multiplier for when the attack is of the attacker's type and/or Tera type. */ - const stabMultiplier = new Utils.NumberHolder(1); + const stabMultiplier = new NumberHolder(1); if (matchesSourceType && moveType !== PokemonType.STELLAR) { stabMultiplier.value += 0.5; } @@ -4188,14 +4180,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { stabMultiplier.value = Math.min(stabMultiplier.value, 2.25); /** Halves damage if the attacker is using a physical attack while burned */ - const burnMultiplier = new Utils.NumberHolder(1); + const burnMultiplier = new NumberHolder(1); if ( isPhysical && source.status && source.status.effect === StatusEffect.BURN ) { if (!move.hasAttr(BypassBurnDamageReductionAttr)) { - const burnDamageReductionCancelled = new Utils.BooleanHolder(false); + const burnDamageReductionCancelled = new BooleanHolder(false); if (!ignoreSourceAbility) { applyAbAttrs( BypassBurnDamageReductionAbAttr, @@ -4211,7 +4203,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** Reduces damage if this Pokemon has a relevant screen (e.g. Light Screen for special attacks) */ - const screenMultiplier = new Utils.NumberHolder(1); + const screenMultiplier = new NumberHolder(1); // Critical hits should bypass screens if (!isCritical) { @@ -4231,7 +4223,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * AND * The move doubles damage when used against that tag */ - const hitsTagMultiplier = new Utils.NumberHolder(1); + const hitsTagMultiplier = new NumberHolder(1); move .getAttrs(HitsTagAttr) .filter(hta => hta.doubleDamage) @@ -4249,7 +4241,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ? 0.5 : 1; - damage.value = Utils.toDmgValue( + damage.value = toDmgValue( baseDamage * targetMultiplier * multiStrikeEnhancementMultiplier.value * @@ -4358,10 +4350,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; - const moveCategory = new Utils.NumberHolder(move.category); + const moveCategory = new NumberHolder(move.category); applyMoveAttrs(VariableMoveCategoryAttr, source, this, move, moveCategory); if (moveCategory.value === MoveCategory.STATUS) { - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); const typeMultiplier = this.getMoveEffectiveness( source, move, @@ -4381,7 +4373,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** Determines whether the attack critically hits */ let isCritical: boolean; - const critOnly = new Utils.BooleanHolder(false); + const critOnly = new BooleanHolder(false); const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT); applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly); applyAbAttrs( @@ -4404,7 +4396,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const noCritTag = globalScene.arena.getTagOnSide(NoCritTag, defendingSide); - const blockCrit = new Utils.BooleanHolder(false); + const blockCrit = new BooleanHolder(false); applyAbAttrs(BlockCritAbAttr, this, null, false, blockCrit); if (noCritTag || blockCrit.value || Overrides.NEVER_CRIT_OVERRIDE) { isCritical = false; @@ -4486,7 +4478,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (damage > 0) { if (source.isPlayer()) { - globalScene.validateAchvs(DamageAchv, new Utils.NumberHolder(damage)); + globalScene.validateAchvs(DamageAchv, new NumberHolder(damage)); if (damage > globalScene.gameData.gameStats.highestDamage) { globalScene.gameData.gameStats.highestDamage = damage; } @@ -4510,7 +4502,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { DamageMoneyRewardModifier, true, source, - new Utils.NumberHolder(damage), + new NumberHolder(damage), ); } } @@ -4575,7 +4567,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.isFainted()) { return 0; } - const surviveDamage = new Utils.BooleanHolder(false); + const surviveDamage = new BooleanHolder(false); if (!preventEndure && this.hp - damage <= 0) { if (this.hp >= 1 && this.getTag(BattlerTagType.ENDURING)) { @@ -4710,7 +4702,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const stubTag = new BattlerTag(tagType, 0, 0); - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); applyPreApplyBattlerTagAbAttrs( BattlerTagImmunityAbAttr, this, @@ -4748,7 +4740,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const newTag = getBattlerTag(tagType, turnCount, sourceMove!, sourceId!); // TODO: are the bangs correct? - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); applyPreApplyBattlerTagAbAttrs( BattlerTagImmunityAbAttr, this, @@ -5081,12 +5073,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let fusionCry = this.getFusionSpeciesForm().cry(soundConfig, true); duration = Math.min(duration, fusionCry.totalDuration * 1000); fusionCry.destroy(); - scene.time.delayedCall(Utils.fixedInt(Math.ceil(duration * 0.4)), () => { + scene.time.delayedCall(fixedInt(Math.ceil(duration * 0.4)), () => { try { SoundFade.fadeOut( scene, cry, - Utils.fixedInt(Math.ceil(duration * 0.2)), + fixedInt(Math.ceil(duration * 0.2)), ); fusionCry = this.getFusionSpeciesForm().cry( Object.assign( @@ -5097,7 +5089,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { SoundFade.fadeIn( scene, fusionCry, - Utils.fixedInt(Math.ceil(duration * 0.2)), + fixedInt(Math.ceil(duration * 0.2)), scene.masterVolume * scene.fieldVolume, 0, ); @@ -5137,7 +5129,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let faintCryTimer: Phaser.Time.TimerEvent | null = globalScene.time.addEvent({ - delay: Utils.fixedInt(delay), + delay: fixedInt(delay), repeat: -1, callback: () => { frameThreshold = sprite.anims.msPerFrame / rate; @@ -5163,7 +5155,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { }); // Failsafe - globalScene.time.delayedCall(Utils.fixedInt(3000), () => { + globalScene.time.delayedCall(fixedInt(3000), () => { if (!faintCryTimer || !globalScene) { return; } @@ -5222,7 +5214,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let faintCryTimer: Phaser.Time.TimerEvent | null = globalScene.time.addEvent({ - delay: Utils.fixedInt(delay), + delay: fixedInt(delay), repeat: -1, callback: () => { ++i; @@ -5239,7 +5231,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { SoundFade.fadeOut( globalScene, cry, - Utils.fixedInt(Math.ceil((duration / rate) * 0.2)), + fixedInt(Math.ceil((duration / rate) * 0.2)), ); fusionCry = globalScene.playSound( fusionCryKey, @@ -5251,7 +5243,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { SoundFade.fadeIn( globalScene, fusionCry, - Utils.fixedInt(Math.ceil((duration / rate) * 0.2)), + fixedInt(Math.ceil((duration / rate) * 0.2)), globalScene.masterVolume * globalScene.fieldVolume, 0, ); @@ -5277,7 +5269,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { }); // Failsafe - globalScene.time.delayedCall(Utils.fixedInt(3000), () => { + globalScene.time.delayedCall(fixedInt(3000), () => { if (!faintCryTimer || !globalScene) { return; } @@ -5352,7 +5344,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } // Check if the source Pokemon has an ability that cancels the Poison/Toxic immunity - const cancelImmunity = new Utils.BooleanHolder(false); + const cancelImmunity = new BooleanHolder(false); if (sourcePokemon) { applyAbAttrs( IgnoreTypeStatusEffectImmunityAbAttr, @@ -5408,7 +5400,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { break; } - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); applyPreSetStatusAbAttrs( StatusEffectImmunityAbAttr, this, @@ -5479,10 +5471,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return true; } - let sleepTurnsRemaining: Utils.NumberHolder; + let sleepTurnsRemaining: NumberHolder; if (effect === StatusEffect.SLEEP) { - sleepTurnsRemaining = new Utils.NumberHolder(this.randSeedIntRange(2, 4)); + sleepTurnsRemaining = new NumberHolder(this.randSeedIntRange(2, 4)); this.setFrameRate(4); @@ -5533,7 +5525,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; if (globalScene.arena.getTagOnSide(ArenaTagType.SAFEGUARD, defendingSide)) { - const bypassed = new Utils.BooleanHolder(false); + const bypassed = new BooleanHolder(false); if (attacker) { applyAbAttrs(InfiltratorAbAttr, attacker, null, false, bypassed); } @@ -5829,9 +5821,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.shiny && variantColors && variantColors[this.variant]) { Object.keys(variantColors[this.variant]).forEach(k => { variantColorSet.set( - Utils.rgbaToInt(Array.from(Object.values(Utils.rgbHexToRgba(k)))), + rgbaToInt(Array.from(Object.values(rgbHexToRgba(k)))), Array.from( - Object.values(Utils.rgbHexToRgba(variantColors[this.variant][k])), + Object.values(rgbHexToRgba(variantColors[this.variant][k])), ), ); }); @@ -5842,7 +5834,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const pixel = pixelData[f].slice(i, i + 4); let [r, g, b, a] = pixel; if (variantColors) { - const color = Utils.rgbaToInt([r, g, b, a]); + const color = rgbaToInt([r, g, b, a]); if (variantColorSet.has(color)) { const mappedPixel = variantColorSet.get(color); if (mappedPixel) { @@ -5891,10 +5883,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ) { for (const k of Object.keys(variantColors[this.fusionVariant])) { variantColorSet.set( - Utils.rgbaToInt(Array.from(Object.values(Utils.rgbHexToRgba(k)))), + rgbaToInt(Array.from(Object.values(rgbHexToRgba(k)))), Array.from( Object.values( - Utils.rgbHexToRgba(variantColors[this.fusionVariant][k]), + rgbHexToRgba(variantColors[this.fusionVariant][k]), ), ), ); @@ -5914,7 +5906,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { pixelData[2 + f][i + 3], ]; if (variantColors) { - const color = Utils.rgbaToInt([r, g, b, a]); + const color = rgbaToInt([r, g, b, a]); if (variantColorSet.has(color)) { const mappedPixel = variantColorSet.get(color); if (mappedPixel) { @@ -5972,7 +5964,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { hsvColors = Array.from(rgbaColors.keys()).reduce( (map: Map, k: number) => { const rgb = rgbaColors.get(k)!.slice(0, 3); - map.set(k, Utils.rgbToHsv(rgb[0], rgb[1], rgb[2])); + map.set(k, rgbToHsv(rgb[0], rgb[1], rgb[2])); return map; }, new Map(), @@ -6052,7 +6044,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { spriteColors.forEach((sc: number[], i: number) => { paletteDeltas.push([]); for (let p = 0; p < palette.length; p++) { - paletteDeltas[i].push(Utils.deltaRgb(sc, palette[p])); + paletteDeltas[i].push(deltaRgb(sc, palette[p])); } }); @@ -6097,8 +6089,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * * This calls either {@linkcode BattleScene.randBattleSeedInt}({@linkcode range}, {@linkcode min}) in `src/battle-scene.ts` * which calls {@linkcode Battle.randSeedInt}({@linkcode range}, {@linkcode min}) in `src/battle.ts` - * which calls {@linkcode Utils.randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts`, - * or it directly calls {@linkcode Utils.randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts` if there is no current battle + * which calls {@linkcode randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts`, + * or it directly calls {@linkcode randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts` if there is no current battle * * @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` @@ -6107,7 +6099,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { randSeedInt(range: number, min = 0): number { return globalScene.currentBattle ? globalScene.randBattleSeedInt(range, min) - : Utils.randSeedInt(range, min); + : randSeedInt(range, min); } /** @@ -6403,7 +6395,7 @@ export class PlayerPokemon extends Pokemon { ? globalScene.gameData.starterData[fusionStarterSpeciesId] : null, ].filter(d => !!d); - const amount = new Utils.NumberHolder(friendship); + const amount = new NumberHolder(friendship); globalScene.applyModifier( PokemonFriendshipBoosterModifier, true, @@ -6418,7 +6410,7 @@ export class PlayerPokemon extends Pokemon { ? 1.5 // Divide candy gain for fusions by 1.5 during events : 2 // 2 for fusions outside events : 1; // 1 for non-fused mons - const starterAmount = new Utils.NumberHolder( + const starterAmount = new NumberHolder( Math.floor( (amount.value * candyFriendshipMultiplier) / fusionReduction, ), @@ -7381,7 +7373,7 @@ export class EnemyPokemon extends Pokemon { //console.log('damage', damage, 'segment', segmentsBypassed + 1, 'segment size', segmentSize, 'damage needed', Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1))); } - damage = Utils.toDmgValue( + damage = toDmgValue( this.hp - hpThreshold + segmentSize * segmentsBypassed, ); clearedBossSegmentIndex = s - segmentsBypassed; @@ -7459,7 +7451,7 @@ export class EnemyPokemon extends Pokemon { } // Pick a random stat from the leftover stats to increase its stages - const randInt = Utils.randSeedInt(totalWeight); + const randInt = randSeedInt(totalWeight); for (const i in statThresholds) { if (randInt < statThresholds[i]) { boostedStat = leftoverStats[i]; @@ -7530,7 +7522,7 @@ export class EnemyPokemon extends Pokemon { this, ); - if (Utils.isBetween(slotIndex, 0, PLAYER_PARTY_MAX_SIZE - 1)) { + if (isBetween(slotIndex, 0, PLAYER_PARTY_MAX_SIZE - 1)) { party.splice(slotIndex, 0, newPokemon); } else { party.push(newPokemon); @@ -7772,7 +7764,7 @@ export class PokemonMove { getMovePp(): number { return ( this.maxPpOverride || - this.getMove().pp + this.ppUp * Utils.toDmgValue(this.getMove().pp / 5) + this.getMove().pp + this.ppUp * toDmgValue(this.getMove().pp / 5) ); } diff --git a/src/field/trainer.ts b/src/field/trainer.ts index ccd8c83e684..30cf43b54a1 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -11,7 +11,7 @@ import { TrainerSlot } from "#enums/trainer-slot"; import { TrainerPoolTier } from "#enums/trainer-pool-tier"; import { TeraAIMode } from "#enums/tera-ai-mode"; import type { EnemyPokemon } from "#app/field/pokemon"; -import * as Utils from "#app/utils"; +import { randSeedWeightedItem, randSeedItem, randSeedInt } from "#app/utils"; import type { PersistentModifier } from "#app/modifier/modifier"; import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; import { getIsInitialized, initI18n } from "#app/plugins/i18n"; @@ -58,7 +58,7 @@ export default class Trainer extends Phaser.GameObjects.Container { this.partyTemplateIndex = Math.min( partyTemplateIndex !== undefined ? partyTemplateIndex - : Utils.randSeedWeightedItem(this.config.partyTemplates.map((_, i) => i)), + : randSeedWeightedItem(this.config.partyTemplates.map((_, i) => i)), this.config.partyTemplates.length - 1, ); const classKey = `trainersCommon:${TrainerType[trainerType]}`; @@ -71,9 +71,7 @@ export default class Trainer extends Phaser.GameObjects.Container { ? ".FEMALE" : ".MALE" : ""; - const trainerKey = Utils.randSeedItem( - Object.keys(i18next.t(`${classKey}${genderKey}`, { returnObjects: true })), - ); + const trainerKey = randSeedItem(Object.keys(i18next.t(`${classKey}${genderKey}`, { returnObjects: true }))); this.nameKey = `${classKey}${genderKey}.${trainerKey}`; } this.name = i18next.t(this.nameKey); @@ -87,7 +85,7 @@ export default class Trainer extends Phaser.GameObjects.Container { } } else { const partnerGenderKey = i18next.exists(`${classKey}.FEMALE`) ? ".FEMALE" : ""; - const partnerTrainerKey = Utils.randSeedItem( + const partnerTrainerKey = randSeedItem( Object.keys( i18next.t(`${classKey}${partnerGenderKey}`, { returnObjects: true, @@ -420,7 +418,7 @@ export default class Trainer extends Phaser.GameObjects.Container { // If useNewSpeciesPool is true, we need to generate a new species from the new species pool, otherwise we generate a random species let species = useNewSpeciesPool - ? getPokemonSpecies(newSpeciesPool[Math.floor(Utils.randSeedInt(newSpeciesPool.length))]) + ? getPokemonSpecies(newSpeciesPool[Math.floor(randSeedInt(newSpeciesPool.length))]) : template.isSameSpecies(index) && index > offset ? getPokemonSpecies( battle.enemyParty[offset].species.getTrainerSpeciesForLevel( @@ -461,7 +459,7 @@ export default class Trainer extends Phaser.GameObjects.Container { let baseSpecies: PokemonSpecies; if (this.config.speciesPools) { - const tierValue = Utils.randSeedInt(512); + const tierValue = randSeedInt(512); let tier = tierValue >= 156 ? TrainerPoolTier.COMMON @@ -480,7 +478,7 @@ export default class Trainer extends Phaser.GameObjects.Container { tier--; } const tierPool = this.config.speciesPools[tier]; - baseSpecies = getPokemonSpecies(Utils.randSeedItem(tierPool)); + baseSpecies = getPokemonSpecies(randSeedItem(tierPool)); } else { baseSpecies = globalScene.randomSpecies(battle.waveIndex, level, false, this.config.speciesFilter); } @@ -619,7 +617,7 @@ export default class Trainer extends Phaser.GameObjects.Container { if (maxScorePartyMemberIndexes.length > 1) { let rand: number; globalScene.executeWithSeedOffset( - () => (rand = Utils.randSeedInt(maxScorePartyMemberIndexes.length)), + () => (rand = randSeedInt(maxScorePartyMemberIndexes.length)), globalScene.currentBattle.turn << 2, ); return maxScorePartyMemberIndexes[rand!]; diff --git a/src/game-mode.ts b/src/game-mode.ts index c340768ef77..4779fda50e8 100644 --- a/src/game-mode.ts +++ b/src/game-mode.ts @@ -7,7 +7,7 @@ import type PokemonSpecies from "./data/pokemon-species"; import { allSpecies } from "./data/pokemon-species"; import type { Arena } from "./field/arena"; import Overrides from "#app/overrides"; -import * as Utils from "./utils"; +import { randSeedInt, randSeedItem } from "#app/utils"; import { Biome } from "#enums/biome"; import { Species } from "#enums/species"; import { Challenges } from "./enums/challenges"; @@ -186,7 +186,7 @@ export class GameMode implements GameModeConfig { if (w < waveIndex) { globalScene.executeWithSeedOffset(() => { const waveTrainerChance = arena.getTrainerChance(); - if (!Utils.randSeedInt(waveTrainerChance)) { + if (!randSeedInt(waveTrainerChance)) { allowTrainerBattle = false; } }, w); @@ -196,7 +196,7 @@ export class GameMode implements GameModeConfig { } } } - return Boolean(allowTrainerBattle && trainerChance && !Utils.randSeedInt(trainerChance)); + return Boolean(allowTrainerBattle && trainerChance && !randSeedInt(trainerChance)); } return false; } @@ -222,7 +222,7 @@ export class GameMode implements GameModeConfig { s.speciesId !== Species.ETERNATUS && s.speciesId !== Species.ARCEUS, ); - return Utils.randSeedItem(allFinalBossSpecies); + return randSeedItem(allFinalBossSpecies); } return null; diff --git a/src/inputs-controller.ts b/src/inputs-controller.ts index fb4555084ee..f92ce3957ab 100644 --- a/src/inputs-controller.ts +++ b/src/inputs-controller.ts @@ -1,6 +1,5 @@ import Phaser from "phaser"; -import * as Utils from "./utils"; -import { deepCopy } from "./utils"; +import { deepCopy, getEnumValues } from "#app/utils"; import pad_generic from "./configs/inputs/pad_generic"; import pad_unlicensedSNES from "./configs/inputs/pad_unlicensedSNES"; import pad_xbox360 from "./configs/inputs/pad_xbox360"; @@ -102,7 +101,7 @@ export class InputsController { [Device.KEYBOARD]: "default", }; - for (const b of Utils.getEnumValues(Button)) { + for (const b of getEnumValues(Button)) { this.interactions[b] = { pressTime: false, isPressed: false, diff --git a/src/loading-scene.ts b/src/loading-scene.ts index f99831c53bc..b45cf64ff56 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -4,7 +4,7 @@ import CacheBustedLoaderPlugin from "#app/plugins/cache-busted-loader-plugin"; import { SceneBase } from "#app/scene-base"; import { WindowVariant, getWindowVariantSuffix } from "#app/ui/ui-theme"; import { isMobile } from "#app/touch-controls"; -import * as Utils from "#app/utils"; +import { localPing, getEnumValues, hasAllLocalizedSprites, getEnumKeys } from "#app/utils"; import { initPokemonPrevolutions, initPokemonStarters } from "#app/data/balance/pokemon-evolutions"; import { initBiomes } from "#app/data/balance/biomes"; import { initEggMoves } from "#app/data/balance/egg-moves"; @@ -34,7 +34,7 @@ export class LoadingScene extends SceneBase { } preload() { - Utils.localPing(); + localPing(); this.load["manifest"] = this.game["manifest"]; this.loadImage("loading_bg", "arenas"); @@ -49,7 +49,7 @@ export class LoadingScene extends SceneBase { this.loadImage("friendship_overlay", "ui"); this.loadImage("cursor", "ui"); this.loadImage("cursor_reverse", "ui"); - for (const wv of Utils.getEnumValues(WindowVariant)) { + for (const wv of getEnumValues(WindowVariant)) { for (let w = 1; w <= 5; w++) { this.loadImage(`window_${w}${getWindowVariantSuffix(wv)}`, "ui/windows"); } @@ -177,7 +177,7 @@ export class LoadingScene extends SceneBase { this.loadImage("default_bg", "arenas"); // Load arena images - Utils.getEnumValues(Biome).map(bt => { + getEnumValues(Biome).map(bt => { const btKey = Biome[bt].toLowerCase(); const isBaseAnimated = btKey === "end"; const baseAKey = `${btKey}_a`; @@ -239,7 +239,7 @@ export class LoadingScene extends SceneBase { // Get current lang and load the types atlas for it. English will only load types while all other languages will load types and types_ const lang = i18next.resolvedLanguage; if (lang !== "en") { - if (Utils.hasAllLocalizedSprites(lang)) { + if (hasAllLocalizedSprites(lang)) { this.loadAtlas(`statuses_${lang}`, ""); this.loadAtlas(`types_${lang}`, ""); } else { @@ -268,7 +268,7 @@ export class LoadingScene extends SceneBase { this.loadAtlas("egg_icons", "egg"); this.loadAtlas("egg_shard", "egg"); this.loadAtlas("egg_lightrays", "egg"); - for (const gt of Utils.getEnumKeys(GachaType)) { + for (const gt of getEnumKeys(GachaType)) { const key = gt.toLowerCase(); this.loadImage(`gacha_${key}`, "egg"); this.loadAtlas(`gacha_underlay_${key}`, "egg"); diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index 6c46f7ff8c0..acc7ac0f63a 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -297,16 +297,16 @@ export class MoveEffectPhase extends PokemonPhase { ); } - /** Is the target protected by Protect, etc. or a relevant conditional protection effect? */ - const isProtected = - ![MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES].includes(this.move.getMove().moveTarget) && - (bypassIgnoreProtect.value || - !this.move.getMove().doesFlagEffectApply({ flag: MoveFlags.IGNORE_PROTECT, user, target })) && - (hasConditionalProtectApplied.value || - (!target.findTags(t => t instanceof DamageProtectedTag).length && - target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))) || - (this.move.getMove().category !== MoveCategory.STATUS && - target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType)))); + /** Is the target protected by Protect, etc. or a relevant conditional protection effect? */ + const isProtected = + ![MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES].includes(this.move.getMove().moveTarget) && + (bypassIgnoreProtect.value || + !this.move.getMove().doesFlagEffectApply({ flag: MoveFlags.IGNORE_PROTECT, user, target })) && + (hasConditionalProtectApplied.value || + (!target.findTags(t => t instanceof DamageProtectedTag).length && + target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))) || + (this.move.getMove().category !== MoveCategory.STATUS && + target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType)))); /** Is the target hidden by the effects of its Commander ability? */ const isCommanding = @@ -316,11 +316,11 @@ export class MoveEffectPhase extends PokemonPhase { /** Is the target reflecting status moves from the magic coat move? */ const isReflecting = !!target.getTag(BattlerTagType.MAGIC_COAT); - /** Is the target's magic bounce ability not ignored and able to reflect this move? */ - const canMagicBounce = - !isReflecting && - !move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user, target }) && - target.hasAbilityWithAttr(ReflectStatusMoveAbAttr); + /** Is the target's magic bounce ability not ignored and able to reflect this move? */ + const canMagicBounce = + !isReflecting && + !move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user, target }) && + target.hasAbilityWithAttr(ReflectStatusMoveAbAttr); const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); @@ -333,21 +333,19 @@ export class MoveEffectPhase extends PokemonPhase { (isReflecting || canMagicBounce) && !semiInvulnerableTag; - // If the move will bounce, then queue the bounce and move on to the next target - if (!target.switchOutStatus && willBounce) { - const newTargets = move.isMultiTarget() - ? getMoveTargets(target, move.id).targets - : [user.getBattlerIndex()]; - if (!isReflecting) { - // TODO: Ability displays should be handled by the ability - queuedPhases.push( - new ShowAbilityPhase( - target.getBattlerIndex(), - target.getPassiveAbility().hasAttr(ReflectStatusMoveAbAttr), - ), - ); - queuedPhases.push(new HideAbilityPhase()); - } + // If the move will bounce, then queue the bounce and move on to the next target + if (!target.switchOutStatus && willBounce) { + const newTargets = move.isMultiTarget() ? getMoveTargets(target, move.id).targets : [user.getBattlerIndex()]; + if (!isReflecting) { + // TODO: Ability displays should be handled by the ability + queuedPhases.push( + new ShowAbilityPhase( + target.getBattlerIndex(), + target.getPassiveAbility().hasAttr(ReflectStatusMoveAbAttr), + ), + ); + queuedPhases.push(new HideAbilityPhase()); + } queuedPhases.push(new MovePhase(target, newTargets, new PokemonMove(move.id, 0, 0, true), true, true, true)); continue; diff --git a/src/phases/revival-blessing-phase.ts b/src/phases/revival-blessing-phase.ts index e650d714abc..f6fe4d9a3ee 100644 --- a/src/phases/revival-blessing-phase.ts +++ b/src/phases/revival-blessing-phase.ts @@ -4,7 +4,7 @@ import type { PartyOption } from "#app/ui/party-ui-handler"; import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler"; import { Mode } from "#app/ui/ui"; import i18next from "i18next"; -import * as Utils from "#app/utils"; +import { toDmgValue, isNullOrUndefined } from "#app/utils"; import { BattlePhase } from "#app/phases/battle-phase"; import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; @@ -33,7 +33,7 @@ export class RevivalBlessingPhase extends BattlePhase { pokemon.resetTurnData(); pokemon.resetStatus(); - pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); + pokemon.heal(Math.min(toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); globalScene.queueMessage( i18next.t("moveTriggers:revivalBlessing", { pokemonName: pokemon.name, @@ -46,7 +46,7 @@ export class RevivalBlessingPhase extends BattlePhase { if ( globalScene.currentBattle.double && globalScene.getPlayerParty().length > 1 && - !Utils.isNullOrUndefined(allyPokemon) + !isNullOrUndefined(allyPokemon) ) { if (slotIndex <= 1) { // Revived ally pokemon diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts index c6ded6be7af..35511531609 100644 --- a/src/phases/select-starter-phase.ts +++ b/src/phases/select-starter-phase.ts @@ -12,7 +12,7 @@ import type { Starter } from "#app/ui/starter-select-ui-handler"; import { Mode } from "#app/ui/ui"; import type { Species } from "#enums/species"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import * as Utils from "../utils"; +import { isNullOrUndefined } from "#app/utils"; export class SelectStarterPhase extends Phase { start() { @@ -49,7 +49,7 @@ export class SelectStarterPhase extends Phase { let starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0)); if ( starter.species.speciesId in Overrides.STARTER_FORM_OVERRIDES && - !Utils.isNullOrUndefined(Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]) && + !isNullOrUndefined(Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]) && starter.species.forms[Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!] ) { starterFormIndex = Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!; @@ -87,7 +87,7 @@ export class SelectStarterPhase extends Phase { starterPokemon.nickname = starter.nickname; } - if (!Utils.isNullOrUndefined(starter.teraType)) { + if (!isNullOrUndefined(starter.teraType)) { starterPokemon.teraType = starter.teraType; } else { starterPokemon.teraType = starterPokemon.species.type1; diff --git a/src/pipelines/field-sprite.ts b/src/pipelines/field-sprite.ts index 612c9fae052..a55b6a9adb6 100644 --- a/src/pipelines/field-sprite.ts +++ b/src/pipelines/field-sprite.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { TerrainType, getTerrainColor } from "../data/terrain"; -import * as Utils from "../utils"; +import { getCurrentTime } from "#app/utils"; import fieldSpriteFragShader from "./glsl/fieldSpriteFragShader.frag?raw"; import spriteVertShader from "./glsl/spriteShader.vert?raw"; @@ -34,7 +34,7 @@ export default class FieldSpritePipeline extends Phaser.Renderer.WebGL.Pipelines const time = globalScene.currentBattle?.waveIndex ? ((globalScene.currentBattle.waveIndex + globalScene.waveCycleOffset) % 40) / 40 // ((new Date().getSeconds() * 1000 + new Date().getMilliseconds()) % 10000) / 10000 - : Utils.getCurrentTime(); + : getCurrentTime(); this.set1f("time", time); this.set1i("ignoreTimeTint", ignoreTimeTint ? 1 : 0); this.set1i("isOutside", globalScene.arena.isOutside() ? 1 : 0); diff --git a/src/pipelines/sprite.ts b/src/pipelines/sprite.ts index acbaac50476..d97cae1662b 100644 --- a/src/pipelines/sprite.ts +++ b/src/pipelines/sprite.ts @@ -3,7 +3,7 @@ import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro"; import Pokemon from "#app/field/pokemon"; import Trainer from "#app/field/trainer"; import { globalScene } from "#app/global-scene"; -import * as Utils from "#app/utils"; +import { rgbHexToRgba } from "#app/utils"; import FieldSpritePipeline from "./field-sprite"; import spriteFragShader from "./glsl/spriteFragShader.frag?raw"; import spriteVertShader from "./glsl/spriteShader.vert?raw"; @@ -144,8 +144,8 @@ export default class SpritePipeline extends FieldSpritePipeline { const baseColors = Object.keys(variantColors[variant]); for (let c = 0; c < 32; c++) { if (c < baseColors.length) { - const baseColor = Array.from(Object.values(Utils.rgbHexToRgba(baseColors[c]))); - const variantColor = Array.from(Object.values(Utils.rgbHexToRgba(variantColors[variant][baseColors[c]]))); + const baseColor = Array.from(Object.values(rgbHexToRgba(baseColors[c]))); + const variantColor = Array.from(Object.values(rgbHexToRgba(variantColors[variant][baseColors[c]]))); flatBaseColors.splice(flatBaseColors.length, 0, ...baseColor); flatVariantColors.splice(flatVariantColors.length, 0, ...variantColor.map(c => c / 255.0)); } else { diff --git a/src/system/achv.ts b/src/system/achv.ts index bd8595b2f94..62e69e6fbfe 100644 --- a/src/system/achv.ts +++ b/src/system/achv.ts @@ -2,7 +2,7 @@ import type { Modifier } from "typescript"; import { TurnHeldItemTransferModifier } from "../modifier/modifier"; import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import i18next from "i18next"; -import * as Utils from "../utils"; +import { NumberHolder } from "#app/utils"; import { PlayerGender } from "#enums/player-gender"; import type { Challenge } from "#app/data/challenge"; import { @@ -138,7 +138,7 @@ export class DamageAchv extends Achv { "", iconImage, score, - (args: any[]) => (args[0] instanceof Utils.NumberHolder ? args[0].value : args[0]) >= this.damageAmount, + (args: any[]) => (args[0] instanceof NumberHolder ? args[0].value : args[0]) >= this.damageAmount, ); this.damageAmount = damageAmount; } @@ -154,7 +154,7 @@ export class HealAchv extends Achv { "", iconImage, score, - (args: any[]) => (args[0] instanceof Utils.NumberHolder ? args[0].value : args[0]) >= this.healAmount, + (args: any[]) => (args[0] instanceof NumberHolder ? args[0].value : args[0]) >= this.healAmount, ); this.healAmount = healAmount; } @@ -170,7 +170,7 @@ export class LevelAchv extends Achv { "", iconImage, score, - (args: any[]) => (args[0] instanceof Utils.NumberHolder ? args[0].value : args[0]) >= this.level, + (args: any[]) => (args[0] instanceof NumberHolder ? args[0].value : args[0]) >= this.level, ); this.level = level; } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 061a6d3a194..63955b02de8 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -8,7 +8,7 @@ import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import type PokemonSpecies from "#app/data/pokemon-species"; import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species"; import { speciesStarterCosts } from "#app/data/balance/starters"; -import * as Utils from "#app/utils"; +import { randInt, getEnumKeys, isLocal, executeIf, fixedInt, randSeedItem, NumberHolder } from "#app/utils"; import Overrides from "#app/overrides"; import PokemonData from "#app/system/pokemon-data"; import PersistentModifierData from "#app/system/modifier-data"; @@ -37,6 +37,7 @@ import { setSettingGamepad, SettingGamepad, settingGamepadDefaults } from "#app/ import type { SettingKeyboard } from "#app/system/settings/settings-keyboard"; import { setSettingKeyboard } from "#app/system/settings/settings-keyboard"; import { TagAddedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; +// biome-ignore lint/style/noNamespaceImport: Something weird is going on here and I don't want to touch it import * as Modifier from "#app/modifier/modifier"; import { StatusEffect } from "#enums/status-effect"; import ChallengeData from "#app/system/challenge-data"; @@ -360,8 +361,8 @@ export class GameData { this.loadSettings(); this.loadGamepadSettings(); this.loadMappingConfigs(); - this.trainerId = Utils.randInt(65536); - this.secretId = Utils.randInt(65536); + this.trainerId = randInt(65536); + this.secretId = randInt(65536); this.starterData = {}; this.gameStats = new GameStats(); this.runHistory = {}; @@ -589,7 +590,7 @@ export class GameData { } if (systemData.voucherCounts) { - Utils.getEnumKeys(VoucherType).forEach(key => { + getEnumKeys(VoucherType).forEach(key => { const index = VoucherType[key]; this.voucherCounts[index] = systemData.voucherCounts[index] || 0; }); @@ -617,7 +618,7 @@ export class GameData { * At the moment, only retrievable from locale cache */ async getRunHistoryData(): Promise { - if (!Utils.isLocal) { + if (!isLocal) { /** * Networking Code DO NOT DELETE! * Note: Might have to be migrated to `pokerogue-api.ts` @@ -1035,6 +1036,7 @@ export class GameData { } getSession(slotId: number): Promise { + // biome-ignore lint/suspicious/noAsyncPromiseExecutor: return new Promise(async (resolve, reject) => { if (slotId < 0) { return resolve(null); @@ -1075,6 +1077,7 @@ export class GameData { } loadSession(slotId: number, sessionData?: SessionSaveData): Promise { + // biome-ignore lint/suspicious/noAsyncPromiseExecutor: return new Promise(async (resolve, reject) => { try { const initSessionFromData = async (sessionData: SessionSaveData) => { @@ -1406,7 +1409,7 @@ export class GameData { saveAll(skipVerification = false, sync = false, useCachedSession = false, useCachedSystem = false): Promise { return new Promise(resolve => { - Utils.executeIf(!skipVerification, updateUserInfo).then(success => { + executeIf(!skipVerification, updateUserInfo).then(success => { if (success !== null && !success) { return resolve(false); } @@ -1586,7 +1589,7 @@ export class GameData { } const displayError = (error: string) => - globalScene.ui.showText(error, null, () => globalScene.ui.showText("", 0), Utils.fixedInt(1500)); + globalScene.ui.showText(error, null, () => globalScene.ui.showText("", 0), fixedInt(1500)); dataName = dataName!; // tell TS compiler that dataName is defined! if (!valid) { @@ -1594,7 +1597,7 @@ export class GameData { `Your ${dataName} data could not be loaded. It may be corrupted.`, null, () => globalScene.ui.showText("", 0), - Utils.fixedInt(1500), + fixedInt(1500), ); } @@ -1687,7 +1690,7 @@ export class GameData { () => { const neutralNatures = [Nature.HARDY, Nature.DOCILE, Nature.SERIOUS, Nature.BASHFUL, Nature.QUIRKY]; for (let s = 0; s < defaultStarterSpecies.length; s++) { - defaultStarterNatures.push(Utils.randSeedItem(neutralNatures)); + defaultStarterNatures.push(randSeedItem(neutralNatures)); } }, 0, @@ -2188,7 +2191,7 @@ export class GameData { value = decrementValue(value); } - const cost = new Utils.NumberHolder(value); + const cost = new NumberHolder(value); applyChallenges(ChallengeType.STARTER_COST, speciesId, cost); return cost.value; @@ -2216,7 +2219,7 @@ export class GameData { entry.hatchedCount = 0; } if (!entry.hasOwnProperty("natureAttr") || (entry.caughtAttr && !entry.natureAttr)) { - entry.natureAttr = this.defaultDexData?.[k].natureAttr || 1 << Utils.randInt(25, 1); + entry.natureAttr = this.defaultDexData?.[k].natureAttr || 1 << randInt(25, 1); } } } diff --git a/src/system/game-speed.ts b/src/system/game-speed.ts index d9c48664f80..3df47fafc6c 100644 --- a/src/system/game-speed.ts +++ b/src/system/game-speed.ts @@ -3,7 +3,7 @@ import type FadeIn from "phaser3-rex-plugins/plugins/audio/fade/FadeIn"; import type FadeOut from "phaser3-rex-plugins/plugins/audio/fade/FadeOut"; import type BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; -import * as Utils from "../utils"; +import { FixedInt } from "#app/utils"; type FadeInType = typeof FadeIn; type FadeOutType = typeof FadeOut; @@ -11,9 +11,9 @@ type FadeOutType = typeof FadeOut; export function initGameSpeed() { const thisArg = this as BattleScene; - const transformValue = (value: number | Utils.FixedInt): number => { - if (value instanceof Utils.FixedInt) { - return (value as Utils.FixedInt).value; + const transformValue = (value: number | FixedInt): number => { + if (value instanceof FixedInt) { + return (value as FixedInt).value; } return thisArg.gameSpeed === 1 ? value : Math.ceil((value /= thisArg.gameSpeed)); }; diff --git a/src/system/version_migration/version_converter.ts b/src/system/version_migration/version_converter.ts index 4b712609819..1fdb9e93f88 100644 --- a/src/system/version_migration/version_converter.ts +++ b/src/system/version_migration/version_converter.ts @@ -48,12 +48,15 @@ export const settingsMigrators: Readonly = [settingsMigr // import * as vA_B_C from "./versions/vA_B_C"; // --- v1.0.4 (and below) PATCHES --- // +// biome-ignore lint/style/noNamespaceImport: Convenience (TODO: make this a file-wide ignore when Biome supports those) import * as v1_0_4 from "./versions/v1_0_4"; // --- v1.7.0 PATCHES --- // +// biome-ignore lint/style/noNamespaceImport: Convenience import * as v1_7_0 from "./versions/v1_7_0"; // --- v1.8.3 PATCHES --- // +// biome-ignore lint/style/noNamespaceImport: Convenience import * as v1_8_3 from "./versions/v1_8_3"; /** Current game version */ diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstact-option-select-ui-handler.ts index f605f73e171..b360065f61d 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstact-option-select-ui-handler.ts @@ -3,7 +3,7 @@ import { TextStyle, addBBCodeTextObject, getTextColor, getTextStyleOptions } fro import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; -import * as Utils from "../utils"; +import { rgbHexToRgba, fixedInt } from "#app/utils"; import { argbFromRgba } from "@material/material-color-utilities"; import { Button } from "#enums/buttons"; import BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodetext/BBCodeText"; @@ -178,8 +178,8 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { itemOverlayIcon.setPositionRelative(this.optionSelectText, 36 * this.scale, 7 + i * (114 * this.scale - 3)); if (option.itemArgs) { - itemIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[0]))); - itemOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[1]))); + itemIcon.setTint(argbFromRgba(rgbHexToRgba(option.itemArgs[0]))); + itemOverlayIcon.setTint(argbFromRgba(rgbHexToRgba(option.itemArgs[1]))); } } } @@ -207,7 +207,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { this.blockInput = true; this.optionSelectTextContainer.setAlpha(0.5); this.cursorObj?.setAlpha(0.8); - globalScene.time.delayedCall(Utils.fixedInt(this.config.delay), () => this.unblockInput()); + globalScene.time.delayedCall(fixedInt(this.config.delay), () => this.unblockInput()); } if (this.config?.supportHover) { diff --git a/src/ui/arena-flyout.ts b/src/ui/arena-flyout.ts index 36a44eb5aa0..1eb18a32f98 100644 --- a/src/ui/arena-flyout.ts +++ b/src/ui/arena-flyout.ts @@ -16,7 +16,7 @@ import type { TurnEndEvent } from "../events/battle-scene"; import { BattleSceneEventType } from "../events/battle-scene"; import { ArenaTagType } from "#enums/arena-tag-type"; import TimeOfDayWidget from "./time-of-day-widget"; -import * as Utils from "../utils"; +import { toCamelCaseString, formatText, fixedInt } from "#app/utils"; import type { ParseKeys } from "i18next"; import i18next from "i18next"; @@ -47,10 +47,10 @@ export function getFieldEffectText(arenaTagType: string): string { if (!arenaTagType || arenaTagType === ArenaTagType.NONE) { return arenaTagType; } - const effectName = Utils.toCamelCaseString(arenaTagType); + const effectName = toCamelCaseString(arenaTagType); const i18nKey = `arenaFlyout:${effectName}` as ParseKeys; const resultName = i18next.t(i18nKey); - return !resultName || resultName === i18nKey ? Utils.formatText(arenaTagType) : resultName; + return !resultName || resultName === i18nKey ? formatText(arenaTagType) : resultName; } export class ArenaFlyout extends Phaser.GameObjects.Container { @@ -411,7 +411,7 @@ export class ArenaFlyout extends Phaser.GameObjects.Container { globalScene.tweens.add({ targets: this.flyoutParent, x: visible ? this.anchorX : this.anchorX - this.translationX, - duration: Utils.fixedInt(125), + duration: fixedInt(125), ease: "Sine.easeInOut", alpha: visible ? 1 : 0, onComplete: () => (this.timeOfDayWidget.parentVisible = visible), diff --git a/src/ui/base-stats-overlay.ts b/src/ui/base-stats-overlay.ts index 5a6c67cae7b..d0b0aff3a9d 100644 --- a/src/ui/base-stats-overlay.ts +++ b/src/ui/base-stats-overlay.ts @@ -1,7 +1,7 @@ import type { InfoToggle } from "../battle-scene"; import { TextStyle, addTextObject } from "./text"; import { addWindow } from "./ui-theme"; -import * as Utils from "../utils"; +import { fixedInt } from "#app/utils"; import i18next from "i18next"; import { globalScene } from "#app/global-scene"; @@ -93,7 +93,7 @@ export class BaseStatsOverlay extends Phaser.GameObjects.Container implements In } globalScene.tweens.add({ targets: this.statsLabels, - duration: Utils.fixedInt(125), + duration: fixedInt(125), ease: "Sine.easeInOut", alpha: visible ? 1 : 0, }); diff --git a/src/ui/battle-flyout.ts b/src/ui/battle-flyout.ts index 206546ad9cb..854f4cc4dd9 100644 --- a/src/ui/battle-flyout.ts +++ b/src/ui/battle-flyout.ts @@ -1,6 +1,6 @@ import type { default as Pokemon } from "../field/pokemon"; import { addTextObject, TextStyle } from "./text"; -import * as Utils from "../utils"; +import { fixedInt } from "#app/utils"; import { globalScene } from "#app/global-scene"; import type Move from "#app/data/moves/move"; import type { BerryUsedEvent, MoveUsedEvent } from "../events/battle-scene"; @@ -201,7 +201,7 @@ export default class BattleFlyout extends Phaser.GameObjects.Container { globalScene.tweens.add({ targets: this.flyoutParent, x: visible ? this.anchorX : this.anchorX - this.translationX, - duration: Utils.fixedInt(125), + duration: fixedInt(125), ease: "Sine.easeInOut", alpha: visible ? 1 : 0, }); diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index ab006269d4e..2b205329ab8 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -1,6 +1,6 @@ import type { EnemyPokemon, default as Pokemon } from "../field/pokemon"; import { getLevelTotalExp, getLevelRelExp } from "../data/exp"; -import * as Utils from "../utils"; +import { getLocalizedSpriteKey, fixedInt } from "#app/utils"; import { addTextObject, TextStyle } from "./text"; import { getGenderSymbol, getGenderColor, Gender } from "../data/gender"; import { StatusEffect } from "#enums/status-effect"; @@ -163,7 +163,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.splicedIcon.setInteractive(new Phaser.Geom.Rectangle(0, 0, 12, 15), Phaser.Geom.Rectangle.Contains); this.add(this.splicedIcon); - this.statusIndicator = globalScene.add.sprite(0, 0, Utils.getLocalizedSpriteKey("statuses")); + this.statusIndicator = globalScene.add.sprite(0, 0, getLocalizedSpriteKey("statuses")); this.statusIndicator.setName("icon_status"); this.statusIndicator.setVisible(false); this.statusIndicator.setOrigin(0, 0); @@ -536,7 +536,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { toggleStats(visible: boolean): void { globalScene.tweens.add({ targets: this.statsContainer, - duration: Utils.fixedInt(125), + duration: fixedInt(125), ease: "Sine.easeInOut", alpha: visible ? 1 : 0, }); diff --git a/src/ui/bgm-bar.ts b/src/ui/bgm-bar.ts index 45ed766c7fa..d944453ba2c 100644 --- a/src/ui/bgm-bar.ts +++ b/src/ui/bgm-bar.ts @@ -1,6 +1,6 @@ import { addTextObject, TextStyle } from "./text"; import i18next from "i18next"; -import * as Utils from "#app/utils"; +import { formatText } from "#app/utils"; import { globalScene } from "#app/global-scene"; const hiddenX = -150; @@ -100,7 +100,7 @@ export default class BgmBar extends Phaser.GameObjects.Container { getRealBgmName(bgmName: string): string { return i18next.t([`bgmName:${bgmName}`, "bgmName:missing_entries"], { - name: Utils.formatText(bgmName), + name: formatText(bgmName), }); } } diff --git a/src/ui/candy-bar.ts b/src/ui/candy-bar.ts index ba85ed7fef3..0cf3e0c91e9 100644 --- a/src/ui/candy-bar.ts +++ b/src/ui/candy-bar.ts @@ -2,7 +2,7 @@ import { starterColors } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; import { TextStyle, addTextObject } from "./text"; import { argbFromRgba } from "@material/material-color-utilities"; -import * as Utils from "../utils"; +import { rgbHexToRgba } from "#app/utils"; import type { Species } from "#enums/species"; export default class CandyBar extends Phaser.GameObjects.Container { @@ -60,8 +60,8 @@ export default class CandyBar extends Phaser.GameObjects.Container { const colorScheme = starterColors[starterSpeciesId]; - this.candyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0]))); - this.candyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1]))); + this.candyIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[0]))); + this.candyOverlayIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[1]))); this.countText.setText( `${globalScene.gameData.starterData[starterSpeciesId].candyCount + count} (+${count.toString()})`, diff --git a/src/ui/challenges-select-ui-handler.ts b/src/ui/challenges-select-ui-handler.ts index 61989cd594e..caffede2487 100644 --- a/src/ui/challenges-select-ui-handler.ts +++ b/src/ui/challenges-select-ui-handler.ts @@ -5,7 +5,7 @@ import { addWindow } from "./ui-theme"; import { Button } from "#enums/buttons"; import i18next from "i18next"; import type { Challenge } from "#app/data/challenge"; -import * as Utils from "../utils"; +import { getLocalizedSpriteKey } from "#app/utils"; import { Challenges } from "#app/enums/challenges"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { Color, ShadowColor } from "#app/enums/color"; @@ -193,7 +193,7 @@ export default class GameChallengesUiHandler extends UiHandler { }; } - this.monoTypeValue = globalScene.add.sprite(8, 98, Utils.getLocalizedSpriteKey("types")); + this.monoTypeValue = globalScene.add.sprite(8, 98, getLocalizedSpriteKey("types")); this.monoTypeValue.setName("challenge-value-monotype-sprite"); this.monoTypeValue.setScale(0.86); this.monoTypeValue.setVisible(false); diff --git a/src/ui/char-sprite.ts b/src/ui/char-sprite.ts index 74c021a65b8..f717927c107 100644 --- a/src/ui/char-sprite.ts +++ b/src/ui/char-sprite.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import * as Utils from "../utils"; +import { MissingTextureKey } from "#app/utils"; export default class CharSprite extends Phaser.GameObjects.Container { private sprite: Phaser.GameObjects.Sprite; @@ -57,7 +57,7 @@ export default class CharSprite extends Phaser.GameObjects.Container { }, }); - this.setVisible(globalScene.textures.get(key).key !== Utils.MissingTextureKey); + this.setVisible(globalScene.textures.get(key).key !== MissingTextureKey); this.shown = true; this.key = key; diff --git a/src/ui/daily-run-scoreboard.ts b/src/ui/daily-run-scoreboard.ts index 53c737898e7..896f2171676 100644 --- a/src/ui/daily-run-scoreboard.ts +++ b/src/ui/daily-run-scoreboard.ts @@ -1,6 +1,6 @@ import i18next from "i18next"; import { globalScene } from "#app/global-scene"; -import * as Utils from "../utils"; +import { getEnumKeys, executeIf } from "#app/utils"; import { TextStyle, addTextObject } from "./text"; import { WindowVariant, addWindow } from "./ui-theme"; import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; @@ -89,7 +89,7 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container { this.prevCategoryButton.setInteractive(new Phaser.Geom.Rectangle(0, 0, 6, 10), Phaser.Geom.Rectangle.Contains); this.prevCategoryButton.on("pointerup", () => { - this.update(this.category ? this.category - 1 : Utils.getEnumKeys(ScoreboardCategory).length - 1); + this.update(this.category ? this.category - 1 : getEnumKeys(ScoreboardCategory).length - 1); }); this.nextCategoryButton = globalScene.add.sprite(window.displayWidth - 4, 4, "cursor"); @@ -98,7 +98,7 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container { this.nextCategoryButton.setInteractive(new Phaser.Geom.Rectangle(0, 0, 6, 10), Phaser.Geom.Rectangle.Contains); this.nextCategoryButton.on("pointerup", () => { - this.update(this.category < Utils.getEnumKeys(ScoreboardCategory).length - 1 ? this.category + 1 : 0); + this.update(this.category < getEnumKeys(ScoreboardCategory).length - 1 ? this.category + 1 : 0); }); this.prevPageButton = globalScene.add.sprite( @@ -226,7 +226,7 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container { this.page = page = 1; } - Utils.executeIf(category !== this.category || this.pageCount === undefined, () => + executeIf(category !== this.category || this.pageCount === undefined, () => pokerogueApi.daily.getRankingsPageCount({ category }).then(count => (this.pageCount = count)), ) .then(() => { diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index cb6a474f01d..956a308448b 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -1,7 +1,7 @@ import { Mode } from "./ui"; import { TextStyle, addTextObject, getEggTierTextTint, getTextStyleOptions } from "./text"; import MessageUiHandler from "./message-ui-handler"; -import * as Utils from "../utils"; +import { getEnumValues, getEnumKeys, fixedInt, randSeedShuffle } from "#app/utils"; import type { IEggOptions } from "../data/egg"; import { Egg, getLegendaryGachaSpeciesForTimestamp } from "../data/egg"; import { VoucherType, getVoucherTypeIcon } from "../system/voucher"; @@ -83,7 +83,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { }); } - Utils.getEnumValues(GachaType).forEach((gachaType, g) => { + getEnumValues(GachaType).forEach((gachaType, g) => { const gachaTypeKey = GachaType[gachaType].toString().toLowerCase(); const gachaContainer = globalScene.add.container(180 * g, 18); @@ -272,7 +272,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { this.eggGachaContainer.add(this.eggGachaOptionsContainer); - new Array(Utils.getEnumKeys(VoucherType).length).fill(null).map((_, i) => { + new Array(getEnumKeys(VoucherType).length).fill(null).map((_, i) => { const container = globalScene.add.container(globalScene.game.canvas.width / 6 - 56 * i, 0); const bg = addWindow(0, 0, 56, 22); @@ -355,7 +355,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { if (this.transitioning && this.transitionCancelled) { delay = Math.ceil(delay / 5); } - return Utils.fixedInt(delay); + return fixedInt(delay); } pull(pullCount = 0, count = 0, eggs?: Egg[]): void { @@ -476,7 +476,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { eggs.push(egg); } // Shuffle the eggs in case the guaranteed one got added as last egg - eggs = Utils.randSeedShuffle(eggs); + eggs = randSeedShuffle(eggs); (globalScene.currentBattle ? globalScene.gameData.saveAll(true, true, true) @@ -643,7 +643,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { } showError(text: string): void { - this.showText(text, undefined, () => this.showText(this.defaultText), Utils.fixedInt(1500)); + this.showText(text, undefined, () => this.showText(this.defaultText), fixedInt(1500)); } setTransitioning(transitioning: boolean): void { @@ -783,7 +783,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { } break; case Button.RIGHT: - if (this.gachaCursor < Utils.getEnumKeys(GachaType).length - 1) { + if (this.gachaCursor < getEnumKeys(GachaType).length - 1) { success = this.setGachaCursor(this.gachaCursor + 1); } break; diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index 9f76e85f228..3775dbc2228 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -6,7 +6,7 @@ import { PokemonType } from "#enums/pokemon-type"; import { Command } from "./command-ui-handler"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; -import * as Utils from "../utils"; +import { getLocalizedSpriteKey, fixedInt, padInt } from "#app/utils"; import { MoveCategory } from "#enums/MoveCategory"; import i18next from "i18next"; import { Button } from "#enums/buttons"; @@ -54,7 +54,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { this.typeIcon = globalScene.add.sprite( globalScene.scaledCanvas.width - 57, -36, - Utils.getLocalizedSpriteKey("types"), + getLocalizedSpriteKey("types"), "unknown", ); this.typeIcon.setVisible(false); @@ -199,7 +199,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { } globalScene.tweens.add({ targets: [this.movesContainer, this.cursorObj], - duration: Utils.fixedInt(125), + duration: fixedInt(125), ease: "Sine.easeInOut", alpha: visible ? 0 : 1, }); @@ -245,7 +245,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { if (hasMove) { const pokemonMove = moveset[cursor]; const moveType = pokemon.getMoveType(pokemonMove.getMove()); - const textureKey = Utils.getLocalizedSpriteKey("types"); + const textureKey = getLocalizedSpriteKey("types"); this.typeIcon.setTexture(textureKey, PokemonType[moveType].toLowerCase()).setScale(0.8); const moveCategory = pokemonMove.getMove().category; @@ -255,8 +255,8 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { const maxPP = pokemonMove.getMovePp(); const pp = maxPP - pokemonMove.ppUsed; - const ppLeftStr = Utils.padInt(pp, 2, " "); - const ppMaxStr = Utils.padInt(maxPP, 2, " "); + const ppLeftStr = padInt(pp, 2, " "); + const ppMaxStr = padInt(maxPP, 2, " "); this.ppText.setText(`${ppLeftStr}/${ppMaxStr}`); this.powerText.setText(`${power >= 0 ? power : "---"}`); this.accuracyText.setText(`${accuracy >= 0 ? accuracy : "---"}`); diff --git a/src/ui/form-modal-ui-handler.ts b/src/ui/form-modal-ui-handler.ts index 8784145acd6..e27b2e9ed89 100644 --- a/src/ui/form-modal-ui-handler.ts +++ b/src/ui/form-modal-ui-handler.ts @@ -4,7 +4,7 @@ import type { Mode } from "./ui"; import { TextStyle, addTextInputObject, addTextObject } from "./text"; import { WindowVariant, addWindow } from "./ui-theme"; import type InputText from "phaser3-rex-plugins/plugins/inputtext"; -import * as Utils from "../utils"; +import { fixedInt } from "#app/utils"; import { Button } from "#enums/buttons"; import { globalScene } from "#app/global-scene"; @@ -135,7 +135,7 @@ export abstract class FormModalUiHandler extends ModalUiHandler { this.tween = globalScene.tweens.add({ targets: this.modalContainer, - duration: Utils.fixedInt(1000), + duration: fixedInt(1000), ease: "Sine.easeInOut", y: "-=24", alpha: 1, diff --git a/src/ui/game-stats-ui-handler.ts b/src/ui/game-stats-ui-handler.ts index 7d3decf0c4c..2e2112dfda4 100644 --- a/src/ui/game-stats-ui-handler.ts +++ b/src/ui/game-stats-ui-handler.ts @@ -3,7 +3,7 @@ import { TextStyle, addTextObject } from "#app/ui/text"; import type { Mode } from "#app/ui/ui"; import UiHandler from "#app/ui/ui-handler"; import { addWindow } from "#app/ui/ui-theme"; -import * as Utils from "#app/utils"; +import { getPlayTimeString, formatFancyLargeNumber, toReadableString } from "#app/utils"; import type { GameData } from "#app/system/game-data"; import { DexAttr } from "#app/system/game-data"; import { speciesStarterCosts } from "#app/data/balance/starters"; @@ -25,7 +25,7 @@ interface DisplayStats { const displayStats: DisplayStats = { playTime: { label_key: "playTime", - sourceFunc: gameData => Utils.getPlayTimeString(gameData.gameStats.playTime), + sourceFunc: gameData => getPlayTimeString(gameData.gameStats.playTime), }, battles: { label_key: "totalBattles", @@ -91,7 +91,7 @@ const displayStats: DisplayStats = { }, highestMoney: { label_key: "highestMoney", - sourceFunc: gameData => Utils.formatFancyLargeNumber(gameData.gameStats.highestMoney), + sourceFunc: gameData => formatFancyLargeNumber(gameData.gameStats.highestMoney), }, highestDamage: { label_key: "highestDamage", @@ -435,7 +435,7 @@ export function initStatsKeys() { } if (!(displayStats[key] as DisplayStat).label_key) { const splittableKey = key.replace(/([a-z]{2,})([A-Z]{1}(?:[^A-Z]|$))/g, "$1_$2"); - (displayStats[key] as DisplayStat).label_key = Utils.toReadableString( + (displayStats[key] as DisplayStat).label_key = toReadableString( `${splittableKey[0].toUpperCase()}${splittableKey.slice(1)}`, ); } diff --git a/src/ui/login-form-ui-handler.ts b/src/ui/login-form-ui-handler.ts index 1087ffa3fd1..5c009357443 100644 --- a/src/ui/login-form-ui-handler.ts +++ b/src/ui/login-form-ui-handler.ts @@ -1,7 +1,7 @@ import type { InputFieldConfig } from "./form-modal-ui-handler"; import { FormModalUiHandler } from "./form-modal-ui-handler"; import type { ModalConfig } from "./modal-ui-handler"; -import * as Utils from "../utils"; +import { fixedInt } from "#app/utils"; import { Mode } from "./ui"; import i18next from "i18next"; import { addTextObject, TextStyle } from "./text"; @@ -283,7 +283,7 @@ export default class LoginFormUiHandler extends FormModalUiHandler { this.externalPartyContainer.setAlpha(0); globalScene.tweens.add({ targets: this.externalPartyContainer, - duration: Utils.fixedInt(1000), + duration: fixedInt(1000), ease: "Sine.easeInOut", y: "-=24", alpha: 1, @@ -292,7 +292,7 @@ export default class LoginFormUiHandler extends FormModalUiHandler { this.infoContainer.setAlpha(0); globalScene.tweens.add({ targets: this.infoContainer, - duration: Utils.fixedInt(1000), + duration: fixedInt(1000), ease: "Sine.easeInOut", y: "-=24", alpha: 1, diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index b83ae24c9e0..241ddbb91a8 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -2,7 +2,7 @@ import { bypassLogin } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; import { TextStyle, addTextObject, getTextStyleOptions } from "./text"; import { Mode } from "./ui"; -import * as Utils from "../utils"; +import { getEnumKeys, isLocal, isBeta, fixedInt, getCookie, sessionIdKey } from "#app/utils"; import { addWindow, WindowVariant } from "./ui-theme"; import MessageUiHandler from "./message-ui-handler"; import type { OptionSelectConfig, OptionSelectItem } from "./abstact-option-select-ui-handler"; @@ -75,7 +75,7 @@ export default class MenuUiHandler extends MessageUiHandler { { condition: bypassLogin, options: [MenuOptions.LOG_OUT] }, ]; - this.menuOptions = Utils.getEnumKeys(MenuOptions) + this.menuOptions = getEnumKeys(MenuOptions) .map(m => Number.parseInt(MenuOptions[m]) as MenuOptions) .filter(m => { return !this.excludedMenus().some(exclusion => exclusion.condition && exclusion.options.includes(m)); @@ -130,7 +130,7 @@ export default class MenuUiHandler extends MessageUiHandler { { condition: bypassLogin, options: [MenuOptions.LOG_OUT] }, ]; - this.menuOptions = Utils.getEnumKeys(MenuOptions) + this.menuOptions = getEnumKeys(MenuOptions) .map(m => Number.parseInt(MenuOptions[m]) as MenuOptions) .filter(m => { return !this.excludedMenus().some(exclusion => exclusion.condition && exclusion.options.includes(m)); @@ -238,7 +238,7 @@ export default class MenuUiHandler extends MessageUiHandler { }); }; - if (Utils.isLocal || Utils.isBeta) { + if (isLocal || isBeta) { manageDataOptions.push({ label: i18next.t("menuUiHandler:importSession"), handler: () => { @@ -292,7 +292,7 @@ export default class MenuUiHandler extends MessageUiHandler { }, keepOpen: true, }); - if (Utils.isLocal || Utils.isBeta) { + if (isLocal || isBeta) { manageDataOptions.push({ label: i18next.t("menuUiHandler:importData"), handler: () => { @@ -328,7 +328,7 @@ export default class MenuUiHandler extends MessageUiHandler { keepOpen: true, }, ); - if (Utils.isLocal || Utils.isBeta) { + if (isLocal || isBeta) { // this should make sure we don't have this option in live manageDataOptions.push({ label: "Test Dialogue", @@ -510,7 +510,7 @@ export default class MenuUiHandler extends MessageUiHandler { this.render(); super.show(args); - this.menuOptions = Utils.getEnumKeys(MenuOptions) + this.menuOptions = getEnumKeys(MenuOptions) .map(m => Number.parseInt(MenuOptions[m]) as MenuOptions) .filter(m => { return !this.excludedMenus().some(exclusion => exclusion.condition && exclusion.options.includes(m)); @@ -574,7 +574,7 @@ export default class MenuUiHandler extends MessageUiHandler { ui.setOverlayMode(Mode.EGG_LIST); success = true; } else { - ui.showText(i18next.t("menuUiHandler:noEggs"), null, () => ui.showText(""), Utils.fixedInt(1500)); + ui.showText(i18next.t("menuUiHandler:noEggs"), null, () => ui.showText(""), fixedInt(1500)); error = true; } break; @@ -607,7 +607,7 @@ export default class MenuUiHandler extends MessageUiHandler { : i18next.t("menuUiHandler:unlinkDiscord"), handler: () => { if (loggedInUser?.discordId === "") { - const token = Utils.getCookie(Utils.sessionIdKey); + const token = getCookie(sessionIdKey); const redirectUri = encodeURIComponent(`${import.meta.env.VITE_SERVER_URL}/auth/discord/callback`); const discordId = import.meta.env.VITE_DISCORD_CLIENT_ID; const discordUrl = `https://discord.com/api/oauth2/authorize?client_id=${discordId}&redirect_uri=${redirectUri}&response_type=code&scope=identify&state=${token}&prompt=none`; @@ -627,7 +627,7 @@ export default class MenuUiHandler extends MessageUiHandler { : i18next.t("menuUiHandler:unlinkGoogle"), handler: () => { if (loggedInUser?.googleId === "") { - const token = Utils.getCookie(Utils.sessionIdKey); + const token = getCookie(sessionIdKey); const redirectUri = encodeURIComponent(`${import.meta.env.VITE_SERVER_URL}/auth/google/callback`); const googleId = import.meta.env.VITE_GOOGLE_CLIENT_ID; const googleUrl = `https://accounts.google.com/o/oauth2/auth?client_id=${googleId}&response_type=code&redirect_uri=${redirectUri}&scope=openid&state=${token}`; diff --git a/src/ui/message-ui-handler.ts b/src/ui/message-ui-handler.ts index e927793e0ab..b57b236531c 100644 --- a/src/ui/message-ui-handler.ts +++ b/src/ui/message-ui-handler.ts @@ -1,6 +1,6 @@ import AwaitableUiHandler from "./awaitable-ui-handler"; import type { Mode } from "./ui"; -import * as Utils from "../utils"; +import { getFrameMs } from "#app/utils"; import { globalScene } from "#app/global-scene"; export default abstract class MessageUiHandler extends AwaitableUiHandler { @@ -183,7 +183,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { if (charDelay) { this.textTimer!.paused = true; // TODO: is the bang correct? globalScene.tweens.addCounter({ - duration: Utils.getFrameMs(charDelay), + duration: getFrameMs(charDelay), onComplete: () => { this.textTimer!.paused = false; // TODO: is the bang correct? advance(); @@ -193,7 +193,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { this.textTimer!.paused = true; globalScene.time.delayedCall(150, () => { globalScene.ui.fadeOut(750).then(() => { - const delay = Utils.getFrameMs(charFade); + const delay = getFrameMs(charFade); globalScene.time.delayedCall(delay, () => { globalScene.ui.fadeIn(500).then(() => { this.textTimer!.paused = false; diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index e5d8f858782..26351d4dbf1 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -10,11 +10,10 @@ import { handleTutorial, Tutorial } from "../tutorial"; import { Button } from "#enums/buttons"; import MoveInfoOverlay from "./move-info-overlay"; import { allMoves } from "../data/moves/move"; -import * as Utils from "./../utils"; +import { formatMoney, NumberHolder } from "#app/utils"; import Overrides from "#app/overrides"; import i18next from "i18next"; import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; -import { NumberHolder } from "./../utils"; import Phaser from "phaser"; import type { PokeballType } from "#enums/pokeball"; @@ -645,7 +644,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { this.rerollCostText.setVisible(true); const canReroll = globalScene.money >= this.rerollCost; - const formattedMoney = Utils.formatMoney(globalScene.moneyFormat, this.rerollCost); + const formattedMoney = formatMoney(globalScene.moneyFormat, this.rerollCost); this.rerollCostText.setText(i18next.t("modifierSelectUiHandler:rerollCost", { formattedMoney })); this.rerollCostText.setColor(this.getTextColor(canReroll ? TextStyle.MONEY : TextStyle.PARTY_RED)); @@ -933,7 +932,7 @@ class ModifierOption extends Phaser.GameObjects.Container { const cost = Overrides.WAIVE_ROLL_FEE_OVERRIDE ? 0 : this.modifierTypeOption.cost; const textStyle = cost <= globalScene.money ? TextStyle.MONEY : TextStyle.PARTY_RED; - const formattedMoney = Utils.formatMoney(globalScene.moneyFormat, cost); + const formattedMoney = formatMoney(globalScene.moneyFormat, cost); this.itemCostText.setText(i18next.t("modifierSelectUiHandler:itemCost", { formattedMoney })); this.itemCostText.setColor(getTextColor(textStyle, false, globalScene.uiTheme)); diff --git a/src/ui/move-info-overlay.ts b/src/ui/move-info-overlay.ts index 6fc99beb0ae..bd9fdf00c72 100644 --- a/src/ui/move-info-overlay.ts +++ b/src/ui/move-info-overlay.ts @@ -2,7 +2,7 @@ import type { InfoToggle } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; import { TextStyle, addTextObject } from "./text"; import { addWindow } from "./ui-theme"; -import * as Utils from "../utils"; +import { getLocalizedSpriteKey, fixedInt } from "#app/utils"; import type Move from "../data/moves/move"; import { MoveCategory } from "#enums/MoveCategory"; import { PokemonType } from "#enums/pokemon-type"; @@ -120,7 +120,7 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem valuesBg.setOrigin(0, 0); this.val.add(valuesBg); - this.typ = globalScene.add.sprite(25, EFF_HEIGHT - 35, Utils.getLocalizedSpriteKey("types"), "unknown"); + this.typ = globalScene.add.sprite(25, EFF_HEIGHT - 35, getLocalizedSpriteKey("types"), "unknown"); this.typ.setScale(0.8); this.val.add(this.typ); @@ -175,7 +175,7 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem this.pow.setText(move.power >= 0 ? move.power.toString() : "---"); this.acc.setText(move.accuracy >= 0 ? move.accuracy.toString() : "---"); this.pp.setText(move.pp >= 0 ? move.pp.toString() : "---"); - this.typ.setTexture(Utils.getLocalizedSpriteKey("types"), PokemonType[move.type].toLowerCase()); + this.typ.setTexture(getLocalizedSpriteKey("types"), PokemonType[move.type].toLowerCase()); this.cat.setFrame(MoveCategory[move.category].toLowerCase()); this.desc.setText(move?.effect || ""); @@ -193,10 +193,10 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem // generate scrolling effects this.descScroll = globalScene.tweens.add({ targets: this.desc, - delay: Utils.fixedInt(2000), + delay: fixedInt(2000), loop: -1, - hold: Utils.fixedInt(2000), - duration: Utils.fixedInt((moveDescriptionLineCount - 3) * 2000), + hold: fixedInt(2000), + duration: fixedInt((moveDescriptionLineCount - 3) * 2000), y: `-=${14.83 * (72 / 96) * (moveDescriptionLineCount - 3)}`, }); } @@ -219,7 +219,7 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem } globalScene.tweens.add({ targets: this.desc, - duration: Utils.fixedInt(125), + duration: fixedInt(125), ease: "Sine.easeInOut", alpha: visible ? 1 : 0, }); diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 87d2e2ba28c..2bf05302c55 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -6,8 +6,7 @@ import { addWindow, WindowVariant } from "./ui-theme"; import type { MysteryEncounterPhase } from "../phases/mystery-encounter-phases"; import { PartyUiMode } from "./party-ui-handler"; import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; -import * as Utils from "../utils"; -import { isNullOrUndefined } from "../utils"; +import { fixedInt, isNullOrUndefined } from "#app/utils"; import { getPokeballAtlasKey } from "../data/pokeball"; import type { OptionSelectSettings } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; @@ -456,10 +455,10 @@ export default class MysteryEncounterUiHandler extends UiHandler { if (optionTextWidth > nonScrollWidth) { this.optionScrollTweens[i] = globalScene.tweens.add({ targets: optionText, - delay: Utils.fixedInt(2000), + delay: fixedInt(2000), loop: -1, - hold: Utils.fixedInt(2000), - duration: Utils.fixedInt(((optionTextWidth - nonScrollWidth) / 15) * 2000), + hold: fixedInt(2000), + duration: fixedInt(((optionTextWidth - nonScrollWidth) / 15) * 2000), x: `-=${optionTextWidth - nonScrollWidth}`, }); } @@ -527,10 +526,10 @@ export default class MysteryEncounterUiHandler extends UiHandler { if (descriptionLineCount > 6) { this.descriptionScrollTween = globalScene.tweens.add({ targets: descriptionTextObject, - delay: Utils.fixedInt(2000), + delay: fixedInt(2000), loop: -1, - hold: Utils.fixedInt(2000), - duration: Utils.fixedInt((descriptionLineCount - 6) * 2000), + hold: fixedInt(2000), + duration: fixedInt((descriptionLineCount - 6) * 2000), y: `-=${10 * (descriptionLineCount - 6)}`, }); } @@ -637,10 +636,10 @@ export default class MysteryEncounterUiHandler extends UiHandler { if (tooltipLineCount > 3) { this.tooltipScrollTween = globalScene.tweens.add({ targets: tooltipTextObject, - delay: Utils.fixedInt(1200), + delay: fixedInt(1200), loop: -1, - hold: Utils.fixedInt(1200), - duration: Utils.fixedInt((tooltipLineCount - 3) * 1200), + hold: fixedInt(1200), + duration: fixedInt((tooltipLineCount - 3) * 1200), y: `-=${11.2 * (tooltipLineCount - 3)}`, }); } diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index ebaccc515c1..61a98d79fbb 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -5,7 +5,7 @@ import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "#ap import { Command } from "#app/ui/command-ui-handler"; import MessageUiHandler from "#app/ui/message-ui-handler"; import { Mode } from "#app/ui/ui"; -import * as Utils from "#app/utils"; +import { BooleanHolder, toReadableString, randInt, getLocalizedSpriteKey } from "#app/utils"; import { PokemonFormChangeItemModifier, PokemonHeldItemModifier, @@ -215,7 +215,7 @@ export default class PartyUiHandler extends MessageUiHandler { * @returns */ private FilterChallengeLegal = (pokemon: PlayerPokemon) => { - const challengeAllowed = new Utils.BooleanHolder(true); + const challengeAllowed = new BooleanHolder(true); applyChallenges(ChallengeType.POKEMON_IN_BATTLE, pokemon, challengeAllowed); if (!challengeAllowed.value) { return i18next.t("partyUiHandler:cantBeUsed", { @@ -1201,7 +1201,7 @@ export default class PartyUiHandler extends MessageUiHandler { if (this.localizedOptions.includes(option)) { optionName = i18next.t(`partyUiHandler:${PartyOption[option]}`); } else { - optionName = Utils.toReadableString(PartyOption[option]); + optionName = toReadableString(PartyOption[option]); } } break; @@ -1309,7 +1309,7 @@ export default class PartyUiHandler extends MessageUiHandler { } getReleaseMessage(pokemonName: string): string { - const rand = Utils.randInt(128); + const rand = randInt(128); if (rand < 20) { return i18next.t("partyUiHandler:goodbye", { pokemonName: pokemonName }); } @@ -1566,7 +1566,7 @@ class PartySlot extends Phaser.GameObjects.Container { } if (this.pokemon.status) { - const statusIndicator = globalScene.add.sprite(0, 0, Utils.getLocalizedSpriteKey("statuses")); + const statusIndicator = globalScene.add.sprite(0, 0, getLocalizedSpriteKey("statuses")); statusIndicator.setFrame(StatusEffect[this.pokemon.status?.effect].toLowerCase()); statusIndicator.setOrigin(0, 0); statusIndicator.setPositionRelative(slotLevelLabel, this.slotIndex >= battlerCount ? 43 : 55, 0); diff --git a/src/ui/pokedex-info-overlay.ts b/src/ui/pokedex-info-overlay.ts index 7dfa3745cb7..43e9bbc1a65 100644 --- a/src/ui/pokedex-info-overlay.ts +++ b/src/ui/pokedex-info-overlay.ts @@ -1,7 +1,7 @@ import type { InfoToggle } from "../battle-scene"; import { TextStyle, addTextObject } from "./text"; import { addWindow } from "./ui-theme"; -import * as Utils from "../utils"; +import { fixedInt } from "#app/utils"; import i18next from "i18next"; import { globalScene } from "#app/global-scene"; @@ -128,10 +128,10 @@ export default class PokedexInfoOverlay extends Phaser.GameObjects.Container imp // generate scrolling effects this.descScroll = globalScene.tweens.add({ targets: this.desc, - delay: Utils.fixedInt(2000), + delay: fixedInt(2000), loop: -1, - hold: Utils.fixedInt(2000), - duration: Utils.fixedInt((lineCount - 3) * 2000), + hold: fixedInt(2000), + duration: fixedInt((lineCount - 3) * 2000), y: `-=${14.83 * (72 / 96) * (lineCount - 3)}`, }); } @@ -154,7 +154,7 @@ export default class PokedexInfoOverlay extends Phaser.GameObjects.Container imp } globalScene.tweens.add({ targets: this.desc, - duration: Utils.fixedInt(125), + duration: fixedInt(125), ease: "Sine.easeInOut", alpha: visible ? 1 : 0, }); diff --git a/src/ui/pokedex-page-ui-handler.ts b/src/ui/pokedex-page-ui-handler.ts index eede346f052..407ebfcd843 100644 --- a/src/ui/pokedex-page-ui-handler.ts +++ b/src/ui/pokedex-page-ui-handler.ts @@ -54,7 +54,7 @@ import { toReadableString, } from "#app/utils"; import type { Nature } from "#enums/nature"; -import * as Utils from "../utils"; +import { getEnumKeys } from "#app/utils"; import { speciesTmMoves } from "#app/data/balance/tms"; import type { BiomeTierTod } from "#app/data/balance/biomes"; import { BiomePoolTier, catchableSpecies } from "#app/data/balance/biomes"; @@ -592,7 +592,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.menuContainer.setVisible(false); - this.menuOptions = Utils.getEnumKeys(MenuOptions).map(m => Number.parseInt(MenuOptions[m]) as MenuOptions); + this.menuOptions = getEnumKeys(MenuOptions).map(m => Number.parseInt(MenuOptions[m]) as MenuOptions); this.optionSelectText = addBBCodeTextObject( 0, @@ -696,7 +696,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.starterAttributes = this.initStarterPrefs(); - this.menuOptions = Utils.getEnumKeys(MenuOptions).map(m => Number.parseInt(MenuOptions[m]) as MenuOptions); + this.menuOptions = getEnumKeys(MenuOptions).map(m => Number.parseInt(MenuOptions[m]) as MenuOptions); this.menuContainer.setVisible(true); diff --git a/src/ui/pokemon-hatch-info-container.ts b/src/ui/pokemon-hatch-info-container.ts index 99940b92351..692f0f1d374 100644 --- a/src/ui/pokemon-hatch-info-container.ts +++ b/src/ui/pokemon-hatch-info-container.ts @@ -1,7 +1,7 @@ import PokemonInfoContainer from "#app/ui/pokemon-info-container"; import { Gender } from "#app/data/gender"; import { PokemonType } from "#enums/pokemon-type"; -import * as Utils from "#app/utils"; +import { rgbHexToRgba, padInt } from "#app/utils"; import { TextStyle, addTextObject } from "#app/ui/text"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { allMoves } from "#app/data/moves/move"; @@ -154,14 +154,14 @@ export default class PokemonHatchInfoContainer extends PokemonInfoContainer { super.show(pokemon, false, 1, hatchInfo.getDex(), hatchInfo.getStarterEntry(), true); const colorScheme = starterColors[species.speciesId]; - this.pokemonCandyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0]))); + this.pokemonCandyIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[0]))); this.pokemonCandyIcon.setVisible(true); - this.pokemonCandyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1]))); + this.pokemonCandyOverlayIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[1]))); this.pokemonCandyOverlayIcon.setVisible(true); this.pokemonCandyCountText.setText(`x${globalScene.gameData.starterData[species.speciesId].candyCount}`); this.pokemonCandyCountText.setVisible(true); - this.pokemonNumberText.setText(Utils.padInt(species.speciesId, 4)); + this.pokemonNumberText.setText(padInt(species.speciesId, 4)); this.pokemonNameText.setText(species.name); const hasEggMoves = species && speciesEggMoves.hasOwnProperty(species.speciesId); diff --git a/src/ui/pokemon-icon-anim-handler.ts b/src/ui/pokemon-icon-anim-handler.ts index c84ee2a0f9a..b6944c0fd84 100644 --- a/src/ui/pokemon-icon-anim-handler.ts +++ b/src/ui/pokemon-icon-anim-handler.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import * as Utils from "../utils"; +import { fixedInt } from "#app/utils"; export enum PokemonIconAnimMode { NONE, @@ -27,7 +27,7 @@ export default class PokemonIconAnimHandler { } }; globalScene.tweens.addCounter({ - duration: Utils.fixedInt(200), + duration: fixedInt(200), from: 0, to: 1, yoyo: true, diff --git a/src/ui/pokemon-info-container.ts b/src/ui/pokemon-info-container.ts index 1c880f6aec9..0ccece46ab9 100644 --- a/src/ui/pokemon-info-container.ts +++ b/src/ui/pokemon-info-container.ts @@ -8,7 +8,7 @@ import type Pokemon from "../field/pokemon"; import i18next from "i18next"; import type { DexEntry, StarterDataEntry } from "../system/game-data"; import { DexAttr } from "../system/game-data"; -import * as Utils from "../utils"; +import { fixedInt } from "#app/utils"; import ConfirmUiHandler from "./confirm-ui-handler"; import { StatsContainer } from "./stats-container"; import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text"; @@ -393,7 +393,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { if (!eggInfo) { globalScene.tweens.add({ targets: this, - duration: Utils.fixedInt(Math.floor(750 / speedMultiplier)), + duration: fixedInt(Math.floor(750 / speedMultiplier)), ease: "Cubic.easeInOut", x: this.initialX - this.infoWindowWidth, onComplete: () => { @@ -403,9 +403,9 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { if (showMoves) { globalScene.tweens.add({ - delay: Utils.fixedInt(Math.floor(325 / speedMultiplier)), + delay: fixedInt(Math.floor(325 / speedMultiplier)), targets: this.pokemonMovesContainer, - duration: Utils.fixedInt(Math.floor(325 / speedMultiplier)), + duration: fixedInt(Math.floor(325 / speedMultiplier)), ease: "Cubic.easeInOut", x: this.movesContainerInitialX - 57, onComplete: () => resolve(), @@ -463,7 +463,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { return new Promise(resolve => { globalScene.tweens.add({ targets: this, - duration: Utils.fixedInt(Math.floor(150 / speedMultiplier)), + duration: fixedInt(Math.floor(150 / speedMultiplier)), ease: "Cubic.easeInOut", x: xPosition, onComplete: () => { @@ -482,14 +482,14 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { globalScene.tweens.add({ targets: this.pokemonMovesContainer, - duration: Utils.fixedInt(Math.floor(750 / speedMultiplier)), + duration: fixedInt(Math.floor(750 / speedMultiplier)), ease: "Cubic.easeInOut", x: this.movesContainerInitialX, }); globalScene.tweens.add({ targets: this, - duration: Utils.fixedInt(Math.floor(750 / speedMultiplier)), + duration: fixedInt(Math.floor(750 / speedMultiplier)), ease: "Cubic.easeInOut", x: this.initialX, onComplete: () => { diff --git a/src/ui/run-history-ui-handler.ts b/src/ui/run-history-ui-handler.ts index 85ea1e93e8d..ffc9d378d18 100644 --- a/src/ui/run-history-ui-handler.ts +++ b/src/ui/run-history-ui-handler.ts @@ -3,7 +3,7 @@ import { GameModes } from "../game-mode"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; -import * as Utils from "../utils"; +import { fixedInt, formatLargeNumber } from "#app/utils"; import type PokemonData from "../system/pokemon-data"; import MessageUiHandler from "./message-ui-handler"; import i18next from "i18next"; @@ -218,7 +218,7 @@ export default class RunHistoryUiHandler extends MessageUiHandler { globalScene.tweens.add({ targets: this.runsContainer, y: this.runContainerInitialY - 56 * scrollCursor, - duration: Utils.fixedInt(325), + duration: fixedInt(325), ease: "Sine.easeInOut", }); } @@ -314,7 +314,7 @@ class RunEntryContainer extends Phaser.GameObjects.Container { const enemyLevel = addTextObject( 32, 20, - `${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatLargeNumber(enemy.level, 1000)}`, + `${i18next.t("saveSlotSelectUiHandler:lv")}${formatLargeNumber(enemy.level, 1000)}`, TextStyle.PARTY, { fontSize: "54px", color: "#f8f8f8" }, ); @@ -408,7 +408,7 @@ class RunEntryContainer extends Phaser.GameObjects.Container { const text = addTextObject( 32, 20, - `${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatLargeNumber(pokemon.level, 1000)}`, + `${i18next.t("saveSlotSelectUiHandler:lv")}${formatLargeNumber(pokemon.level, 1000)}`, TextStyle.PARTY, { fontSize: "54px", color: "#f8f8f8" }, ); diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index 8719950381a..47de6a1a64d 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -5,7 +5,7 @@ import { TextStyle, addTextObject, addBBCodeTextObject, getTextColor } from "./t import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; import { getPokeballAtlasKey } from "#app/data/pokeball"; -import * as Utils from "../utils"; +import { formatLargeNumber, getPlayTimeString, formatMoney, formatFancyLargeNumber } from "#app/utils"; import type PokemonData from "../system/pokemon-data"; import i18next from "i18next"; import { Button } from "../enums/buttons"; @@ -19,7 +19,8 @@ import { PokemonType } from "#enums/pokemon-type"; import { TypeColor, TypeShadow } from "#app/enums/color"; import { getNatureStatMultiplier, getNatureName } from "../data/nature"; import { getVariantTint } from "#app/sprites/variant"; -import * as Modifier from "../modifier/modifier"; +// biome-ignore lint/style/noNamespaceImport: See `src/system/game-data.ts` +import * as Modifier from "#app/modifier/modifier"; import type { Species } from "#enums/species"; import { PlayerGender } from "#enums/player-gender"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; @@ -411,7 +412,7 @@ export default class RunInfoUiHandler extends UiHandler { const enemyLevel = addTextObject( 36, 26, - `${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatLargeNumber(enemy.level, 1000)}`, + `${i18next.t("saveSlotSelectUiHandler:lv")}${formatLargeNumber(enemy.level, 1000)}`, enemyLevelStyle, { fontSize: "44px", color: "#f8f8f8" }, ); @@ -441,7 +442,7 @@ export default class RunInfoUiHandler extends UiHandler { const enemyLevel = addTextObject( 36, 26, - `${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatLargeNumber(enemy.level, 1000)}`, + `${i18next.t("saveSlotSelectUiHandler:lv")}${formatLargeNumber(enemy.level, 1000)}`, bossStatus ? TextStyle.PARTY_RED : TextStyle.PARTY, { fontSize: "44px", color: "#f8f8f8" }, ); @@ -527,7 +528,7 @@ export default class RunInfoUiHandler extends UiHandler { const enemyLevel = addTextObject( 43 * (e % 3), 27 * (pokemonRowHeight + 1), - `${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatLargeNumber(enemy.level, 1000)}`, + `${i18next.t("saveSlotSelectUiHandler:lv")}${formatLargeNumber(enemy.level, 1000)}`, isBoss ? TextStyle.PARTY_RED : TextStyle.PARTY, { fontSize: "54px" }, ); @@ -606,9 +607,9 @@ export default class RunInfoUiHandler extends UiHandler { fontSize: "50px", lineSpacing: lineSpacing, }); - const runTime = Utils.getPlayTimeString(this.runInfo.playTime); + const runTime = getPlayTimeString(this.runInfo.playTime); runInfoText.appendText(`${i18next.t("runHistory:runLength")}: ${runTime}`, false); - const runMoney = Utils.formatMoney(globalScene.moneyFormat, this.runInfo.money); + const runMoney = formatMoney(globalScene.moneyFormat, this.runInfo.money); const moneyTextColor = getTextColor(TextStyle.MONEY_WINDOW, false, globalScene.uiTheme); runInfoText.appendText( `[color=${moneyTextColor}]${i18next.t("battleScene:moneyOwned", { formattedMoney: runMoney })}[/color]`, @@ -770,7 +771,7 @@ export default class RunInfoUiHandler extends UiHandler { lineSpacing: lineSpacing, }); pokeInfoText.appendText( - `${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatFancyLargeNumber(pokemon.level, 1)} - ${pNatureName}`, + `${i18next.t("saveSlotSelectUiHandler:lv")}${formatFancyLargeNumber(pokemon.level, 1)} - ${pNatureName}`, ); pokeInfoText.appendText(pAbilityInfo); pokeInfoText.appendText(pPassiveInfo); @@ -780,7 +781,7 @@ export default class RunInfoUiHandler extends UiHandler { // Colored Arrows (Red/Blue) are placed by stats that are boosted from natures const pokeStatTextContainer = globalScene.add.container(-35, 6); const pStats: string[] = []; - pokemon.stats.forEach(element => pStats.push(Utils.formatFancyLargeNumber(element, 1))); + pokemon.stats.forEach(element => pStats.push(formatFancyLargeNumber(element, 1))); for (let i = 0; i < pStats.length; i++) { const isMult = getNatureStatMultiplier(pNature, i); pStats[i] = isMult < 1 ? pStats[i] + "[color=#40c8f8]↓[/color]" : pStats[i]; diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index a1e9e5219b4..0c16e41bbef 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -2,10 +2,11 @@ import i18next from "i18next"; import { globalScene } from "#app/global-scene"; import { Button } from "#enums/buttons"; import { GameMode } from "../game-mode"; -import * as Modifier from "../modifier/modifier"; +// biome-ignore lint/style/noNamespaceImport: See `src/system/game-data.ts` +import * as Modifier from "#app/modifier/modifier"; import type { SessionSaveData } from "../system/game-data"; import type PokemonData from "../system/pokemon-data"; -import * as Utils from "../utils"; +import { isNullOrUndefined, fixedInt, getPlayTimeString, formatLargeNumber } from "#app/utils"; import MessageUiHandler from "./message-ui-handler"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; @@ -296,7 +297,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { } this.setArrowVisibility(hasData); } - if (!Utils.isNullOrUndefined(prevSlotIndex)) { + if (!isNullOrUndefined(prevSlotIndex)) { this.revertSessionSlot(prevSlotIndex); } @@ -339,7 +340,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { globalScene.tweens.add({ targets: this.sessionSlotsContainer, y: this.sessionSlotsContainerInitialY - 56 * scrollCursor, - duration: Utils.fixedInt(325), + duration: fixedInt(325), ease: "Sine.easeInOut", }); } @@ -407,7 +408,7 @@ class SessionSlot extends Phaser.GameObjects.Container { const timestampLabel = addTextObject(8, 19, new Date(data.timestamp).toLocaleString(), TextStyle.WINDOW); this.add(timestampLabel); - const playTimeLabel = addTextObject(8, 33, Utils.getPlayTimeString(data.playTime), TextStyle.WINDOW); + const playTimeLabel = addTextObject(8, 33, getPlayTimeString(data.playTime), TextStyle.WINDOW); this.add(playTimeLabel); const pokemonIconsContainer = globalScene.add.container(144, 4); @@ -421,7 +422,7 @@ class SessionSlot extends Phaser.GameObjects.Container { const text = addTextObject( 32, 20, - `${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatLargeNumber(pokemon.level, 1000)}`, + `${i18next.t("saveSlotSelectUiHandler:lv")}${formatLargeNumber(pokemon.level, 1000)}`, TextStyle.PARTY, { fontSize: "54px", color: "#f8f8f8" }, ); diff --git a/src/ui/saving-icon-handler.ts b/src/ui/saving-icon-handler.ts index 4404ea423b1..3db84f128a1 100644 --- a/src/ui/saving-icon-handler.ts +++ b/src/ui/saving-icon-handler.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import * as Utils from "../utils"; +import { fixedInt } from "#app/utils"; export default class SavingIconHandler extends Phaser.GameObjects.Container { private icon: Phaser.GameObjects.Sprite; @@ -36,10 +36,10 @@ export default class SavingIconHandler extends Phaser.GameObjects.Container { globalScene.tweens.add({ targets: this, alpha: 1, - duration: Utils.fixedInt(250), + duration: fixedInt(250), ease: "Sine.easeInOut", onComplete: () => { - globalScene.time.delayedCall(Utils.fixedInt(500), () => { + globalScene.time.delayedCall(fixedInt(500), () => { this.animActive = false; if (!this.shown) { this.hide(); @@ -64,7 +64,7 @@ export default class SavingIconHandler extends Phaser.GameObjects.Container { globalScene.tweens.add({ targets: this, alpha: 0, - duration: Utils.fixedInt(250), + duration: fixedInt(250), ease: "Sine.easeInOut", onComplete: () => { this.animActive = false; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 3876f2585db..3e2940f45b9 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -43,7 +43,7 @@ import { Egg } from "#app/data/egg"; import Overrides from "#app/overrides"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; import { Passive as PassiveAttr } from "#enums/passive"; -import * as Challenge from "#app/data/challenge"; +import { applyChallenges, ChallengeType } from "#app/data/challenge"; import MoveInfoOverlay from "#app/ui/move-info-overlay"; import { getEggTierForSpecies } from "#app/data/egg"; import { Device } from "#enums/devices"; @@ -78,7 +78,6 @@ import { import type { Nature } from "#enums/nature"; import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; import { achvs } from "#app/system/achv"; -import * as Utils from "../utils"; import type { GameObjects } from "phaser"; import { checkStarterValidForChallenge } from "#app/data/challenge"; @@ -2518,7 +2517,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { case Button.CYCLE_TERA: if (this.canCycleTera) { const speciesForm = getPokemonSpeciesForm(this.lastSpecies.speciesId, starterAttributes.form ?? 0); - if (speciesForm.type1 === this.teraCursor && !Utils.isNullOrUndefined(speciesForm.type2)) { + if (speciesForm.type1 === this.teraCursor && !isNullOrUndefined(speciesForm.type2)) { starterAttributes.tera = speciesForm.type2!; this.setSpeciesDetails(this.lastSpecies, { teraType: speciesForm.type2!, @@ -2960,7 +2959,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { valueLimit.value = 10; } - Challenge.applyChallenges(Challenge.ChallengeType.STARTER_POINTS, valueLimit); + applyChallenges(ChallengeType.STARTER_POINTS, valueLimit); return valueLimit.value; } @@ -3748,7 +3747,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { ); // TODO: is this bang correct? this.abilityCursor = abilityIndex !== undefined ? abilityIndex : (abilityIndex = oldAbilityIndex); this.natureCursor = natureIndex !== undefined ? natureIndex : (natureIndex = oldNatureIndex); - this.teraCursor = !Utils.isNullOrUndefined(teraType) ? teraType : (teraType = species.type1); + this.teraCursor = !isNullOrUndefined(teraType) ? teraType : (teraType = species.type1); const [isInParty, partyIndex]: [boolean, number] = this.isInParty(species); // we use this to firstly check if the pokemon is in the party, and if so, to get the party index in order to update the icon image if (isInParty) { this.updatePartyIcon(species, partyIndex); @@ -3886,7 +3885,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.canCycleTera = !this.statsMode && globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id) && - !Utils.isNullOrUndefined(getPokemonSpeciesForm(species.speciesId, formIndex ?? 0).type2); + !isNullOrUndefined(getPokemonSpeciesForm(species.speciesId, formIndex ?? 0).type2); } if (dexEntry.caughtAttr && species.malePercent !== null) { @@ -4483,7 +4482,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.canCycleTera = !this.statsMode && globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id) && - !Utils.isNullOrUndefined(getPokemonSpeciesForm(this.lastSpecies.speciesId, formIndex ?? 0).type2); + !isNullOrUndefined(getPokemonSpeciesForm(this.lastSpecies.speciesId, formIndex ?? 0).type2); this.updateInstructions(); } } diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index aa3d014bd95..1e0924aa2c5 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -2,7 +2,16 @@ import { starterColors } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; import { Mode } from "#app/ui/ui"; import UiHandler from "#app/ui/ui-handler"; -import * as Utils from "#app/utils"; +import { + getLocalizedSpriteKey, + rgbHexToRgba, + padInt, + getEnumValues, + fixedInt, + isNullOrUndefined, + toReadableString, + formatStat, +} from "#app/utils"; import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters"; import { argbFromRgba } from "@material/material-color-utilities"; @@ -255,7 +264,7 @@ export default class SummaryUiHandler extends UiHandler { this.statusContainer.add(statusLabel); - this.status = globalScene.add.sprite(91, 4, Utils.getLocalizedSpriteKey("statuses")); + this.status = globalScene.add.sprite(91, 4, getLocalizedSpriteKey("statuses")); this.status.setOrigin(0.5, 0); this.statusContainer.add(this.status); @@ -330,10 +339,10 @@ export default class SummaryUiHandler extends UiHandler { this.shinyOverlay.setVisible(this.pokemon.isShiny()); const colorScheme = starterColors[this.pokemon.species.getRootSpeciesId()]; - this.candyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0]))); - this.candyOverlay.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1]))); + this.candyIcon.setTint(argbFromRgba(rgbHexToRgba(colorScheme[0]))); + this.candyOverlay.setTint(argbFromRgba(rgbHexToRgba(colorScheme[1]))); - this.numberText.setText(Utils.padInt(this.pokemon.species.speciesId, 4)); + this.numberText.setText(padInt(this.pokemon.species.speciesId, 4)); this.numberText.setColor(this.getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD)); this.numberText.setShadowColor( this.getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true), @@ -600,7 +609,7 @@ export default class SummaryUiHandler extends UiHandler { } success = true; } else { - const pages = Utils.getEnumValues(Page); + const pages = getEnumValues(Page); switch (button) { case Button.UP: case Button.DOWN: { @@ -675,10 +684,10 @@ export default class SummaryUiHandler extends UiHandler { if (moveDescriptionLineCount > 3) { this.descriptionScrollTween = globalScene.tweens.add({ targets: this.moveDescriptionText, - delay: Utils.fixedInt(2000), + delay: fixedInt(2000), loop: -1, - hold: Utils.fixedInt(2000), - duration: Utils.fixedInt((moveDescriptionLineCount - 3) * 2000), + hold: fixedInt(2000), + duration: fixedInt((moveDescriptionLineCount - 3) * 2000), y: `-=${14.83 * (moveDescriptionLineCount - 3)}`, }); } @@ -697,10 +706,10 @@ export default class SummaryUiHandler extends UiHandler { this.moveCursorObj.setVisible(true); this.moveCursorBlinkTimer = globalScene.time.addEvent({ loop: true, - delay: Utils.fixedInt(600), + delay: fixedInt(600), callback: () => { this.moveCursorObj?.setVisible(false); - globalScene.time.delayedCall(Utils.fixedInt(100), () => { + globalScene.time.delayedCall(fixedInt(100), () => { if (!this.moveCursorObj) { return; } @@ -818,7 +827,7 @@ export default class SummaryUiHandler extends UiHandler { const getTypeIcon = (index: number, type: PokemonType, tera = false) => { const xCoord = typeLabel.width * typeLabel.scale + 9 + 34 * index; const typeIcon = !tera - ? globalScene.add.sprite(xCoord, 42, Utils.getLocalizedSpriteKey("types"), PokemonType[type].toLowerCase()) + ? globalScene.add.sprite(xCoord, 42, getLocalizedSpriteKey("types"), PokemonType[type].toLowerCase()) : globalScene.add.sprite(xCoord, 42, "type_tera"); if (tera) { typeIcon.setScale(0.5); @@ -853,7 +862,7 @@ export default class SummaryUiHandler extends UiHandler { if ( globalScene.gameData.achvUnlocks.hasOwnProperty(achvs.TERASTALLIZE.id) && - !Utils.isNullOrUndefined(this.pokemon) + !isNullOrUndefined(this.pokemon) ) { const teraIcon = globalScene.add.sprite(123, 26, "button_tera"); teraIcon.setName("terrastallize-icon"); @@ -925,10 +934,10 @@ export default class SummaryUiHandler extends UiHandler { abilityInfo.descriptionText.setY(69); this.descriptionScrollTween = globalScene.tweens.add({ targets: abilityInfo.descriptionText, - delay: Utils.fixedInt(2000), + delay: fixedInt(2000), loop: -1, - hold: Utils.fixedInt(2000), - duration: Utils.fixedInt((abilityDescriptionLineCount - 2) * 2000), + hold: fixedInt(2000), + duration: fixedInt((abilityDescriptionLineCount - 2) * 2000), y: `-=${14.83 * (abilityDescriptionLineCount - 2)}`, }); } @@ -939,8 +948,8 @@ export default class SummaryUiHandler extends UiHandler { this.passiveContainer?.descriptionText?.setVisible(false); const closeFragment = getBBCodeFrag("", TextStyle.WINDOW_ALT); - const rawNature = Utils.toReadableString(Nature[this.pokemon?.getNature()!]); // TODO: is this bang correct? - const nature = `${getBBCodeFrag(Utils.toReadableString(getNatureName(this.pokemon?.getNature()!)), TextStyle.SUMMARY_RED)}${closeFragment}`; // TODO: is this bang correct? + const rawNature = toReadableString(Nature[this.pokemon?.getNature()!]); // TODO: is this bang correct? + const nature = `${getBBCodeFrag(toReadableString(getNatureName(this.pokemon?.getNature()!)), TextStyle.SUMMARY_RED)}${closeFragment}`; // TODO: is this bang correct? const memoString = i18next.t("pokemonSummary:memoString", { metFragment: i18next.t( @@ -999,8 +1008,8 @@ export default class SummaryUiHandler extends UiHandler { const statValueText = stat !== Stat.HP - ? Utils.formatStat(this.pokemon?.getStat(stat)!) // TODO: is this bang correct? - : `${Utils.formatStat(this.pokemon?.hp!, true)}/${Utils.formatStat(this.pokemon?.getMaxHp()!, true)}`; // TODO: are those bangs correct? + ? formatStat(this.pokemon?.getStat(stat)!) // TODO: is this bang correct? + : `${formatStat(this.pokemon?.hp!, true)}/${formatStat(this.pokemon?.getMaxHp()!, true)}`; // TODO: are those bangs correct? const ivText = `${this.pokemon?.ivs[stat]}/31`; const statValue = addTextObject(93 + 88 * colIndex, 16 * rowIndex, statValueText, TextStyle.WINDOW_ALT); @@ -1106,7 +1115,7 @@ export default class SummaryUiHandler extends UiHandler { this.extraMoveRowContainer.setVisible(true); if (this.newMove && this.pokemon) { - const spriteKey = Utils.getLocalizedSpriteKey("types"); + const spriteKey = getLocalizedSpriteKey("types"); const moveType = this.pokemon.getMoveType(this.newMove); const newMoveTypeIcon = globalScene.add.sprite(0, 0, spriteKey, PokemonType[moveType].toLowerCase()); newMoveTypeIcon.setOrigin(0, 1); @@ -1116,7 +1125,7 @@ export default class SummaryUiHandler extends UiHandler { ppOverlay.setOrigin(0, 1); this.extraMoveRowContainer.add(ppOverlay); - const pp = Utils.padInt(this.newMove?.pp!, 2, " "); // TODO: is this bang correct? + const pp = padInt(this.newMove?.pp!, 2, " "); // TODO: is this bang correct? const ppText = addTextObject(173, 1, `${pp}/${pp}`, TextStyle.WINDOW); ppText.setOrigin(0, 1); this.extraMoveRowContainer.add(ppText); @@ -1132,7 +1141,7 @@ export default class SummaryUiHandler extends UiHandler { this.moveRowsContainer.add(moveRowContainer); if (move && this.pokemon) { - const spriteKey = Utils.getLocalizedSpriteKey("types"); + const spriteKey = getLocalizedSpriteKey("types"); const moveType = this.pokemon.getMoveType(move.getMove()); const typeIcon = globalScene.add.sprite(0, 0, spriteKey, PokemonType[moveType].toLowerCase()); typeIcon.setOrigin(0, 1); @@ -1153,7 +1162,7 @@ export default class SummaryUiHandler extends UiHandler { if (move) { const maxPP = move.getMovePp(); const pp = maxPP - move.ppUsed; - ppText.setText(`${Utils.padInt(pp, 2, " ")}/${Utils.padInt(maxPP, 2, " ")}`); + ppText.setText(`${padInt(pp, 2, " ")}/${padInt(maxPP, 2, " ")}`); } moveRowContainer.add(ppText); diff --git a/src/ui/target-select-ui-handler.ts b/src/ui/target-select-ui-handler.ts index d2f72ef4a4c..a9f88b337f3 100644 --- a/src/ui/target-select-ui-handler.ts +++ b/src/ui/target-select-ui-handler.ts @@ -1,7 +1,7 @@ import { BattlerIndex } from "../battle"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; -import * as Utils from "../utils"; +import { isNullOrUndefined, fixedInt } from "#app/utils"; import { getMoveTargets } from "../data/moves/move"; import { Button } from "#enums/buttons"; import type { Moves } from "#enums/moves"; @@ -70,7 +70,7 @@ export default class TargetSelectUiHandler extends UiHandler { * @param user the Pokemon using the move */ resetCursor(cursorN: number, user: Pokemon): void { - if (!Utils.isNullOrUndefined(cursorN)) { + if (!isNullOrUndefined(cursorN)) { if ([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2].includes(cursorN) || user.battleSummonData.waveTurnCount === 1) { // Reset cursor on the first turn of a fight or if an ally was targeted last turn cursorN = -1; @@ -89,11 +89,11 @@ export default class TargetSelectUiHandler extends UiHandler { this.targetSelectCallback(button === Button.ACTION ? targetIndexes : []); success = true; if (this.fieldIndex === BattlerIndex.PLAYER) { - if (Utils.isNullOrUndefined(this.cursor0) || this.cursor0 !== this.cursor) { + if (isNullOrUndefined(this.cursor0) || this.cursor0 !== this.cursor) { this.cursor0 = this.cursor; } } else if (this.fieldIndex === BattlerIndex.PLAYER_2) { - if (Utils.isNullOrUndefined(this.cursor1) || this.cursor1 !== this.cursor) { + if (isNullOrUndefined(this.cursor1) || this.cursor1 !== this.cursor) { this.cursor1 = this.cursor; } } @@ -152,7 +152,7 @@ export default class TargetSelectUiHandler extends UiHandler { key: { start: 1, to: 0.25 }, loop: -1, loopDelay: 150, - duration: Utils.fixedInt(450), + duration: fixedInt(450), ease: "Sine.easeInOut", yoyo: true, onUpdate: t => { @@ -178,7 +178,7 @@ export default class TargetSelectUiHandler extends UiHandler { targets: [info], y: { start: info.getBaseY(), to: info.getBaseY() + 1 }, loop: -1, - duration: Utils.fixedInt(250), + duration: fixedInt(250), ease: "Linear", yoyo: true, }), diff --git a/src/ui/time-of-day-widget.ts b/src/ui/time-of-day-widget.ts index bda1f750cb1..5e42e6215f8 100644 --- a/src/ui/time-of-day-widget.ts +++ b/src/ui/time-of-day-widget.ts @@ -1,4 +1,4 @@ -import * as Utils from "../utils"; +import { fixedInt } from "#app/utils"; import { globalScene } from "#app/global-scene"; import { BattleSceneEventType } from "../events/battle-scene"; import { EaseType } from "#enums/ease-type"; @@ -75,14 +75,14 @@ export default class TimeOfDayWidget extends Phaser.GameObjects.Container { const rotate = { targets: [this.timeOfDayIconMgs[0], this.timeOfDayIconMgs[1]], angle: "+=90", - duration: Utils.fixedInt(1500), + duration: fixedInt(1500), ease: "Back.easeOut", paused: !this.parentVisible, }; const fade = { targets: [this.timeOfDayIconBgs[1], this.timeOfDayIconMgs[1], this.timeOfDayIconFgs[1]], alpha: 0, - duration: Utils.fixedInt(500), + duration: fixedInt(500), ease: "Linear", paused: !this.parentVisible, }; @@ -98,14 +98,14 @@ export default class TimeOfDayWidget extends Phaser.GameObjects.Container { const bounce = { targets: [this.timeOfDayIconMgs[0], this.timeOfDayIconMgs[1]], angle: "+=90", - duration: Utils.fixedInt(2000), + duration: fixedInt(2000), ease: "Bounce.easeOut", paused: !this.parentVisible, }; const fade = { targets: [this.timeOfDayIconBgs[1], this.timeOfDayIconMgs[1], this.timeOfDayIconFgs[1]], alpha: 0, - duration: Utils.fixedInt(800), + duration: fixedInt(800), ease: "Linear", paused: !this.parentVisible, }; diff --git a/src/ui/title-ui-handler.ts b/src/ui/title-ui-handler.ts index d87d4e5ca79..405e3cc4a27 100644 --- a/src/ui/title-ui-handler.ts +++ b/src/ui/title-ui-handler.ts @@ -1,6 +1,6 @@ import OptionSelectUiHandler from "./settings/option-select-ui-handler"; import { Mode } from "./ui"; -import * as Utils from "../utils"; +import { fixedInt, randInt, randItem } from "#app/utils"; import { TextStyle, addTextObject } from "./text"; import { getSplashMessages } from "../data/splash-messages"; import i18next from "i18next"; @@ -72,7 +72,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { globalScene.tweens.add({ targets: this.splashMessageText, - duration: Utils.fixedInt(350), + duration: fixedInt(350), scale: originalSplashMessageScale * 1.25, loop: -1, yoyo: true, @@ -104,7 +104,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { /** Used solely to display a random Pokémon name in a splash message. */ randomPokemon(): void { - const rand = Utils.randInt(1025, 1); + const rand = randInt(1025, 1); const pokemon = getPokemonSpecies(rand as Species); if ( this.splashMessage === "splashMessages:underratedPokemon" || @@ -132,7 +132,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { // Moving player count to top of the menu this.playerCountLabel.setY(globalScene.game.canvas.height / 6 - 13 - this.getWindowHeight()); - this.splashMessage = Utils.randItem(getSplashMessages()); + this.splashMessage = randItem(getSplashMessages()); this.splashMessageText.setText( i18next.t(this.splashMessage, { count: TitleUiHandler.BATTLES_WON_FALLBACK, @@ -159,7 +159,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { globalScene.tweens.add({ targets: [this.titleContainer, ui.getMessageHandler().bg], - duration: Utils.fixedInt(325), + duration: fixedInt(325), alpha: (target: any) => (target === this.titleContainer ? 1 : 0), ease: "Sine.easeInOut", }); @@ -180,7 +180,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { globalScene.tweens.add({ targets: [this.titleContainer, ui.getMessageHandler().bg], - duration: Utils.fixedInt(325), + duration: fixedInt(325), alpha: (target: any) => (target === this.titleContainer ? 0 : 1), ease: "Sine.easeInOut", }); diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 6605e5ef730..c7981cd5fba 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -28,7 +28,7 @@ import { addWindow } from "./ui-theme"; import LoginFormUiHandler from "./login-form-ui-handler"; import RegistrationFormUiHandler from "./registration-form-ui-handler"; import LoadingModalUiHandler from "./loading-modal-ui-handler"; -import * as Utils from "../utils"; +import { executeIf } from "#app/utils"; import GameStatsUiHandler from "./game-stats-ui-handler"; import AwaitableUiHandler from "./awaitable-ui-handler"; import SaveSlotSelectUiHandler from "./save-slot-select-ui-handler"; @@ -674,7 +674,7 @@ export default class UI extends Phaser.GameObjects.Container { if (!this?.modeChain?.length) { return resolve(); } - this.revertMode().then(success => Utils.executeIf(success, this.revertModes).then(() => resolve())); + this.revertMode().then(success => executeIf(success, this.revertModes).then(() => resolve())); }); } diff --git a/src/ui/unavailable-modal-ui-handler.ts b/src/ui/unavailable-modal-ui-handler.ts index 3007f7247f1..01ed850f6d0 100644 --- a/src/ui/unavailable-modal-ui-handler.ts +++ b/src/ui/unavailable-modal-ui-handler.ts @@ -3,7 +3,7 @@ import { ModalUiHandler } from "./modal-ui-handler"; import { addTextObject, TextStyle } from "./text"; import type { Mode } from "./ui"; import { updateUserInfo } from "#app/account"; -import * as Utils from "#app/utils"; +import { removeCookie, sessionIdKey } from "#app/utils"; import i18next from "i18next"; import { globalScene } from "#app/global-scene"; @@ -65,7 +65,7 @@ export default class UnavailableModalUiHandler extends ModalUiHandler { globalScene.playSound("se/pb_bounce_1"); this.reconnectCallback(); } else if (response[1] === 401) { - Utils.removeCookie(Utils.sessionIdKey); + removeCookie(sessionIdKey); globalScene.reset(true, true); } else { this.reconnectDuration = Math.min(this.reconnectDuration * 2, this.maxTime); // Set a max delay so it isn't infinite diff --git a/test/escape-calculations.test.ts b/test/escape-calculations.test.ts index 0cbf11dd230..b4504c7359c 100644 --- a/test/escape-calculations.test.ts +++ b/test/escape-calculations.test.ts @@ -1,7 +1,7 @@ import { AttemptRunPhase } from "#app/phases/attempt-run-phase"; import type { CommandPhase } from "#app/phases/command-phase"; import { Command } from "#app/ui/command-ui-handler"; -import * as Utils from "#app/utils"; +import { NumberHolder } from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; @@ -45,7 +45,7 @@ describe("Escape chance calculations", () => { await game.phaseInterceptor.to(AttemptRunPhase, false); const phase = game.scene.getCurrentPhase() as AttemptRunPhase; - const escapePercentage = new Utils.NumberHolder(0); + 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 const escapeChances: { @@ -118,7 +118,7 @@ describe("Escape chance calculations", () => { await game.phaseInterceptor.to(AttemptRunPhase, false); const phase = game.scene.getCurrentPhase() as AttemptRunPhase; - const escapePercentage = new Utils.NumberHolder(0); + 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 const escapeChances: { @@ -197,7 +197,7 @@ describe("Escape chance calculations", () => { await game.phaseInterceptor.to(AttemptRunPhase, false); const phase = game.scene.getCurrentPhase() as AttemptRunPhase; - const escapePercentage = new Utils.NumberHolder(0); + 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 const escapeChances: { @@ -284,7 +284,7 @@ describe("Escape chance calculations", () => { await game.phaseInterceptor.to(AttemptRunPhase, false); const phase = game.scene.getCurrentPhase() as AttemptRunPhase; - const escapePercentage = new Utils.NumberHolder(0); + 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 const escapeChances: { diff --git a/test/items/exp_booster.test.ts b/test/items/exp_booster.test.ts index e4491b22637..2b1308f1afb 100644 --- a/test/items/exp_booster.test.ts +++ b/test/items/exp_booster.test.ts @@ -1,6 +1,6 @@ import { Abilities } from "#app/enums/abilities"; import { PokemonExpBoosterModifier } from "#app/modifier/modifier"; -import * as Utils from "#app/utils"; +import { NumberHolder } from "#app/utils"; import GameManager from "#test/testUtils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -33,7 +33,7 @@ describe("EXP Modifier Items", () => { const partyMember = game.scene.getPlayerPokemon()!; partyMember.exp = 100; - const expHolder = new Utils.NumberHolder(partyMember.exp); + const expHolder = new NumberHolder(partyMember.exp); game.scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, expHolder); expect(expHolder.value).toBe(440); }, 20000); diff --git a/test/items/leek.test.ts b/test/items/leek.test.ts index ec4d075fe19..afb31a5f9fa 100644 --- a/test/items/leek.test.ts +++ b/test/items/leek.test.ts @@ -1,5 +1,5 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import * as Utils from "#app/utils"; +import { randInt } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; @@ -78,7 +78,7 @@ describe("Items - Leek", () => { // Randomly choose from the Farfetch'd line const species = [Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD]; - await game.startBattle([species[Utils.randInt(species.length)], Species.PIKACHU]); + await game.startBattle([species[randInt(species.length)], Species.PIKACHU]); const [partyMember, ally] = game.scene.getPlayerParty(); @@ -106,7 +106,7 @@ describe("Items - Leek", () => { // Randomly choose from the Farfetch'd line const species = [Species.FARFETCHD, Species.GALAR_FARFETCHD, Species.SIRFETCHD]; - await game.startBattle([Species.PIKACHU, species[Utils.randInt(species.length)]]); + await game.startBattle([Species.PIKACHU, species[randInt(species.length)]]); const [partyMember, ally] = game.scene.getPlayerParty(); diff --git a/test/items/light_ball.test.ts b/test/items/light_ball.test.ts index e4959002904..1f5227142eb 100644 --- a/test/items/light_ball.test.ts +++ b/test/items/light_ball.test.ts @@ -2,7 +2,7 @@ import { Stat } from "#enums/stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import * as Utils from "#app/utils"; +import { NumberHolder } from "#app/utils"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phase from "phaser"; @@ -90,9 +90,9 @@ describe("Items - Light Ball", () => { const spAtkStat = partyMember.getStat(Stat.SPATK); // Making sure modifier is not applied without holding item - const atkValue = new Utils.NumberHolder(atkStat); + const atkValue = new NumberHolder(atkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, atkValue); - const spAtkValue = new Utils.NumberHolder(spAtkStat); + const spAtkValue = new NumberHolder(spAtkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPDEF, spAtkValue); expect(atkValue.value / atkStat).toBe(1); @@ -129,9 +129,9 @@ describe("Items - Light Ball", () => { const spAtkStat = partyMember.getStat(Stat.SPATK); // Making sure modifier is not applied without holding item - const atkValue = new Utils.NumberHolder(atkStat); + const atkValue = new NumberHolder(atkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, atkValue); - const spAtkValue = new Utils.NumberHolder(spAtkStat); + const spAtkValue = new NumberHolder(spAtkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPDEF, spAtkValue); expect(atkValue.value / atkStat).toBe(1); @@ -168,9 +168,9 @@ describe("Items - Light Ball", () => { const spAtkStat = partyMember.getStat(Stat.SPATK); // Making sure modifier is not applied without holding item - const atkValue = new Utils.NumberHolder(atkStat); + const atkValue = new NumberHolder(atkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, atkValue); - const spAtkValue = new Utils.NumberHolder(spAtkStat); + const spAtkValue = new NumberHolder(spAtkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPDEF, spAtkValue); expect(atkValue.value / atkStat).toBe(1); @@ -197,9 +197,9 @@ describe("Items - Light Ball", () => { const spAtkStat = partyMember.getStat(Stat.SPATK); // Making sure modifier is not applied without holding item - const atkValue = new Utils.NumberHolder(atkStat); + const atkValue = new NumberHolder(atkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, atkValue); - const spAtkValue = new Utils.NumberHolder(spAtkStat); + const spAtkValue = new NumberHolder(spAtkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPDEF, spAtkValue); expect(atkValue.value / atkStat).toBe(1); diff --git a/test/items/metal_powder.test.ts b/test/items/metal_powder.test.ts index 460a95d0f06..ed96d3c498b 100644 --- a/test/items/metal_powder.test.ts +++ b/test/items/metal_powder.test.ts @@ -2,7 +2,7 @@ import { Stat } from "#enums/stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import * as Utils from "#app/utils"; +import { NumberHolder } from "#app/utils"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phase from "phaser"; @@ -89,7 +89,7 @@ describe("Items - Metal Powder", () => { const defStat = partyMember.getStat(Stat.DEF); // Making sure modifier is not applied without holding item - const defValue = new Utils.NumberHolder(defStat); + const defValue = new NumberHolder(defStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); expect(defValue.value / defStat).toBe(1); @@ -122,7 +122,7 @@ describe("Items - Metal Powder", () => { const defStat = partyMember.getStat(Stat.DEF); // Making sure modifier is not applied without holding item - const defValue = new Utils.NumberHolder(defStat); + const defValue = new NumberHolder(defStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); expect(defValue.value / defStat).toBe(1); @@ -155,7 +155,7 @@ describe("Items - Metal Powder", () => { const defStat = partyMember.getStat(Stat.DEF); // Making sure modifier is not applied without holding item - const defValue = new Utils.NumberHolder(defStat); + const defValue = new NumberHolder(defStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); expect(defValue.value / defStat).toBe(1); @@ -178,7 +178,7 @@ describe("Items - Metal Powder", () => { const defStat = partyMember.getStat(Stat.DEF); // Making sure modifier is not applied without holding item - const defValue = new Utils.NumberHolder(defStat); + const defValue = new NumberHolder(defStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); expect(defValue.value / defStat).toBe(1); diff --git a/test/items/quick_powder.test.ts b/test/items/quick_powder.test.ts index 26faf5a0f4f..7115cad8cd1 100644 --- a/test/items/quick_powder.test.ts +++ b/test/items/quick_powder.test.ts @@ -2,7 +2,7 @@ import { Stat } from "#enums/stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import * as Utils from "#app/utils"; +import { NumberHolder } from "#app/utils"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phase from "phaser"; @@ -89,7 +89,7 @@ describe("Items - Quick Powder", () => { const spdStat = partyMember.getStat(Stat.SPD); // Making sure modifier is not applied without holding item - const spdValue = new Utils.NumberHolder(spdStat); + const spdValue = new NumberHolder(spdStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); expect(spdValue.value / spdStat).toBe(1); @@ -122,7 +122,7 @@ describe("Items - Quick Powder", () => { const spdStat = partyMember.getStat(Stat.SPD); // Making sure modifier is not applied without holding item - const spdValue = new Utils.NumberHolder(spdStat); + const spdValue = new NumberHolder(spdStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); expect(spdValue.value / spdStat).toBe(1); @@ -155,7 +155,7 @@ describe("Items - Quick Powder", () => { const spdStat = partyMember.getStat(Stat.SPD); // Making sure modifier is not applied without holding item - const spdValue = new Utils.NumberHolder(spdStat); + const spdValue = new NumberHolder(spdStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); expect(spdValue.value / spdStat).toBe(1); @@ -178,7 +178,7 @@ describe("Items - Quick Powder", () => { const spdStat = partyMember.getStat(Stat.SPD); // Making sure modifier is not applied without holding item - const spdValue = new Utils.NumberHolder(spdStat); + const spdValue = new NumberHolder(spdStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); expect(spdValue.value / spdStat).toBe(1); diff --git a/test/items/thick_club.test.ts b/test/items/thick_club.test.ts index 9edbbcdc7d9..69ca316d455 100644 --- a/test/items/thick_club.test.ts +++ b/test/items/thick_club.test.ts @@ -2,7 +2,7 @@ import { Stat } from "#enums/stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import * as Utils from "#app/utils"; +import { NumberHolder, randInt } from "#app/utils"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phase from "phaser"; @@ -89,7 +89,7 @@ describe("Items - Thick Club", () => { const atkStat = partyMember.getStat(Stat.ATK); // Making sure modifier is not applied without holding item - const atkValue = new Utils.NumberHolder(atkStat); + const atkValue = new NumberHolder(atkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(1); @@ -112,7 +112,7 @@ describe("Items - Thick Club", () => { const atkStat = partyMember.getStat(Stat.ATK); // Making sure modifier is not applied without holding item - const atkValue = new Utils.NumberHolder(atkStat); + const atkValue = new NumberHolder(atkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(1); @@ -135,7 +135,7 @@ describe("Items - Thick Club", () => { const atkStat = partyMember.getStat(Stat.ATK); // Making sure modifier is not applied without holding item - const atkValue = new Utils.NumberHolder(atkStat); + const atkValue = new NumberHolder(atkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(1); @@ -153,7 +153,7 @@ describe("Items - Thick Club", () => { it("THICK_CLUB held by fused CUBONE line (base)", async () => { // Randomly choose from the Cubone line const species = [Species.CUBONE, Species.MAROWAK, Species.ALOLA_MAROWAK]; - const randSpecies = Utils.randInt(species.length); + const randSpecies = randInt(species.length); await game.classicMode.startBattle([species[randSpecies], Species.PIKACHU]); @@ -172,7 +172,7 @@ describe("Items - Thick Club", () => { const atkStat = partyMember.getStat(Stat.ATK); // Making sure modifier is not applied without holding item - const atkValue = new Utils.NumberHolder(atkStat); + const atkValue = new NumberHolder(atkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(1); @@ -190,7 +190,7 @@ describe("Items - Thick Club", () => { it("THICK_CLUB held by fused CUBONE line (part)", async () => { // Randomly choose from the Cubone line const species = [Species.CUBONE, Species.MAROWAK, Species.ALOLA_MAROWAK]; - const randSpecies = Utils.randInt(species.length); + const randSpecies = randInt(species.length); await game.classicMode.startBattle([Species.PIKACHU, species[randSpecies]]); @@ -209,7 +209,7 @@ describe("Items - Thick Club", () => { const atkStat = partyMember.getStat(Stat.ATK); // Making sure modifier is not applied without holding item - const atkValue = new Utils.NumberHolder(atkStat); + const atkValue = new NumberHolder(atkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(1); @@ -232,7 +232,7 @@ describe("Items - Thick Club", () => { const atkStat = partyMember.getStat(Stat.ATK); // Making sure modifier is not applied without holding item - const atkValue = new Utils.NumberHolder(atkStat); + const atkValue = new NumberHolder(atkStat); game.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(1); diff --git a/test/moves/multi_target.test.ts b/test/moves/multi_target.test.ts index 2b17929a5df..5d33c7860cb 100644 --- a/test/moves/multi_target.test.ts +++ b/test/moves/multi_target.test.ts @@ -1,7 +1,7 @@ import { BattlerIndex } from "#app/battle"; import { Abilities } from "#app/enums/abilities"; import { Species } from "#app/enums/species"; -import * as Utils from "#app/utils"; +import { toDmgValue } from "#app/utils"; import { Moves } from "#enums/moves"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; @@ -71,8 +71,8 @@ describe("Multi-target damage reduction", () => { // Single target moves don't get reduced expect(tackle1).toBe(tackle2); // Moves that target all enemies get reduced if there's more than one enemy - expect(gleam1).toBeLessThanOrEqual(Utils.toDmgValue(gleam2 * 0.75) + 1); - expect(gleam1).toBeGreaterThanOrEqual(Utils.toDmgValue(gleam2 * 0.75) - 1); + expect(gleam1).toBeLessThanOrEqual(toDmgValue(gleam2 * 0.75) + 1); + expect(gleam1).toBeGreaterThanOrEqual(toDmgValue(gleam2 * 0.75) - 1); }); it("should reduce earthquake when more than one pokemon other than user is not fainted", async () => { @@ -122,7 +122,7 @@ describe("Multi-target damage reduction", () => { const damageEnemy1Turn3 = enemy1.getMaxHp() - enemy1.hp; // Turn 3: 1 target, should be no damage reduction - expect(damageEnemy1Turn1).toBeLessThanOrEqual(Utils.toDmgValue(damageEnemy1Turn3 * 0.75) + 1); - expect(damageEnemy1Turn1).toBeGreaterThanOrEqual(Utils.toDmgValue(damageEnemy1Turn3 * 0.75) - 1); + expect(damageEnemy1Turn1).toBeLessThanOrEqual(toDmgValue(damageEnemy1Turn3 * 0.75) + 1); + expect(damageEnemy1Turn1).toBeGreaterThanOrEqual(toDmgValue(damageEnemy1Turn3 * 0.75) - 1); }); }); diff --git a/test/mystery-encounter/encounter-test-utils.ts b/test/mystery-encounter/encounter-test-utils.ts index 8c54e0dd606..93629778e0a 100644 --- a/test/mystery-encounter/encounter-test-utils.ts +++ b/test/mystery-encounter/encounter-test-utils.ts @@ -1,3 +1,4 @@ +// biome-ignore lint/style/noNamespaceImport: Necessary for mocks import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { Status } from "#app/data/status-effect"; import { CommandPhase } from "#app/phases/command-phase"; diff --git a/test/testUtils/gameWrapper.ts b/test/testUtils/gameWrapper.ts index 388861e01c4..02865701ed0 100644 --- a/test/testUtils/gameWrapper.ts +++ b/test/testUtils/gameWrapper.ts @@ -2,7 +2,7 @@ import BattleScene, * as battleScene from "#app/battle-scene"; import { MoveAnim } from "#app/data/battle-anims"; import Pokemon from "#app/field/pokemon"; -import * as Utils from "#app/utils"; +import { setCookie, sessionIdKey } from "#app/utils"; import { blobToString } from "#test/testUtils/gameManagerUtils"; import { MockClock } from "#test/testUtils/mocks/mockClock"; import { MockFetch } from "#test/testUtils/mocks/mockFetch"; @@ -29,7 +29,7 @@ window.URL.createObjectURL = (blob: Blob) => { }; navigator.getGamepads = () => []; global.fetch = vi.fn(MockFetch); -Utils.setCookie(Utils.sessionIdKey, "fake_token"); +setCookie(sessionIdKey, "fake_token"); window.matchMedia = () => ({ matches: false, From f9ff4abfb0396da1ed74882343e22081c5924599 Mon Sep 17 00:00:00 2001 From: Jimmybald1 <122436263+Jimmybald1@users.noreply.github.com> Date: Sat, 12 Apr 2025 16:56:04 +0200 Subject: [PATCH 16/52] [Bug] Fixed biome map options counting rng twice (#5648) Fixed biome map options counting rng twice Co-authored-by: Jimmybald1 <147992650+IBBCalc@users.noreply.github.com> Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> --- src/phases/select-biome-phase.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index de705728c50..b27e2d0e7cc 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -38,15 +38,7 @@ export class SelectBiomePhase extends BattlePhase { .map(b => (!Array.isArray(b) ? b : b[0])); if (biomes.length > 1 && globalScene.findModifier(m => m instanceof MapModifier)) { - const biomeChoices: Biome[] = ( - !Array.isArray(biomeLinks[currentBiome]) - ? [biomeLinks[currentBiome] as Biome] - : (biomeLinks[currentBiome] as (Biome | [Biome, number])[]) - ) - .filter(b => !Array.isArray(b) || !randSeedInt(b[1])) - .map(b => (Array.isArray(b) ? b[0] : b)); - - const biomeSelectItems = biomeChoices.map(b => { + const biomeSelectItems = biomes.map(b => { const ret: OptionSelectItem = { label: getBiomeName(b), handler: () => { From 15e535a1a0e4328e0c6998f6008b656387fc2dfa Mon Sep 17 00:00:00 2001 From: Lylian BALL <131535108+PyGaVS@users.noreply.github.com> Date: Sun, 13 Apr 2025 03:22:04 +0200 Subject: [PATCH 17/52] [Ability] Implement Illusion (#3273) * implement illusion ability with unit test and localizations * try removing whitespace change on unnecessary files * nit corrections * nit update src/field/pokemon.ts Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> * nit update src/phases.ts Co-authored-by: Amani H. <109637146+xsn34kzx@users.noreply.github.com> * illusion test correction * unexpected error correction * refactor property pokemon.illusion to pokemon.battleData.illusion * nit * nit * update unit test up-to-date * add docs * merge up to date * bugfix * bugfix * merge up to date * refactor field illusion out of battleData * fix nit * fix nit * Zoroark change illusion after lastPokemon update * Zoroark change illusion after lastPokemon update * refactor bug fix * bugfix * bug fix on tests * Update src/field/pokemon.ts Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> * use GetFailedText * remove useless import * add condition 'no illusion' into transform move * wild Zoroark creates an illusion according to the current biome * wild Zoroark creates an illusion according to the current biome * delete console.log() * add doc * Update src/field/pokemon.ts Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> * fix tests * update locales submodule * update Illusion interface * bug fix * bug fix * bugfix * rename some params for future implementations * Zoroark keep illusion between battles * Zoroark keep illusion between battles * delete draft * merge up-to-date * bugfix * merge * merge * implement canApplyPresummon method * Update test/abilities/illusion.test.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * Update src/data/ability.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * Update src/data/ability.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * Update test/abilities/illusion.test.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * Update test/abilities/illusion.test.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * nit * review corrections * nit * type hints affected by enemy illusion * type hints affected by enemy illusionin fight-ui-handler * nit * rename some parameters back in useIllusion * Update src/field/pokemon.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/field/pokemon.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * refactor battleData.illusion as summonData.illusion and delete oncePerBattleClause * add comments * illusion will break before evolution * Update src/field/pokemon.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * Update src/data/ability.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * Update src/data/ability.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * Update src/data/ability.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * Update src/data/ability.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * bug fix * g * get submodule back * get submodule back * bug fix to save illusion status * add pokemon.getPokeball() * Update src/field/pokemon.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * Update src/field/pokemon.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * Update src/field/pokemon.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * Update src/data/ability.ts Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * Update src/field/pokemon.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/field/pokemon.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/field/pokemon.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/field/pokemon.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --------- Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> Co-authored-by: Amani H. <109637146+xsn34kzx@users.noreply.github.com> Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/battle-scene.ts | 2 +- src/data/ability.ts | 135 +++++++- src/data/moves/move.ts | 10 +- src/field/pokemon.ts | 495 ++++++++++++++++++++++-------- src/messages.ts | 17 +- src/phases/encounter-phase.ts | 7 +- src/phases/level-up-phase.ts | 1 + src/phases/summon-phase.ts | 14 +- src/phases/switch-summon-phase.ts | 11 +- src/system/game-data.ts | 4 +- src/system/pokemon-data.ts | 18 +- src/ui/battle-info.ts | 28 +- src/ui/fight-ui-handler.ts | 5 +- src/ui/party-ui-handler.ts | 38 +-- src/ui/rename-form-ui-handler.ts | 2 +- src/ui/summary-ui-handler.ts | 24 +- test/abilities/illusion.test.ts | 144 +++++++++ 17 files changed, 757 insertions(+), 198 deletions(-) create mode 100644 test/abilities/illusion.test.ts diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 8ae2be5af43..dd983f2b397 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1072,7 +1072,7 @@ export default class BattleScene extends SceneBase { container.add(icon); - if (pokemon.isFusion()) { + if (pokemon.isFusion(true)) { const fusionIcon = this.add.sprite(0, 0, pokemon.getFusionIconAtlasKey(ignoreOverride)); fusionIcon.setName("sprite-fusion-icon"); fusionIcon.setOrigin(0.5, 0); diff --git a/src/data/ability.ts b/src/data/ability.ts index b07f13c18e9..3e32a624f9f 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2370,6 +2370,18 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr { } } +/** + * Removes illusions when a Pokemon is summoned. + */ +export class PostSummonRemoveIllusionAbAttr extends PostSummonAbAttr { + applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { + for (const pokemon of globalScene.getField(true)) { + pokemon.breakIllusion(); + } + return true; + } +} + export class PostSummonUnnamedMessageAbAttr extends PostSummonAbAttr { //Attr doesn't force pokemon name on the message private message: string; @@ -2812,7 +2824,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { } private getTarget(targets: Pokemon[]): Pokemon { - let target: Pokemon; + let target: Pokemon = targets[0]; if (targets.length > 1) { globalScene.executeWithSeedOffset(() => { // in a double battle, if one of the opposing pokemon is fused the other one will be chosen @@ -2829,6 +2841,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { } else { target = targets[0]; } + target = target!; return target; @@ -2836,6 +2849,12 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { const targets = pokemon.getOpponents(); + const target = this.getTarget(targets); + + if (!!target.summonData?.illusion) { + return false; + } + if (simulated || !targets.length) { return simulated; } @@ -4741,8 +4760,8 @@ export class MaxMultiHitAbAttr extends AbAttr { } export class PostBattleAbAttr extends AbAttr { - constructor() { - super(true); + constructor(showAbility: boolean = true) { + super(showAbility); } canApplyPostBattle(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { @@ -5259,6 +5278,92 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { } } +/** + * Base class for defining {@linkcode Ability} attributes before summon + * (should use {@linkcode PostSummonAbAttr} for most ability) + * @see {@linkcode applyPreSummon()} + */ +export class PreSummonAbAttr extends AbAttr { + applyPreSummon(pokemon: Pokemon, passive: boolean, args: any[]): void {} + + canApplyPreSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + return true; + } +} + +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 + * @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; + pokemon.setIllusion(lastPokemon); + } + + override canApplyPreSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + pokemon.initSummondata() + 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 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 === Species.OGERPON || speciesId === Species.TERAPAGOS) && (lastPokemon.isTerastallized || pokemon.isTerastallized)) + ) { + return false; + } + } + return !pokemon.summonData.illusionBroken; + } +} + +export class IllusionBreakAbAttr 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 + * @returns - Whether the illusion was destroyed. + */ + 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 + } +} + +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 + * @returns - Whether the illusion was applied. + */ + 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). * @@ -6017,6 +6122,20 @@ export function applyPostSummonAbAttrs( ); } +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 + ); +} + export function applyPreSwitchOutAbAttrs( attrType: Constructor, pokemon: Pokemon, @@ -6811,8 +6930,14 @@ export function initAbilities() { return isNullOrUndefined(movePhase); }, 1.3), new Ability(Abilities.ILLUSION, 5) + //The pokemon generate an illusion if it's available + .attr(IllusionPreSummonAbAttr, false) + //The pokemon loses his illusion when he is damaged by a move + .attr(IllusionBreakAbAttr, true) + //Illusion is available again after a battle + .conditionalAttr((pokemon) => pokemon.isAllowedInBattle(), IllusionPostBattleAbAttr, false) .uncopiable() - .unimplemented(), + .bypassFaint(), new Ability(Abilities.IMPOSTER, 5) .attr(PostSummonTransformAbAttr) .uncopiable(), @@ -7223,6 +7348,8 @@ export function initAbilities() { .attr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr) .uncopiable() .attr(NoTransformAbilityAbAttr) + .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonNeutralizingGas", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) + .attr(PostSummonRemoveIllusionAbAttr) .bypassFaint(), new Ability(Abilities.PASTEL_VEIL, 8) .attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index a0f68dcd5cb..962a13bb840 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -7389,11 +7389,13 @@ export class AbilityChangeAttr extends MoveEffectAttr { const moveTarget = this.selfTarget ? user : target; - globalScene.queueMessage(i18next.t("moveTriggers:acquiredAbility", { pokemonName: getPokemonNameWithAffix((this.selfTarget ? user : target)), abilityName: allAbilities[this.ability].name })); - + globalScene.triggerPokemonFormChange(moveTarget, SpeciesFormChangeRevertWeatherFormTrigger); + if (moveTarget.breakIllusion()) { + globalScene.queueMessage(i18next.t("abilityTriggers:illusionBreak", { pokemonName: getPokemonNameWithAffix(moveTarget) })); + } + globalScene.queueMessage(i18next.t("moveTriggers:acquiredAbility", { pokemonName: getPokemonNameWithAffix(moveTarget), abilityName: allAbilities[this.ability].name })); moveTarget.setTempAbility(allAbilities[this.ability]); globalScene.triggerPokemonFormChange(moveTarget, SpeciesFormChangeRevertWeatherFormTrigger); - return true; } @@ -8673,6 +8675,8 @@ export function initMoves() { .makesContact(false), new StatusMove(Moves.TRANSFORM, PokemonType.NORMAL, -1, 10, -1, 0, 1) .attr(TransformAttr) + .condition((user, target, move) => !target.getTag(BattlerTagType.SUBSTITUTE)) + .condition((user, target, move) => !target.summonData?.illusion && !user.summonData?.illusion) // transforming from or into fusion pokemon causes various problems (such as crashes) .condition((user, target, move) => !target.getTag(BattlerTagType.SUBSTITUTE) && !user.fusionSpecies && !target.fusionSpecies) .ignoresProtect(), diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 8fc75ca657d..b59b7ba01fe 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -536,21 +536,33 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - getNameToRender() { + /** + * @param {boolean} useIllusion - Whether we want the fake name or the real name of the Pokemon (for Illusion ability). + */ + getNameToRender(useIllusion: boolean = true) { + const name: string = (!useIllusion && !!this.summonData?.illusion) ? this.summonData?.illusion.basePokemon!.name : this.name; + const nickname: string = (!useIllusion && !!this.summonData?.illusion) ? this.summonData?.illusion.basePokemon!.nickname : this.nickname; try { - if (this.nickname) { - return decodeURIComponent(escape(atob(this.nickname))); + if (nickname) { + return decodeURIComponent(escape(atob(nickname))); } - return this.name; + return name; } catch (err) { - console.error(`Failed to decode nickname for ${this.name}`, err); - return this.name; + console.error(`Failed to decode nickname for ${name}`, err); + return name; + } + } + + getPokeball(useIllusion = false){ + if(useIllusion){ + return this.summonData?.illusion?.pokeball ?? this.pokeball + } else { + return this.pokeball } } init(): void { this.fieldPosition = FieldPosition.CENTER; - this.initBattleInfo(); globalScene.fieldUI.addAt(this.battleInfo, 0); @@ -584,7 +596,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.addAt(sprite, 0); this.addAt(tintSprite, 1); - if (this.isShiny() && !this.shinySparkle) { + if (this.isShiny(true) && !this.shinySparkle) { this.initShinySparkle(); } } @@ -682,6 +694,92 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } + /** + * Generate an illusion of the last pokemon in the party, as other wild pokemon in the area. + */ + setIllusion(pokemon: Pokemon): boolean { + if(!!this.summonData?.illusion){ + this.breakIllusion(); + } + if (this.hasTrainer()) { + const speciesId = pokemon.species.speciesId; + + this.summonData.illusion = { + basePokemon: { + name: this.name, + nickname: this.nickname, + shiny: this.shiny, + variant: this.variant, + fusionShiny: this.fusionShiny, + fusionVariant: this.fusionVariant + }, + species: speciesId, + formIndex: pokemon.formIndex, + gender: pokemon.gender, + pokeball: pokemon.pokeball, + fusionFormIndex: pokemon.fusionFormIndex, + fusionSpecies: pokemon.fusionSpecies || undefined, + fusionGender: pokemon.fusionGender + }; + + this.name = pokemon.name; + this.nickname = pokemon.nickname; + this.shiny = pokemon.shiny; + this.variant = pokemon.variant; + this.fusionVariant = pokemon.fusionVariant; + this.fusionShiny = pokemon.fusionShiny; + if (this.shiny) { + this.initShinySparkle(); + } + this.loadAssets(false, true).then(() => this.playAnim()); + this.updateInfo(); + } else { + const randomIllusion: PokemonSpecies = globalScene.arena.randomSpecies(globalScene.currentBattle.waveIndex, this.level); + + this.summonData.illusion = { + basePokemon: { + name: this.name, + nickname: this.nickname, + shiny: this.shiny, + variant: this.variant, + fusionShiny: this.fusionShiny, + fusionVariant: this.fusionVariant + }, + species: randomIllusion.speciesId, + formIndex: randomIllusion.formIndex, + gender: this.gender, + pokeball: this.pokeball + }; + + this.name = randomIllusion.name; + this.loadAssets(false, true).then(() => this.playAnim()); + } + return true; + } + + breakIllusion(): boolean { + if (!this.summonData?.illusion) { + return false; + } else { + this.name = this.summonData?.illusion.basePokemon.name; + this.nickname = this.summonData?.illusion.basePokemon.nickname; + this.shiny = this.summonData?.illusion.basePokemon.shiny; + this.variant = this.summonData?.illusion.basePokemon.variant; + this.fusionVariant = this.summonData?.illusion.basePokemon.fusionVariant; + this.fusionShiny = this.summonData?.illusion.basePokemon.fusionShiny; + this.summonData.illusion = null; + } + if (this.isOnField()) { + globalScene.playSound("PRSFX- Transform"); + } + if (this.shiny) { + this.initShinySparkle(); + } + this.loadAssets(false).then(() => this.playAnim()); + this.updateInfo(true); + return true; + } + abstract isPlayer(): boolean; abstract hasTrainer(): boolean; @@ -690,29 +788,41 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { abstract getBattlerIndex(): BattlerIndex; - async loadAssets(ignoreOverride = true): Promise { + /** +   * @param useIllusion - Whether we want the illusion or not. +   */ + async loadAssets(ignoreOverride = true, useIllusion: boolean = false): Promise { /** Promises that are loading assets and can be run concurrently. */ const loadPromises: Promise[] = []; // Assets for moves loadPromises.push(loadMoveAnimations(this.getMoveset().map(m => m.getMove().id))); // Load the assets for the species form + const formIndex = !!this.summonData?.illusion && useIllusion ? this.summonData?.illusion.formIndex : this.formIndex; loadPromises.push( - this.getSpeciesForm().loadAssets(this.getGender() === Gender.FEMALE, this.formIndex, this.shiny, this.variant), + this.getSpeciesForm(false, useIllusion).loadAssets( + this.getGender(useIllusion) === Gender.FEMALE, + formIndex, + this.isShiny(useIllusion), + this.getVariant(useIllusion) + ), ); - if (this.isPlayer() || this.getFusionSpeciesForm()) { + if (this.isPlayer() || this.getFusionSpeciesForm(false, useIllusion)) { globalScene.loadPokemonAtlas( this.getBattleSpriteKey(true, ignoreOverride), this.getBattleSpriteAtlasPath(true, ignoreOverride), ); } if (this.getFusionSpeciesForm()) { - loadPromises.push(this.getFusionSpeciesForm().loadAssets( - this.getFusionGender() === Gender.FEMALE, - this.fusionFormIndex, - this.fusionShiny, - this.fusionVariant, + const fusionFormIndex = !!this.summonData?.illusion && useIllusion ? this.summonData?.illusion.fusionFormIndex : this.fusionFormIndex; + const fusionShiny = !!this.summonData?.illusion && !useIllusion ? this.summonData?.illusion.basePokemon!.fusionShiny : this.fusionShiny; + const fusionVariant = !!this.summonData?.illusion && !useIllusion ? this.summonData?.illusion.basePokemon!.fusionVariant : this.fusionVariant; + loadPromises.push(this.getFusionSpeciesForm(false, useIllusion).loadAssets( + this.getFusionGender(false, useIllusion) === Gender.FEMALE, + fusionFormIndex, + fusionShiny, + fusionVariant )); globalScene.loadPokemonAtlas( this.getFusionBattleSpriteKey(true, ignoreOverride), @@ -720,7 +830,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ); } - if (this.shiny) { + if (this.isShiny(true)) { loadPromises.push(populateVariantColors(this, false, ignoreOverride)) if (this.isPlayer()) { loadPromises.push(populateVariantColors(this, true, ignoreOverride)); @@ -870,11 +980,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getSpriteId(ignoreOverride?: boolean): string { - return this.getSpeciesForm(ignoreOverride).getSpriteId( - this.getGender(ignoreOverride) === Gender.FEMALE, - this.formIndex, - this.shiny, - this.variant, + const formIndex: integer = !!this.summonData?.illusion ? this.summonData?.illusion.formIndex! : this.formIndex; + return this.getSpeciesForm(ignoreOverride, true).getSpriteId( + this.getGender(ignoreOverride, true) === Gender.FEMALE, + formIndex, + this.shiny, + this.variant ); } @@ -882,21 +993,24 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (back === undefined) { back = this.isPlayer(); } - return this.getSpeciesForm(ignoreOverride).getSpriteId( - this.getGender(ignoreOverride) === Gender.FEMALE, - this.formIndex, - this.shiny, - this.variant, - back, + + const formIndex: integer = !!this.summonData?.illusion ? this.summonData?.illusion.formIndex! : this.formIndex; + + return this.getSpeciesForm(ignoreOverride, true).getSpriteId( + this.getGender(ignoreOverride, true) === Gender.FEMALE, + formIndex, + this.shiny, + this.variant, + back ); } getSpriteKey(ignoreOverride?: boolean): string { - return this.getSpeciesForm(ignoreOverride).getSpriteKey( + return this.getSpeciesForm(ignoreOverride, false).getSpriteKey( this.getGender(ignoreOverride) === Gender.FEMALE, this.formIndex, - this.shiny, - this.variant, + this.summonData?.illusion?.basePokemon.shiny ?? this.shiny, + this.summonData?.illusion?.basePokemon.variant ?? this.variant ); } @@ -905,11 +1019,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getFusionSpriteId(ignoreOverride?: boolean): string { - return this.getFusionSpeciesForm(ignoreOverride).getSpriteId( - this.getFusionGender(ignoreOverride) === Gender.FEMALE, - this.fusionFormIndex, - this.fusionShiny, - this.fusionVariant, + const fusionFormIndex: integer = !!this.summonData?.illusion ? this.summonData?.illusion.fusionFormIndex! : this.fusionFormIndex; + return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId( + this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, + fusionFormIndex, + this.fusionShiny, + this.fusionVariant ); } @@ -917,12 +1032,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (back === undefined) { back = this.isPlayer(); } - return this.getFusionSpeciesForm(ignoreOverride).getSpriteId( - this.getFusionGender(ignoreOverride) === Gender.FEMALE, - this.fusionFormIndex, - this.fusionShiny, - this.fusionVariant, - back, + + const fusionFormIndex: integer = !!this.summonData?.illusion ? this.summonData?.illusion.fusionFormIndex! : this.fusionFormIndex; + + return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId( + this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, + fusionFormIndex, + this.fusionShiny, + this.fusionVariant, + back ); } @@ -941,62 +1059,77 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getIconAtlasKey(ignoreOverride?: boolean): string { - return this.getSpeciesForm(ignoreOverride).getIconAtlasKey( - this.formIndex, - this.shiny, - this.variant, + const formIndex: integer = !!this.summonData?.illusion ? this.summonData?.illusion.formIndex : this.formIndex; + return this.getSpeciesForm(ignoreOverride, true).getIconAtlasKey( + formIndex, + this.shiny, + this.variant ); } getFusionIconAtlasKey(ignoreOverride?: boolean): string { - return this.getFusionSpeciesForm(ignoreOverride).getIconAtlasKey( - this.fusionFormIndex, - this.fusionShiny, - this.fusionVariant, + return this.getFusionSpeciesForm(ignoreOverride, true).getIconAtlasKey( + this.fusionFormIndex, + this.fusionShiny, + this.fusionVariant ); } getIconId(ignoreOverride?: boolean): string { - return this.getSpeciesForm(ignoreOverride).getIconId( - this.getGender(ignoreOverride) === Gender.FEMALE, - this.formIndex, - this.shiny, - this.variant, + const formIndex: integer = !!this.summonData?.illusion ? this.summonData?.illusion.formIndex : this.formIndex; + return this.getSpeciesForm(ignoreOverride, true).getIconId( + this.getGender(ignoreOverride, true) === Gender.FEMALE, + formIndex, + this.shiny, + this.variant ); } getFusionIconId(ignoreOverride?: boolean): string { - return this.getFusionSpeciesForm(ignoreOverride).getIconId( - this.getFusionGender(ignoreOverride) === Gender.FEMALE, - this.fusionFormIndex, - this.fusionShiny, - this.fusionVariant, + const fusionFormIndex: integer = !!this.summonData?.illusion ? this.summonData?.illusion.fusionFormIndex! : this.fusionFormIndex; + return this.getFusionSpeciesForm(ignoreOverride, true).getIconId( + this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, + fusionFormIndex, + this.fusionShiny, + this.fusionVariant ); } - getSpeciesForm(ignoreOverride?: boolean): PokemonSpeciesForm { + /** + * @param {boolean} useIllusion - Whether we want the speciesForm of the illusion or not. + */ + getSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { + const species: PokemonSpecies = useIllusion && !!this.summonData?.illusion ? getPokemonSpecies(this.summonData?.illusion.species) : this.species; + + const formIndex: integer = useIllusion && !!this.summonData?.illusion ? this.summonData?.illusion.formIndex : this.formIndex; + if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.speciesForm; } - if (this.species.forms && this.species.forms.length > 0) { - return this.species.forms[this.formIndex]; + if (species.forms && species.forms.length > 0) { + return species.forms[formIndex]; } - return this.species; + return species; } - getFusionSpeciesForm(ignoreOverride?: boolean): PokemonSpeciesForm { + /** + * @param {boolean} useIllusion - Whether we want the fusionSpeciesForm of the illusion or not. + */ + getFusionSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { + const fusionSpecies: PokemonSpecies = useIllusion && !!this.summonData?.illusion ? this.summonData?.illusion.fusionSpecies! : this.fusionSpecies!; + const fusionFormIndex: integer = useIllusion && !!this.summonData?.illusion ? this.summonData?.illusion.fusionFormIndex! : this.fusionFormIndex; + if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.fusionSpeciesForm; } if ( - !this.fusionSpecies?.forms?.length || - this.fusionFormIndex >= this.fusionSpecies?.forms.length + !fusionSpecies?.forms?.length || + fusionFormIndex >= fusionSpecies?.forms.length ) { - //@ts-ignore - return this.fusionSpecies; // TODO: I don't even know how to fix this... A complete cluster of classes involved + null + return fusionSpecies; } - return this.fusionSpecies?.forms[this.fusionFormIndex]; + return fusionSpecies?.forms[fusionFormIndex]; } getSprite(): Phaser.GameObjects.Sprite { @@ -1652,36 +1785,98 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - getGender(ignoreOverride?: boolean): Gender { - if (!ignoreOverride && this.summonData?.gender !== undefined) { + /** + * @param {boolean} useIllusion - Whether we want the fake or real gender (illusion ability). + */ + getGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { + if (useIllusion && !!this.summonData?.illusion) { + return this.summonData?.illusion.gender!; + } else if (!ignoreOverride && this.summonData?.gender !== undefined) { return this.summonData.gender; } return this.gender; } - getFusionGender(ignoreOverride?: boolean): Gender { - if (!ignoreOverride && this.summonData?.fusionGender !== undefined) { + /** + * @param {boolean} useIllusion - Whether we want the fake or real gender (illusion ability). + */ + getFusionGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { + if (useIllusion && !!this.summonData?.illusion) { + return this.summonData?.illusion.fusionGender!; + } else if (!ignoreOverride && this.summonData?.fusionGender !== undefined) { return this.summonData.fusionGender; } return this.fusionGender; } - isShiny(): boolean { - return this.shiny || (this.isFusion() && this.fusionShiny); + /** + * @param {boolean} useIllusion - Whether we want the fake or real shininess (illusion ability). + */ + isShiny(useIllusion: boolean = false): boolean { + if (!useIllusion && !!this.summonData?.illusion) { + return this.summonData?.illusion.basePokemon?.shiny || (!!this.summonData?.illusion.fusionSpecies && this.summonData?.illusion.basePokemon?.fusionShiny) || false; + } else { + return this.shiny || (this.isFusion(useIllusion) && this.fusionShiny); + } } - getVariant(): Variant { - return !this.isFusion() - ? this.variant - : (Math.max(this.variant, this.fusionVariant) as Variant); + /** + * + * @param useIllusion - Whether we want the fake or real shininess (illusion ability). + * @returns `true` if the {@linkcode Pokemon} is shiny and the fusion is shiny as well, `false` otherwise + */ + isDoubleShiny(useIllusion: boolean = false): boolean { + if (!useIllusion && !!this.summonData?.illusion) { + return this.isFusion(false) && this.summonData?.illusion.basePokemon.shiny && this.summonData?.illusion.basePokemon.fusionShiny; + } else { + return this.isFusion(useIllusion) && this.shiny && this.fusionShiny; + } + } + + /** + * @param {boolean} useIllusion - Whether we want the fake or real variant (illusion ability). + */ + getVariant(useIllusion: boolean = false): Variant { + if (!useIllusion && !!this.summonData?.illusion) { + return !this.isFusion(false) + ? this.summonData?.illusion.basePokemon!.variant + : Math.max(this.variant, this.fusionVariant) as Variant; + } else { + return !this.isFusion(true) + ? this.variant + : Math.max(this.variant, this.fusionVariant) as Variant; + } + } + + getBaseVariant(doubleShiny: boolean): Variant { + if (doubleShiny) { + return !!this.summonData?.illusion + ? this.summonData?.illusion.basePokemon!.variant + : this.variant; + } else { + return this.getVariant(); + } } getLuck(): number { return this.luck + (this.isFusion() ? this.fusionLuck : 0); } - isFusion(): boolean { - return !!this.fusionSpecies; + isFusion(useIllusion: boolean = false): boolean { + if (useIllusion && !!this.summonData?.illusion) { + return !!this.summonData?.illusion.fusionSpecies; + } else { + return !!this.fusionSpecies; + } + } + + /** + * @param {boolean} useIllusion - Whether we want the fake name or the real name of the Pokemon (for Illusion ability). + */ + getName(useIllusion: boolean = false): string { + return (!useIllusion && !!this.summonData?.illusion && this.summonData?.illusion.basePokemon) + ? this.summonData?.illusion.basePokemon.name + : this.name; } /** @@ -1796,12 +1991,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param includeTeraType - `true` to include tera-formed type; Default: `false` * @param forDefend - `true` if the pokemon is defending from an attack; Default: `false` * @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false` + * @param useIllusion - `true` to return the types of the illusion instead of the actual types; "AUTO" will depend on forDefend param; Default: "AUTO" * @returns array of {@linkcode PokemonType} */ public getTypes( - includeTeraType = false, - forDefend = false, - ignoreOverride = false, + includeTeraType = false, + forDefend: boolean = false, + ignoreOverride?: boolean, + useIllusion: boolean | "AUTO" = "AUTO" ): PokemonType[] { const types: PokemonType[] = []; @@ -1815,17 +2012,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } } - if (!types.length || !includeTeraType) { + + const doIllusion: boolean = (useIllusion === "AUTO") ? !forDefend : useIllusion; if ( - !ignoreOverride && - this.summonData?.types && - this.summonData.types.length > 0 + !ignoreOverride && + this.summonData?.types && + this.summonData.types.length > 0 && + (!this.summonData?.illusion || !doIllusion) ) { this.summonData.types.forEach(t => types.push(t)); } else { - const speciesForm = this.getSpeciesForm(ignoreOverride); - const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOverride); + const speciesForm = this.getSpeciesForm(ignoreOverride, doIllusion); + const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOverride, doIllusion); const customTypes = this.customPokemonData.types?.length > 0; // First type, checking for "permanently changed" types from ME @@ -2378,6 +2577,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param ignoreAbility Whether to ignore abilities that might affect type effectiveness or immunity (defaults to `false`). * @param simulated Whether to apply abilities via simulated calls (defaults to `true`) * @param cancelled {@linkcode BooleanHolder} Stores whether the move was cancelled by a non-type-based immunity. + * @param useIllusion - Whether we want the attack move effectiveness on the illusion or not * Currently only used by {@linkcode Pokemon.apply} to determine whether a "No effect" message should be shown. * @returns The type damage multiplier, indicating the effectiveness of the move */ @@ -2387,6 +2587,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ignoreAbility = false, simulated = true, cancelled?: BooleanHolder, + useIllusion: boolean = false ): TypeDamageMultiplier { if (!isNullOrUndefined(this.turnData?.moveEffectiveness)) { return this.turnData?.moveEffectiveness; @@ -2398,17 +2599,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const moveType = source.getMoveType(move); const typeMultiplier = new NumberHolder( - move.category !== MoveCategory.STATUS || - move.hasAttr(RespectAttackTypeImmunityAttr) - ? this.getAttackTypeEffectiveness( - moveType, - source, - false, - simulated, - move, - ) - : 1, - ); + move.category !== MoveCategory.STATUS || + move.hasAttr(RespectAttackTypeImmunityAttr) + ? this.getAttackTypeEffectiveness( + moveType, + source, + false, + simulated, + move, + useIllusion + ) + : 1); applyMoveAttrs( VariableMoveTypeMultiplierAttr, @@ -2512,19 +2713,21 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param ignoreStrongWinds whether or not this ignores strong winds (anticipation, forewarn, stealth rocks) * @param simulated tag to only apply the strong winds effect message when the move is used * @param move (optional) the move whose type effectiveness is to be checked. Used for applying {@linkcode VariableMoveTypeChartAttr} + * @param useIllusion - Whether we want the attack type effectiveness on the illusion or not * @returns a multiplier for the type effectiveness */ getAttackTypeEffectiveness( - moveType: PokemonType, - source?: Pokemon, - ignoreStrongWinds = false, - simulated = true, - move?: Move, + moveType: PokemonType, + source?: Pokemon, + ignoreStrongWinds: boolean = false, + simulated: boolean = true, + move?: Move, + useIllusion: boolean = false ): TypeDamageMultiplier { if (moveType === PokemonType.STELLAR) { return this.isTerastallized ? 2 : 1; } - const types = this.getTypes(true, true); + const types = this.getTypes(true, true, undefined, useIllusion); const arena = globalScene.arena; // Handle flying v ground type immunity without removing flying type so effective types are still effective @@ -2623,7 +2826,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ getMatchupScore(opponent: Pokemon): number { const types = this.getTypes(true); - const enemyTypes = opponent.getTypes(true, true); + + const enemyTypes = opponent.getTypes(true, true, false, true); /** Is this Pokemon faster than the opponent? */ const outspeed = (this.isActive(true) @@ -2634,9 +2838,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * Based on how effective this Pokemon's types are offensively against the opponent's types. * This score is increased by 25 percent if this Pokemon is faster than the opponent. */ - let atkScore = - opponent.getAttackTypeEffectiveness(types[0], this) * - (outspeed ? 1.25 : 1); + let atkScore = opponent.getAttackTypeEffectiveness(types[0], this, false, true, undefined, true) * (outspeed ? 1.25 : 1); /** * Based on how effectively this Pokemon defends against the opponent's types. * This score cannot be higher than 4. @@ -2648,12 +2850,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { atkScore *= opponent.getAttackTypeEffectiveness(types[1], this); } if (enemyTypes.length > 1) { - defScore *= - 1 / - Math.max( - this.getAttackTypeEffectiveness(enemyTypes[1], opponent), - 0.25, - ); + defScore *= (1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[1], opponent, false, false, undefined, true), 0.25)); } /** * Based on this Pokemon's HP ratio compared to that of the opponent. @@ -5538,7 +5735,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.summonDataPrimer = summonDataPrimer; } + // For PreSummonAbAttr to get access to summonData + initSummondata(): void { + this.summonData = this.summonData ?? this.summonDataPrimer ?? new PokemonSummonData() + } + resetSummonData(): void { + const illusion: IllusionData | null = this.summonData?.illusion; if (this.summonData?.speciesForm) { this.summonData.speciesForm = null; this.updateFusionPalette(); @@ -5574,6 +5777,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } this.summonDataPrimer = null; } + this.summonData.illusion = illusion this.updateInfo(); } @@ -7146,15 +7350,11 @@ export class EnemyPokemon extends Pokemon { ) { targetScore = -20; } else if (move instanceof AttackMove) { - /** - * Attack moves are given extra multipliers to their base benefit score based on - * the move's type effectiveness against the target and whether the move is a STAB move. - */ - const effectiveness = target.getMoveEffectiveness( - this, - move, - !target.battleData?.abilityRevealed, - ); + /** + * Attack moves are given extra multipliers to their base benefit score based on + * the move's type effectiveness against the target and whether the move is a STAB move. + */ + const effectiveness = target.getMoveEffectiveness(this, move, !target.battleData?.abilityRevealed, undefined, undefined, true); if (target.isPlayer() !== this.isPlayer()) { targetScore *= effectiveness; if (this.isOfType(move.type)) { @@ -7543,6 +7743,42 @@ export class EnemyPokemon extends Pokemon { } } +/** + * Illusion property + */ +interface IllusionData { + basePokemon: { + /** The actual name of the Pokemon */ + name: string; + /** The actual nickname of the Pokemon */ + nickname: string; + /** Whether the base pokemon is shiny or not */ + shiny: boolean; + /** The shiny variant of the base pokemon */ + variant: Variant; + /** Whether the fusion species of the base pokemon is shiny or not */ + fusionShiny: boolean; + /** The variant of the fusion species of the base pokemon */ + fusionVariant: Variant; + }; + /** The species of the illusion */ + species: Species; + /** The formIndex of the illusion */ + formIndex: number; + /** The gender of the illusion */ + gender: Gender; + /** The pokeball of the illusion */ + pokeball: PokeballType; + /** The fusion species of the illusion if it's a fusion */ + fusionSpecies?: PokemonSpecies; + /** The fusionFormIndex of the illusion */ + fusionFormIndex?: number; + /** The fusionGender of the illusion if it's a fusion */ + fusionGender?: Gender; + /** The level of the illusion (not used currently) */ + level?: number +} + export interface TurnMove { move: Moves; targets: BattlerIndex[]; @@ -7576,9 +7812,12 @@ export class PokemonSummonData { public fusionGender: Gender; public stats: number[] = [0, 0, 0, 0, 0, 0]; public moveset: PokemonMove[]; + public illusionBroken: boolean = false; + // If not initialized this value will not be populated from save data. public types: PokemonType[] = []; public addedType: PokemonType | null = null; + public illusion: IllusionData | null = null; } export class PokemonBattleData { @@ -7589,7 +7828,7 @@ export class PokemonBattleData { public endured = false; public berriesEaten: BerryType[] = []; public abilitiesApplied: Abilities[] = []; - public abilityRevealed = false; + public abilityRevealed: boolean = false; } export class PokemonBattleSummonData { diff --git a/src/messages.ts b/src/messages.ts index e35b48f7226..c29151a98b3 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -6,9 +6,10 @@ import i18next from "i18next"; /** * Retrieves the Pokemon's name, potentially with an affix indicating its role (wild or foe) in the current battle context, translated * @param pokemon {@linkcode Pokemon} name and battle context will be retrieved from this instance + * @param {boolean} useIllusion - Whether we want the name of the illusion or not. Default value : true * @returns {string} ex: "Wild Gengar", "Ectoplasma sauvage" */ -export function getPokemonNameWithAffix(pokemon: Pokemon | undefined): string { +export function getPokemonNameWithAffix(pokemon: Pokemon | undefined, useIllusion = true): string { if (!pokemon) { return "Missigno"; } @@ -18,19 +19,17 @@ export function getPokemonNameWithAffix(pokemon: Pokemon | undefined): string { return !pokemon.isPlayer() ? pokemon.hasTrainer() ? i18next.t("battle:foePokemonWithAffix", { - pokemonName: pokemon.getNameToRender(), + pokemonName: pokemon.getNameToRender(useIllusion), }) : i18next.t("battle:wildPokemonWithAffix", { - pokemonName: pokemon.getNameToRender(), + pokemonName: pokemon.getNameToRender(useIllusion), }) - : pokemon.getNameToRender(); + : pokemon.getNameToRender(useIllusion); case BattleSpec.FINAL_BOSS: return !pokemon.isPlayer() - ? i18next.t("battle:foePokemonWithAffix", { - pokemonName: pokemon.getNameToRender(), - }) - : pokemon.getNameToRender(); + ? i18next.t("battle:foePokemonWithAffix", { pokemonName: pokemon.getNameToRender(useIllusion) }) + : pokemon.getNameToRender(useIllusion); default: - return pokemon.getNameToRender(); + return pokemon.getNameToRender(useIllusion); } } diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index 9e5edf3e1d9..15f3d102e41 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -1,7 +1,7 @@ import { BattlerIndex, BattleType } from "#app/battle"; import { globalScene } from "#app/global-scene"; import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; -import { applyAbAttrs, SyncEncounterNatureAbAttr } from "#app/data/ability"; +import { applyAbAttrs, SyncEncounterNatureAbAttr, applyPreSummonAbAttrs, PreSummonAbAttr } from "#app/data/ability"; import { initEncounterAnims, loadEncounterAnimAssets } from "#app/data/battle-anims"; import { getCharVariantFromDialogue } from "#app/data/dialogue"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; @@ -259,6 +259,9 @@ export class EncounterPhase extends BattlePhase { } if (e < (battle.double ? 2 : 1)) { if (battle.battleType === BattleType.WILD) { + for (const pokemon of globalScene.getField()) { + applyPreSummonAbAttrs(PreSummonAbAttr, pokemon, []); + } globalScene.field.add(enemyPokemon); battle.seenEnemyPartyMemberIds.add(enemyPokemon.id); const playerPokemon = globalScene.getPlayerPokemon(); @@ -545,7 +548,7 @@ export class EncounterPhase extends BattlePhase { const enemyField = globalScene.getEnemyField(); enemyField.forEach((enemyPokemon, e) => { - if (enemyPokemon.isShiny()) { + if (enemyPokemon.isShiny(true)) { globalScene.unshiftPhase(new ShinySparklePhase(BattlerIndex.ENEMY + e)); } /** This sets Eternatus' held item to be untransferrable, preventing it from being stolen */ diff --git a/src/phases/level-up-phase.ts b/src/phases/level-up-phase.ts index 31c7fabf451..c6ca17d583e 100644 --- a/src/phases/level-up-phase.ts +++ b/src/phases/level-up-phase.ts @@ -71,6 +71,7 @@ export class LevelUpPhase extends PlayerPartyMemberPokemonPhase { if (!this.pokemon.pauseEvolutions) { const evolution = this.pokemon.getEvolution(); if (evolution) { + this.pokemon.breakIllusion() globalScene.unshiftPhase(new EvolutionPhase(this.pokemon, evolution, this.lastLevel)); } } diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index 621c8c8c2a9..7379d509e55 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -13,6 +13,7 @@ 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/ability"; import { globalScene } from "#app/global-scene"; export class SummonPhase extends PartyMemberPokemonPhase { @@ -27,6 +28,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { start() { super.start(); + applyPreSummonAbAttrs(PreSummonAbAttr, this.getPokemon()); this.preSummon(); } @@ -126,7 +128,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { this.player ? 36 : 248, this.player ? 80 : 44, "pb", - getPokeballAtlasKey(pokemon.pokeball), + getPokeballAtlasKey(pokemon.getPokeball(true)), ); pokeball.setVisible(false); pokeball.setOrigin(0.5, 0.625); @@ -175,7 +177,11 @@ export class SummonPhase extends PartyMemberPokemonPhase { } globalScene.currentBattle.seenEnemyPartyMemberIds.add(pokemon.id); } - addPokeballOpenParticles(pokemon.x, pokemon.y - 16, pokemon.pokeball); + addPokeballOpenParticles( + pokemon.x, + pokemon.y - 16, + pokemon.getPokeball(true), + ); globalScene.updateModifiers(this.player); globalScene.updateFieldScale(); pokemon.showInfo(); @@ -183,7 +189,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { pokemon.setVisible(true); pokemon.getSprite().setVisible(true); pokemon.setScale(0.5); - pokemon.tint(getPokeballTintColor(pokemon.pokeball)); + pokemon.tint(getPokeballTintColor(pokemon.getPokeball(true))); pokemon.untint(250, "Sine.easeIn"); globalScene.updateFieldScale(); globalScene.tweens.add({ @@ -270,7 +276,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { onEnd(): void { const pokemon = this.getPokemon(); - if (pokemon.isShiny()) { + if (pokemon.isShiny(true)) { globalScene.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex())); } diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index e0903ada275..d63cdb90f25 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -1,5 +1,11 @@ import { globalScene } from "#app/global-scene"; -import { applyPreSwitchOutAbAttrs, PostDamageForceSwitchAbAttr, PreSwitchOutAbAttr } from "#app/data/ability"; +import { + applyPreSummonAbAttrs, + applyPreSwitchOutAbAttrs, + PostDamageForceSwitchAbAttr, + PreSummonAbAttr, + PreSwitchOutAbAttr, +} from "#app/data/ability"; import { allMoves, ForceSwitchOutAttr } from "#app/data/moves/move"; import { getPokeballTintColor } from "#app/data/pokeball"; import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms"; @@ -99,7 +105,7 @@ export class SwitchSummonPhase extends SummonPhase { ); globalScene.playSound("se/pb_rel"); pokemon.hideInfo(); - pokemon.tint(getPokeballTintColor(pokemon.pokeball), 1, 250, "Sine.easeIn"); + pokemon.tint(getPokeballTintColor(pokemon.getPokeball(true)), 1, 250, "Sine.easeIn"); globalScene.tweens.add({ targets: pokemon, duration: 250, @@ -116,6 +122,7 @@ export class SwitchSummonPhase extends SummonPhase { const party = this.player ? this.getParty() : globalScene.getEnemyParty(); const switchedInPokemon = party[this.slotIndex]; this.lastPokemon = this.getPokemon(); + applyPreSummonAbAttrs(PreSummonAbAttr, switchedInPokemon); applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, this.lastPokemon); if (this.switchType === SwitchType.BATON_PASS && switchedInPokemon) { (this.player ? globalScene.getEnemyField() : globalScene.getPlayerField()).forEach(enemyPokemon => diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 63955b02de8..53146301666 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -1426,12 +1426,11 @@ export class GameData { ), ) // TODO: is this bang correct? : this.getSessionSaveData(); - const maxIntAttrValue = 0x80000000; const systemData = useCachedSystem ? this.parseSystemData(decrypt(localStorage.getItem(`data_${loggedInUser?.username}`)!, bypassLogin)) : this.getSystemSaveData(); // TODO: is this bang correct? - + const request = { system: systemData, session: sessionData, @@ -1448,7 +1447,6 @@ export class GameData { bypassLogin, ), ); - localStorage.setItem( `sessionData${globalScene.sessionSlotId ? globalScene.sessionSlotId : ""}_${loggedInUser?.username}`, encrypt(JSON.stringify(sessionData), bypassLogin), diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 7cdcb0c72c3..97ce494a43a 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -79,12 +79,14 @@ export default class PokemonData { this.id = source.id; this.player = sourcePokemon ? sourcePokemon.isPlayer() : source.player; this.species = sourcePokemon ? sourcePokemon.species.speciesId : source.species; - this.nickname = sourcePokemon ? sourcePokemon.nickname : source.nickname; + this.nickname = sourcePokemon + ? (!!sourcePokemon.summonData?.illusion ? sourcePokemon.summonData.illusion.basePokemon.nickname : sourcePokemon.nickname) + : source.nickname; this.formIndex = Math.max(Math.min(source.formIndex, getPokemonSpecies(this.species).forms.length - 1), 0); this.abilityIndex = source.abilityIndex; this.passive = source.passive; - this.shiny = source.shiny; - this.variant = source.variant; + this.shiny = sourcePokemon ? sourcePokemon.isShiny() : source.shiny; + this.variant = sourcePokemon ? sourcePokemon.getVariant() : source.variant; this.pokeball = source.pokeball; this.level = source.level; this.exp = source.exp; @@ -117,8 +119,12 @@ export default class PokemonData { this.fusionSpecies = sourcePokemon ? sourcePokemon.fusionSpecies?.speciesId : source.fusionSpecies; this.fusionFormIndex = source.fusionFormIndex; this.fusionAbilityIndex = source.fusionAbilityIndex; - this.fusionShiny = source.fusionShiny; - this.fusionVariant = source.fusionVariant; + this.fusionShiny = sourcePokemon + ? (!!sourcePokemon.summonData?.illusion ? sourcePokemon.summonData.illusion.basePokemon.fusionShiny : sourcePokemon.fusionShiny) + : source.fusionShiny; + this.fusionVariant = sourcePokemon + ? (!!sourcePokemon.summonData?.illusion ? sourcePokemon.summonData.illusion.basePokemon.fusionVariant : sourcePokemon.fusionVariant) + : source.fusionVariant; this.fusionGender = source.fusionGender; this.fusionLuck = source.fusionLuck !== undefined ? source.fusionLuck : source.fusionShiny ? source.fusionVariant + 1 : 0; @@ -174,6 +180,7 @@ export default class PokemonData { this.summonData.types = source.summonData.types; this.summonData.speciesForm = source.summonData.speciesForm; this.summonDataSpeciesFormIndex = source.summonDataSpeciesFormIndex; + this.summonData.illusionBroken = source.summonData.illusionBroken; if (source.summonData.tags) { this.summonData.tags = source.summonData.tags?.map(t => loadBattlerTag(t)); @@ -219,6 +226,7 @@ export default class PokemonData { if (this.summonData) { // when loading from saved session, recover summonData.speciesFrom and form index species object // used to stay transformed on reload session + if (this.summonData.speciesForm) { this.summonData.speciesForm = getPokemonSpeciesForm( this.summonData.speciesForm.speciesId, diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index 2b205329ab8..06c5f7fb3f1 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -356,7 +356,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { }); this.teraIcon.on("pointerout", () => globalScene.ui.hideTooltip()); - const isFusion = pokemon.isFusion(); + const isFusion = pokemon.isFusion(true); this.splicedIcon.setPositionRelative( this.nameText, @@ -375,7 +375,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { } const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny; - const baseVariant = !doubleShiny ? pokemon.getVariant() : pokemon.variant; + const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant; this.shinyIcon.setPositionRelative( this.nameText, @@ -617,6 +617,11 @@ export default class BattleInfo extends Phaser.GameObjects.Container { return resolve(); } + const gender: Gender = !!pokemon.summonData?.illusion ? pokemon.summonData?.illusion.gender : pokemon.gender; + + this.genderText.setText(getGenderSymbol(gender)); + this.genderText.setColor(getGenderColor(gender)); + const nameUpdated = this.lastName !== pokemon.getNameToRender(); if (nameUpdated) { @@ -638,8 +643,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.lastTeraType = teraType; } + const isFusion = pokemon.isFusion(true); + if (nameUpdated || teraTypeUpdated) { - this.splicedIcon.setVisible(!!pokemon.fusionSpecies); + this.splicedIcon.setVisible(isFusion); this.teraIcon.setPositionRelative( this.nameText, @@ -764,7 +771,17 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.lastStats = statsStr; } - this.shinyIcon.setVisible(pokemon.isShiny()); + this.shinyIcon.setVisible(pokemon.isShiny(true)); + + const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny; + const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant; + this.shinyIcon.setTint(getVariantTint(baseVariant)); + + this.fusionShinyIcon.setVisible(doubleShiny); + if (isFusion) { + this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant)); + } + this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y); resolve(); }); @@ -777,10 +794,11 @@ export default class BattleInfo extends Phaser.GameObjects.Container { const nameSizeTest = addTextObject(0, 0, displayName, TextStyle.BATTLE_INFO); nameTextWidth = nameSizeTest.displayWidth; + const gender: Gender = !!pokemon.summonData?.illusion ? pokemon.summonData?.illusion.gender : pokemon.gender; while ( nameTextWidth > (this.player || !this.boss ? 60 : 98) - - ((pokemon.gender !== Gender.GENDERLESS ? 6 : 0) + + ((gender !== Gender.GENDERLESS ? 6 : 0) + (pokemon.fusionSpecies ? 8 : 0) + (pokemon.isShiny() ? 8 : 0) + (Math.min(pokemon.level.toString().length, 3) - 3) * 8) diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index 3775dbc2228..27985629e3d 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -306,6 +306,9 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { pokemon, pokemonMove.getMove(), !opponent.battleData?.abilityRevealed, + undefined, + undefined, + true ); if (effectiveness === undefined) { return undefined; @@ -350,7 +353,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { const moveColors = opponents .map(opponent => - opponent.getMoveEffectiveness(pokemon, pokemonMove.getMove(), !opponent.battleData.abilityRevealed), + opponent.getMoveEffectiveness(pokemon, pokemonMove.getMove(), !opponent.battleData.abilityRevealed, undefined, undefined, true), ) .sort((a, b) => b - a) .map(effectiveness => getTypeDamageMultiplierColor(effectiveness ?? 0, "offense")); diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 61a98d79fbb..ba90108c274 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -193,18 +193,14 @@ export default class PartyUiHandler extends MessageUiHandler { public static FilterNonFainted = (pokemon: PlayerPokemon) => { if (pokemon.isFainted()) { - return i18next.t("partyUiHandler:noEnergy", { - pokemonName: getPokemonNameWithAffix(pokemon), - }); + return i18next.t("partyUiHandler:noEnergy", { pokemonName: getPokemonNameWithAffix(pokemon, false) }); } return null; }; public static FilterFainted = (pokemon: PlayerPokemon) => { if (!pokemon.isFainted()) { - return i18next.t("partyUiHandler:hasEnergy", { - pokemonName: getPokemonNameWithAffix(pokemon), - }); + return i18next.t("partyUiHandler:hasEnergy", { pokemonName: getPokemonNameWithAffix(pokemon, false) }); } return null; }; @@ -218,9 +214,7 @@ export default class PartyUiHandler extends MessageUiHandler { const challengeAllowed = new BooleanHolder(true); applyChallenges(ChallengeType.POKEMON_IN_BATTLE, pokemon, challengeAllowed); if (!challengeAllowed.value) { - return i18next.t("partyUiHandler:cantBeUsed", { - pokemonName: getPokemonNameWithAffix(pokemon), - }); + return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: getPokemonNameWithAffix(pokemon, false) }); } return null; }; @@ -232,9 +226,7 @@ export default class PartyUiHandler extends MessageUiHandler { m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id && m.matchType(modifier), ) as PokemonHeldItemModifier; if (matchingModifier && matchingModifier.stackCount === matchingModifier.getMaxStackCount()) { - return i18next.t("partyUiHandler:tooManyItems", { - pokemonName: getPokemonNameWithAffix(pokemon), - }); + return i18next.t("partyUiHandler:tooManyItems", { pokemonName: getPokemonNameWithAffix(pokemon, false) }); } return null; }; @@ -583,7 +575,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.showText( i18next.t( pokemon.pauseEvolutions ? "partyUiHandler:pausedEvolutions" : "partyUiHandler:unpausedEvolutions", - { pokemonName: getPokemonNameWithAffix(pokemon) }, + { pokemonName: getPokemonNameWithAffix(pokemon, false) }, ), undefined, () => this.showText("", 0), @@ -596,14 +588,14 @@ export default class PartyUiHandler extends MessageUiHandler { this.showText( i18next.t("partyUiHandler:unspliceConfirmation", { fusionName: pokemon.fusionSpecies?.name, - pokemonName: pokemon.name, + pokemonName: pokemon.getName(), }), null, () => { ui.setModeWithoutClear( Mode.CONFIRM, () => { - const fusionName = pokemon.name; + const fusionName = pokemon.getName(); pokemon.unfuse().then(() => { this.clearPartySlots(); this.populatePartySlots(); @@ -611,7 +603,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.showText( i18next.t("partyUiHandler:wasReverted", { fusionName: fusionName, - pokemonName: pokemon.name, + pokemonName: pokemon.getName(false), }), undefined, () => { @@ -637,7 +629,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.blockInput = true; this.showText( i18next.t("partyUiHandler:releaseConfirmation", { - pokemonName: getPokemonNameWithAffix(pokemon), + pokemonName: getPokemonNameWithAffix(pokemon, false), }), null, () => { @@ -1285,7 +1277,7 @@ export default class PartyUiHandler extends MessageUiHandler { doRelease(slotIndex: number): void { this.showText( - this.getReleaseMessage(getPokemonNameWithAffix(globalScene.getPlayerParty()[slotIndex])), + this.getReleaseMessage(getPokemonNameWithAffix(globalScene.getPlayerParty()[slotIndex], false)), null, () => { this.clearPartySlots(); @@ -1495,7 +1487,7 @@ class PartySlot extends Phaser.GameObjects.Container { const slotInfoContainer = globalScene.add.container(0, 0); this.add(slotInfoContainer); - let displayName = this.pokemon.getNameToRender(); + let displayName = this.pokemon.getNameToRender(false); let nameTextWidth: number; const nameSizeTest = addTextObject(0, 0, displayName, TextStyle.PARTY); @@ -1575,12 +1567,12 @@ class PartySlot extends Phaser.GameObjects.Container { } if (this.pokemon.isShiny()) { - const doubleShiny = this.pokemon.isFusion() && this.pokemon.shiny && this.pokemon.fusionShiny; + const doubleShiny = this.pokemon.isDoubleShiny(false); const shinyStar = globalScene.add.image(0, 0, `shiny_star_small${doubleShiny ? "_1" : ""}`); shinyStar.setOrigin(0, 0); shinyStar.setPositionRelative(this.slotName, -9, 3); - shinyStar.setTint(getVariantTint(!doubleShiny ? this.pokemon.getVariant() : this.pokemon.variant)); + shinyStar.setTint(getVariantTint(this.pokemon.getBaseVariant(doubleShiny))); slotInfoContainer.add(shinyStar); @@ -1588,7 +1580,9 @@ class PartySlot extends Phaser.GameObjects.Container { const fusionShinyStar = globalScene.add.image(0, 0, "shiny_star_small_2"); fusionShinyStar.setOrigin(0, 0); fusionShinyStar.setPosition(shinyStar.x, shinyStar.y); - fusionShinyStar.setTint(getVariantTint(this.pokemon.fusionVariant)); + fusionShinyStar.setTint( + getVariantTint(this.pokemon.summonData?.illusion?.basePokemon.fusionVariant ?? this.pokemon.fusionVariant), + ); slotInfoContainer.add(fusionShinyStar); } diff --git a/src/ui/rename-form-ui-handler.ts b/src/ui/rename-form-ui-handler.ts index 91c0025d283..7083f83865b 100644 --- a/src/ui/rename-form-ui-handler.ts +++ b/src/ui/rename-form-ui-handler.ts @@ -38,7 +38,7 @@ export default class RenameFormUiHandler extends FormModalUiHandler { if (super.show(args)) { const config = args[0] as ModalConfig; if (args[1] && typeof (args[1] as PlayerPokemon).getNameToRender === "function") { - this.inputs[0].text = (args[1] as PlayerPokemon).getNameToRender(); + this.inputs[0].text = (args[1] as PlayerPokemon).getNameToRender(false); } else { this.inputs[0].text = args[1]; } diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 1e0924aa2c5..04bcf71d7ae 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -357,8 +357,14 @@ export default class SummaryUiHandler extends UiHandler { this.pokemonSprite.setPipelineData("isTerastallized", this.pokemon.isTerastallized); this.pokemonSprite.setPipelineData("ignoreTimeTint", true); this.pokemonSprite.setPipelineData("spriteKey", this.pokemon.getSpriteKey()); - this.pokemonSprite.setPipelineData("shiny", this.pokemon.shiny); - this.pokemonSprite.setPipelineData("variant", this.pokemon.variant); + this.pokemonSprite.setPipelineData( + "shiny", + this.pokemon.summonData?.illusion?.basePokemon.shiny ?? this.pokemon.shiny, + ); + this.pokemonSprite.setPipelineData( + "variant", + this.pokemon.summonData?.illusion?.basePokemon.variant ?? this.pokemon.variant, + ); ["spriteColors", "fusionSpriteColors"].map(k => { delete this.pokemonSprite.pipelineData[`${k}Base`]; if (this.pokemon?.summonData?.speciesForm) { @@ -368,7 +374,7 @@ export default class SummaryUiHandler extends UiHandler { }); this.pokemon.cry(); - this.nameText.setText(this.pokemon.getNameToRender()); + this.nameText.setText(this.pokemon.getNameToRender(false)); const isFusion = this.pokemon.isFusion(); @@ -426,8 +432,8 @@ export default class SummaryUiHandler extends UiHandler { this.friendshipShadow.setCrop(0, 0, 16, 16 - 16 * ((this.pokemon?.friendship || 0) / 255)); - const doubleShiny = isFusion && this.pokemon.shiny && this.pokemon.fusionShiny; - const baseVariant = !doubleShiny ? this.pokemon.getVariant() : this.pokemon.variant; + const doubleShiny = this.pokemon.isDoubleShiny(false); + const baseVariant = this.pokemon.getBaseVariant(doubleShiny); this.shinyIcon.setPositionRelative( this.nameText, @@ -435,7 +441,7 @@ export default class SummaryUiHandler extends UiHandler { 3, ); this.shinyIcon.setTexture(`shiny_star${doubleShiny ? "_1" : ""}`); - this.shinyIcon.setVisible(this.pokemon.isShiny()); + this.shinyIcon.setVisible(this.pokemon.isShiny(false)); this.shinyIcon.setTint(getVariantTint(baseVariant)); if (this.shinyIcon.visible) { const shinyDescriptor = @@ -455,7 +461,9 @@ export default class SummaryUiHandler extends UiHandler { this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y); this.fusionShinyIcon.setVisible(doubleShiny); if (isFusion) { - this.fusionShinyIcon.setTint(getVariantTint(this.pokemon.fusionVariant)); + this.fusionShinyIcon.setTint( + getVariantTint(this.pokemon.summonData?.illusion?.basePokemon.fusionVariant ?? this.pokemon.fusionVariant), + ); } this.pokeball.setFrame(getPokeballAtlasKey(this.pokemon.pokeball)); @@ -838,7 +846,7 @@ export default class SummaryUiHandler extends UiHandler { return typeIcon; }; - const types = this.pokemon?.getTypes(false, false, true)!; // TODO: is this bang correct? + const types = this.pokemon?.getTypes(false, false, true, false)!; // TODO: is this bang correct? profileContainer.add(getTypeIcon(0, types[0])); if (types.length > 1) { profileContainer.add(getTypeIcon(1, types[1])); diff --git a/test/abilities/illusion.test.ts b/test/abilities/illusion.test.ts new file mode 100644 index 00000000000..aa77aa701b2 --- /dev/null +++ b/test/abilities/illusion.test.ts @@ -0,0 +1,144 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#test/testUtils/gameManager"; +import { Species } from "#enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Moves } from "#enums/moves"; +import { Abilities } from "#enums/abilities"; +import { PokeballType } from "#app/enums/pokeball"; +import { Gender } from "#app/data/gender"; + +describe("Abilities - Illusion", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override.battleType("single"); + game.override.enemySpecies(Species.ZORUA); + game.override.enemyAbility(Abilities.ILLUSION); + game.override.enemyMoveset(Moves.TACKLE); + game.override.enemyHeldItems([{ name: "WIDE_LENS", count: 3 }]); + + game.override.moveset([Moves.WORRY_SEED, Moves.SOAK, Moves.TACKLE]); + game.override.startingHeldItems([{ name: "WIDE_LENS", count: 3 }]); + }); + + it("creates illusion at the start", async () => { + await game.classicMode.startBattle([Species.ZOROARK, Species.AXEW]); + const zoroark = game.scene.getPlayerPokemon()!; + const zorua = game.scene.getEnemyPokemon()!; + + expect(!!zoroark.summonData?.illusion).equals(true); + expect(!!zorua.summonData?.illusion).equals(true); + }); + + it("break after receiving damaging move", async () => { + await game.classicMode.startBattle([Species.AXEW]); + game.move.select(Moves.TACKLE); + + await game.phaseInterceptor.to(TurnEndPhase); + + const zorua = game.scene.getEnemyPokemon()!; + + expect(!!zorua.summonData?.illusion).equals(false); + expect(zorua.name).equals("Zorua"); + }); + + it("break after getting ability changed", async () => { + await game.classicMode.startBattle([Species.AXEW]); + game.move.select(Moves.WORRY_SEED); + + await game.phaseInterceptor.to(TurnEndPhase); + + const zorua = game.scene.getEnemyPokemon()!; + + expect(!!zorua.summonData?.illusion).equals(false); + }); + + it("break if the ability is suppressed", async () => { + game.override.enemyAbility(Abilities.NEUTRALIZING_GAS); + await game.classicMode.startBattle([Species.KOFFING]); + + const zorua = game.scene.getEnemyPokemon()!; + + expect(!!zorua.summonData?.illusion).equals(false); + }); + + it("causes enemy AI to consider the illusion's type instead of the actual type when considering move effectiveness", async () => { + game.override.enemyMoveset([Moves.FLAMETHROWER, Moves.PSYCHIC, Moves.TACKLE]); + await game.classicMode.startBattle([Species.ZOROARK, Species.AXEW]); + + const enemy = game.scene.getEnemyPokemon()!; + const zoroark = game.scene.getPlayerPokemon()!; + + const flameThrower = enemy.getMoveset()[0]!.getMove(); + const psychic = enemy.getMoveset()[1]!.getMove(); + const flameThrowerEffectiveness = zoroark.getAttackTypeEffectiveness( + flameThrower.type, + enemy, + undefined, + undefined, + flameThrower, + true, + ); + const psychicEffectiveness = zoroark.getAttackTypeEffectiveness( + psychic.type, + enemy, + undefined, + undefined, + psychic, + true, + ); + expect(psychicEffectiveness).above(flameThrowerEffectiveness); + }); + + it("does not break from indirect damage", async () => { + game.override.enemySpecies(Species.GIGALITH); + game.override.enemyAbility(Abilities.SAND_STREAM); + game.override.enemyMoveset(Moves.WILL_O_WISP); + game.override.moveset([Moves.FLARE_BLITZ]); + + await game.classicMode.startBattle([Species.ZOROARK, Species.AZUMARILL]); + + game.move.select(Moves.FLARE_BLITZ); + + await game.phaseInterceptor.to(TurnEndPhase); + + const zoroark = game.scene.getPlayerPokemon()!; + + expect(!!zoroark.summonData?.illusion).equals(true); + }); + + it("copies the the name, nickname, gender, shininess, and pokeball from the illusion source", async () => { + game.override.enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([Species.ABRA, Species.ZOROARK, Species.AXEW]); + const axew = game.scene.getPlayerParty().at(2)!; + axew.shiny = true; + axew.nickname = btoa(unescape(encodeURIComponent("axew nickname"))); + axew.gender = Gender.FEMALE; + axew.pokeball = PokeballType.GREAT_BALL; + + game.doSwitchPokemon(1); + + await game.phaseInterceptor.to(TurnEndPhase); + + const zoroark = game.scene.getPlayerPokemon()!; + + expect(zoroark.name).equals("Axew"); + expect(zoroark.getNameToRender()).equals("axew nickname"); + expect(zoroark.getGender(false, true)).equals(Gender.FEMALE); + expect(zoroark.isShiny(true)).equals(true); + expect(zoroark.getPokeball(true)).equals(PokeballType.GREAT_BALL); + }); +}); From c82e01eed377cacc5d3ffd2ef9caa27270e8a2dc Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Mon, 14 Apr 2025 10:31:26 -0500 Subject: [PATCH 18/52] [Refactor] Move many interfaces and enums to their own file (#5646) * Move LearnMoveSituation to its own file * Remove unused selfStatLowerMoves array * Move all-moves to its own file * Move TurnMove interface to its own file * move AiType to its own file * Move PokemonMove to its own file * Move DamageCalculationResult interface to its own file * Move fieldPosition to its own file * Move hit-result to its own file * Move DamageResult to its own file * Move SpeciesWildEvolutionDelay to its own file * move EvolutionItem to its own file --- src/@types/damage-result.ts | 10 + src/battle-scene.ts | 2 +- src/battle.ts | 3 +- src/data/ability.ts | 9 +- src/data/arena-tag.ts | 5 +- src/data/balance/egg-moves.ts | 2 +- src/data/balance/pokemon-evolutions.ts | 60 +---- src/data/battle-anims.ts | 3 +- src/data/battler-tags.ts | 5 +- src/data/berry.ts | 2 +- src/data/challenge.ts | 2 +- src/data/moves/all-moves.ts | 3 + src/data/moves/move.ts | 21 +- src/data/moves/pokemon-move.ts | 93 ++++++++ .../encounters/absolute-avarice-encounter.ts | 3 +- .../encounters/bug-type-superfan-encounter.ts | 4 +- .../encounters/clowning-around-encounter.ts | 2 +- .../encounters/dancing-lessons-encounter.ts | 3 +- .../encounters/field-trip-encounter.ts | 3 +- .../encounters/fiery-fallout-encounter.ts | 2 +- .../encounters/fun-and-games-encounter.ts | 2 +- .../global-trade-system-encounter.ts | 3 +- .../encounters/lost-at-sea-encounter.ts | 2 +- .../slumbering-snorlax-encounter.ts | 3 +- .../encounters/the-strong-stuff-encounter.ts | 2 +- .../encounters/trash-to-treasure-encounter.ts | 2 +- .../encounters/uncommon-breed-encounter.ts | 2 +- .../encounters/weird-dream-encounter.ts | 2 +- .../mystery-encounter-requirements.ts | 3 +- .../mystery-encounters/mystery-encounter.ts | 3 +- .../can-learn-move-requirement.ts | 2 +- .../utils/encounter-phase-utils.ts | 7 +- src/data/pokemon-forms.ts | 2 +- src/data/pokemon-species.ts | 7 +- src/data/trainers/trainer-config.ts | 2 +- src/enums/ai-type.ts | 5 + src/enums/evolution-item.ts | 48 ++++ src/enums/field-position.ts | 5 + src/enums/hit-result.ts | 15 ++ src/enums/learn-move-context.ts | 8 + src/enums/species-wild-evolution-delay.ts | 8 + src/field/damage-number-handler.ts | 4 +- src/field/pokemon.ts | 206 ++---------------- src/interfaces/attack-move-result.ts | 12 + src/interfaces/damage-calculation-result.ts | 11 + src/interfaces/turn-move.ts | 12 + src/modifier/modifier-type.ts | 9 +- src/modifier/modifier.ts | 2 +- src/overrides.ts | 2 +- src/phases/command-phase.ts | 5 +- src/phases/damage-anim-phase.ts | 3 +- src/phases/encounter-phase.ts | 2 +- src/phases/evolution-phase.ts | 10 +- src/phases/faint-phase.ts | 7 +- src/phases/learn-move-phase.ts | 2 +- src/phases/move-charge-phase.ts | 2 +- src/phases/move-effect-phase.ts | 5 +- src/phases/move-header-phase.ts | 2 +- src/phases/move-phase.ts | 4 +- src/phases/pokemon-heal-phase.ts | 2 +- src/phases/pokemon-transform-phase.ts | 2 +- src/phases/select-target-phase.ts | 2 +- src/phases/summon-phase.ts | 2 +- src/phases/switch-summon-phase.ts | 3 +- src/phases/toggle-double-position-phase.ts | 2 +- src/phases/turn-start-phase.ts | 5 +- src/phases/weather-effect-phase.ts | 2 +- src/system/game-data.ts | 2 +- src/system/pokemon-data.ts | 3 +- src/ui/fight-ui-handler.ts | 2 +- src/ui/modifier-select-ui-handler.ts | 2 +- src/ui/party-ui-handler.ts | 6 +- src/ui/pokedex-page-ui-handler.ts | 2 +- src/ui/pokedex-scan-ui-handler.ts | 2 +- src/ui/pokedex-ui-handler.ts | 2 +- src/ui/pokemon-hatch-info-container.ts | 2 +- src/ui/starter-select-ui-handler.ts | 2 +- src/ui/summary-ui-handler.ts | 3 +- test/abilities/aura_break.test.ts | 2 +- test/abilities/battery.test.ts | 2 +- test/abilities/battle_bond.test.ts | 3 +- test/abilities/flower_veil.test.ts | 2 +- test/abilities/friend_guard.test.ts | 2 +- test/abilities/galvanize.test.ts | 4 +- test/abilities/hustle.test.ts | 2 +- test/abilities/infiltrator.test.ts | 2 +- test/abilities/libero.test.ts | 2 +- test/abilities/magic_bounce.test.ts | 2 +- test/abilities/power_spot.test.ts | 2 +- test/abilities/protean.test.ts | 2 +- test/abilities/sap_sipper.test.ts | 3 +- test/abilities/serene_grace.test.ts | 2 +- test/abilities/sheer_force.test.ts | 3 +- test/abilities/steely_spirit.test.ts | 2 +- test/abilities/supreme_overlord.test.ts | 2 +- test/abilities/tera_shell.test.ts | 2 +- test/abilities/unburden.test.ts | 3 +- test/abilities/wimp_out.test.ts | 9 +- test/abilities/wonder_skin.test.ts | 2 +- test/arena/arena_gravity.test.ts | 2 +- test/arena/grassy_terrain.test.ts | 2 +- test/arena/weather_fog.test.ts | 2 +- test/arena/weather_strong_winds.test.ts | 2 +- test/battle/damage_calculation.test.ts | 2 +- test/battlerTags/substitute.test.ts | 6 +- test/enemy_command.test.ts | 4 +- test/evolution.test.ts | 7 +- test/imports.test.ts | 2 +- test/items/reviver_seed.test.ts | 2 +- test/moves/astonish.test.ts | 2 +- test/moves/aurora_veil.test.ts | 3 +- test/moves/burning_jealousy.test.ts | 2 +- test/moves/ceaseless_edge.test.ts | 2 +- test/moves/copycat.test.ts | 3 +- test/moves/destiny_bond.test.ts | 2 +- test/moves/diamond_storm.test.ts | 2 +- test/moves/dig.test.ts | 2 +- test/moves/dragon_tail.test.ts | 2 +- test/moves/dynamax_cannon.test.ts | 2 +- test/moves/effectiveness.test.ts | 2 +- test/moves/fell_stinger.test.ts | 2 +- test/moves/fly.test.ts | 2 +- test/moves/freezy_frost.test.ts | 2 +- test/moves/fusion_flare_bolt.test.ts | 2 +- test/moves/glaive_rush.test.ts | 2 +- test/moves/hard_press.test.ts | 2 +- test/moves/hyper_beam.test.ts | 2 +- test/moves/lash_out.test.ts | 2 +- test/moves/last_respects.test.ts | 2 +- test/moves/light_screen.test.ts | 3 +- test/moves/magic_coat.test.ts | 2 +- test/moves/metronome.test.ts | 3 +- test/moves/moongeist_beam.test.ts | 3 +- test/moves/pledge_moves.test.ts | 3 +- test/moves/powder.test.ts | 3 +- test/moves/protect.test.ts | 2 +- test/moves/rage_fist.test.ts | 2 +- test/moves/reflect.test.ts | 3 +- test/moves/retaliate.test.ts | 2 +- test/moves/rollout.test.ts | 2 +- test/moves/round.test.ts | 2 +- test/moves/scale_shot.test.ts | 2 +- test/moves/secret_power.test.ts | 2 +- test/moves/shell_side_arm.test.ts | 3 +- test/moves/shell_trap.test.ts | 2 +- test/moves/sketch.test.ts | 6 +- test/moves/solar_beam.test.ts | 2 +- test/moves/sparkly_swirl.test.ts | 2 +- test/moves/spectral_thief.test.ts | 2 +- test/moves/spit_up.test.ts | 4 +- test/moves/steamroller.test.ts | 4 +- test/moves/stockpile.test.ts | 2 +- test/moves/substitute.test.ts | 3 +- test/moves/swallow.test.ts | 2 +- test/moves/telekinesis.test.ts | 2 +- test/moves/tera_blast.test.ts | 5 +- test/moves/toxic.test.ts | 2 +- test/moves/triple_arrows.test.ts | 3 +- ...an-offer-you-cant-refuse-encounter.test.ts | 3 +- .../bug-type-superfan-encounter.test.ts | 2 +- .../clowning-around-encounter.test.ts | 2 +- .../dancing-lessons-encounter.test.ts | 2 +- .../fight-or-flight-encounter.test.ts | 2 +- .../encounters/part-timer-encounter.test.ts | 2 +- .../the-strong-stuff-encounter.test.ts | 2 +- .../trash-to-treasure-encounter.test.ts | 2 +- .../uncommon-breed-encounter.test.ts | 2 +- test/testUtils/helpers/moveHelper.ts | 2 +- 168 files changed, 490 insertions(+), 455 deletions(-) create mode 100644 src/@types/damage-result.ts create mode 100644 src/data/moves/all-moves.ts create mode 100644 src/data/moves/pokemon-move.ts create mode 100644 src/enums/ai-type.ts create mode 100644 src/enums/evolution-item.ts create mode 100644 src/enums/field-position.ts create mode 100644 src/enums/hit-result.ts create mode 100644 src/enums/learn-move-context.ts create mode 100644 src/enums/species-wild-evolution-delay.ts create mode 100644 src/interfaces/attack-move-result.ts create mode 100644 src/interfaces/damage-calculation-result.ts create mode 100644 src/interfaces/turn-move.ts diff --git a/src/@types/damage-result.ts b/src/@types/damage-result.ts new file mode 100644 index 00000000000..7086d843cf4 --- /dev/null +++ b/src/@types/damage-result.ts @@ -0,0 +1,10 @@ +import type { HitResult } from "#enums/hit-result"; + +export type DamageResult = + | HitResult.EFFECTIVE + | HitResult.SUPER_EFFECTIVE + | HitResult.NOT_VERY_EFFECTIVE + | HitResult.ONE_HIT_KO + | HitResult.CONFUSION + | HitResult.INDIRECT_KO + | HitResult.INDIRECT; diff --git a/src/battle-scene.ts b/src/battle-scene.ts index dd983f2b397..12dbfca68e8 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -51,7 +51,7 @@ import { initGameSpeed } from "#app/system/game-speed"; import { Arena, ArenaBase } from "#app/field/arena"; import { GameData } from "#app/system/game-data"; import { addTextObject, getTextColor, TextStyle } from "#app/ui/text"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "./data/moves/all-moves"; import { MusicPreference } from "#app/system/settings/settings"; import { getDefaultModifierTypeForTier, diff --git a/src/battle.ts b/src/battle.ts index fb5af223b8f..1122db2679a 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -15,7 +15,8 @@ import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/mod import type { PokeballType } from "#enums/pokeball"; import { trainerConfigs } from "#app/data/trainers/trainer-config"; import { SpeciesFormKey } from "#enums/species-form-key"; -import type { EnemyPokemon, PlayerPokemon, TurnMove } from "#app/field/pokemon"; +import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; +import type { TurnMove } from "./interfaces/turn-move"; import type Pokemon from "#app/field/pokemon"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattleSpec } from "#enums/battle-spec"; diff --git a/src/data/ability.ts b/src/data/ability.ts index 3e32a624f9f..02cc12dd0f4 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1,6 +1,8 @@ -import type { EnemyPokemon, PokemonMove } from "../field/pokemon"; +import type { EnemyPokemon } from "../field/pokemon"; +import type { PokemonMove } from "./moves/pokemon-move"; import type Pokemon from "../field/pokemon"; -import { HitResult, MoveResult, PlayerPokemon } from "../field/pokemon"; +import { MoveResult, PlayerPokemon } from "../field/pokemon"; +import { HitResult } from "#enums/hit-result"; import { PokemonType } from "#enums/pokemon-type"; import { BooleanHolder, NumberHolder, toDmgValue, isNullOrUndefined, randSeedItem, randSeedInt, type Constructor } from "#app/utils"; import { getPokemonNameWithAffix } from "../messages"; @@ -10,7 +12,8 @@ import { BattlerTagLapseType, GroundedTag } from "./battler-tags"; import { getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "#app/data/status-effect"; import { Gender } from "./gender"; import type Move from "./moves/move"; -import { AttackMove, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./moves/move"; +import { AttackMove, FlinchAttr, OneHitKOAttr, HitHealAttr, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./moves/move"; +import { allMoves } from "./moves/all-moves"; import { MoveFlags } from "#enums/MoveFlags"; import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 871f622f70a..c6a1515685f 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -2,12 +2,13 @@ import { globalScene } from "#app/global-scene"; import type { Arena } from "#app/field/arena"; import { PokemonType } from "#enums/pokemon-type"; import { BooleanHolder, NumberHolder, toDmgValue } from "#app/utils"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "./moves/all-moves"; import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; import { getPokemonNameWithAffix } from "#app/messages"; import type Pokemon from "#app/field/pokemon"; -import { HitResult, PokemonMove } from "#app/field/pokemon"; +import { HitResult } from "#enums/hit-result"; +import { PokemonMove } from "./moves/pokemon-move"; import { StatusEffect } from "#enums/status-effect"; import type { BattlerIndex } from "#app/battle"; import { diff --git a/src/data/balance/egg-moves.ts b/src/data/balance/egg-moves.ts index 74f6a2c1afb..98f3347764c 100644 --- a/src/data/balance/egg-moves.ts +++ b/src/data/balance/egg-moves.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { getEnumKeys, getEnumValues } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/src/data/balance/pokemon-evolutions.ts b/src/data/balance/pokemon-evolutions.ts index 17f71f3c3c9..70b616be8e5 100644 --- a/src/data/balance/pokemon-evolutions.ts +++ b/src/data/balance/pokemon-evolutions.ts @@ -14,66 +14,10 @@ import { DamageMoneyRewardModifier, ExtraModifierModifier, MoneyMultiplierModifi import { SpeciesFormKey } from "#enums/species-form-key"; import { speciesStarterCosts } from "./starters"; import i18next from "i18next"; +import { SpeciesWildEvolutionDelay } from "#enums/species-wild-evolution-delay"; +import { EvolutionItem } from "#enums/evolution-item"; -export enum SpeciesWildEvolutionDelay { - NONE, - SHORT, - MEDIUM, - LONG, - VERY_LONG, - NEVER -} - -export enum EvolutionItem { - NONE, - - LINKING_CORD, - SUN_STONE, - MOON_STONE, - LEAF_STONE, - FIRE_STONE, - WATER_STONE, - THUNDER_STONE, - ICE_STONE, - DUSK_STONE, - DAWN_STONE, - SHINY_STONE, - CRACKED_POT, - SWEET_APPLE, - TART_APPLE, - STRAWBERRY_SWEET, - UNREMARKABLE_TEACUP, - UPGRADE, - DUBIOUS_DISC, - DRAGON_SCALE, - PRISM_SCALE, - RAZOR_CLAW, - RAZOR_FANG, - REAPER_CLOTH, - ELECTIRIZER, - MAGMARIZER, - PROTECTOR, - SACHET, - WHIPPED_DREAM, - SYRUPY_APPLE, - CHIPPED_POT, - GALARICA_CUFF, - GALARICA_WREATH, - AUSPICIOUS_ARMOR, - MALICIOUS_ARMOR, - MASTERPIECE_TEACUP, - SUN_FLUTE, - MOON_FLUTE, - - BLACK_AUGURITE = 51, - PEAT_BLOCK, - METAL_ALLOY, - SCROLL_OF_DARKNESS, - SCROLL_OF_WATERS, - LEADERS_CREST -} - /** * Pokemon Evolution tuple type consisting of: * @property 0 {@linkcode Species} The species of the Pokemon. diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 511c80bee72..396cf71d984 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -1,5 +1,6 @@ import { globalScene } from "#app/global-scene"; -import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, SelfStatusMove, allMoves } from "./moves/move"; +import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, SelfStatusMove } from "./moves/move"; +import { allMoves } from "./moves/all-moves"; import { MoveFlags } from "#enums/MoveFlags"; import type Pokemon from "../field/pokemon"; import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName } from "../utils"; diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 76e91485460..c3dcfc49ef6 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -11,12 +11,12 @@ import { import { ChargeAnim, CommonAnim, CommonBattleAnim, MoveChargeAnim } from "#app/data/battle-anims"; import type Move from "#app/data/moves/move"; import { - allMoves, applyMoveAttrs, ConsecutiveUseDoublePowerAttr, HealOnAllyAttr, StatusCategoryOnAllyAttr, } from "#app/data/moves/move"; +import { allMoves } from "./moves/all-moves"; import { MoveFlags } from "#enums/MoveFlags"; import { MoveCategory } from "#enums/MoveCategory"; import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms"; @@ -24,7 +24,8 @@ import { getStatusEffectHealText } from "#app/data/status-effect"; import { TerrainType } from "#app/data/terrain"; import { PokemonType } from "#enums/pokemon-type"; import type Pokemon from "#app/field/pokemon"; -import { HitResult, MoveResult } from "#app/field/pokemon"; +import { MoveResult } from "#app/field/pokemon"; +import { HitResult } from "#enums/hit-result"; import { getPokemonNameWithAffix } from "#app/messages"; import { CommonAnimPhase } from "#app/phases/common-anim-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; diff --git a/src/data/berry.ts b/src/data/berry.ts index 8a58d337aa4..aaa0dda6e7f 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -1,6 +1,6 @@ import { getPokemonNameWithAffix } from "../messages"; import type Pokemon from "../field/pokemon"; -import { HitResult } from "../field/pokemon"; +import { HitResult } from "#enums/hit-result"; import { getStatusEffectHealText } from "./status-effect"; import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils"; import { diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 51616c3f00f..9a3c329a70b 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -6,7 +6,7 @@ import type PokemonSpecies from "#app/data/pokemon-species"; import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; import { speciesStarterCosts } from "#app/data/balance/starters"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "./moves/pokemon-move"; import type { FixedBattleConfig } from "#app/battle"; import { ClassicFixedBossWaves, BattleType, getRandomTrainerFunc } from "#app/battle"; import Trainer, { TrainerVariant } from "#app/field/trainer"; diff --git a/src/data/moves/all-moves.ts b/src/data/moves/all-moves.ts new file mode 100644 index 00000000000..c7b6d11a08d --- /dev/null +++ b/src/data/moves/all-moves.ts @@ -0,0 +1,3 @@ +import type Move from "./move"; + +export const allMoves: Move[] = []; diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 962a13bb840..591894f5f1e 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -12,16 +12,17 @@ import { TypeBoostTag, } from "../battler-tags"; import { getPokemonNameWithAffix } from "../../messages"; -import type { AttackMoveResult, TurnMove } from "../../field/pokemon"; +import type { AttackMoveResult } from "#app/interfaces/attack-move-result"; +import type { TurnMove } from "#app/interfaces/turn-move"; import type Pokemon from "../../field/pokemon"; import { EnemyPokemon, - FieldPosition, - HitResult, MoveResult, PlayerPokemon, - PokemonMove, } from "../../field/pokemon"; +import { HitResult } from "#enums/hit-result"; +import { FieldPosition } from "#enums/field-position"; +import { PokemonMove } from "./pokemon-move"; import { getNonVolatileStatusEffects, getStatusEffectHealText, @@ -121,6 +122,7 @@ import { MoveFlags } from "#enums/MoveFlags"; import { MoveEffectTrigger } from "#enums/MoveEffectTrigger"; import { MultiHitType } from "#enums/MultiHitType"; import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves } from "./invalid-moves"; +import { allMoves } from "./all-moves"; type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean; type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean; @@ -8257,11 +8259,7 @@ export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveT return { targets: set.filter(p => p?.isActive(true)).map(p => p.getBattlerIndex()).filter(t => t !== undefined), multiple }; } -export const allMoves: Move[] = [ - new SelfStatusMove(Moves.NONE, PokemonType.NORMAL, MoveCategory.STATUS, -1, -1, 0, 1), -]; - -export const selfStatLowerMoves: Moves[] = []; +allMoves.push(new SelfStatusMove(Moves.NONE, PokemonType.NORMAL, MoveCategory.STATUS, -1, -1, 0, 1)); export function initMoves() { allMoves.push( @@ -11250,9 +11248,4 @@ export function initMoves() { new AttackMove(Moves.MALIGNANT_CHAIN, PokemonType.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9) .attr(StatusEffectAttr, StatusEffect.TOXIC) ); - allMoves.map(m => { - if (m.getAttrs(StatStageChangeAttr).some(a => a.selfTarget && a.stages < 0)) { - selfStatLowerMoves.push(m.id); - } - }); } diff --git a/src/data/moves/pokemon-move.ts b/src/data/moves/pokemon-move.ts new file mode 100644 index 00000000000..49ccaba698b --- /dev/null +++ b/src/data/moves/pokemon-move.ts @@ -0,0 +1,93 @@ +import * as Utils from "#app/utils"; +import { allMoves } from "./all-moves"; +import type { Moves } from "#enums/moves"; +import type Pokemon from "#app/field/pokemon"; +import type Move from "./move"; + +/** + * Wrapper class for the {@linkcode Move} class for Pokemon to interact with. + * These are the moves assigned to a {@linkcode Pokemon} object. + * It links to {@linkcode Move} class via the move ID. + * Compared to {@linkcode Move}, this class also tracks if a move has received. + * PP Ups, amount of PP used, and things like that. + * @see {@linkcode isUsable} - checks if move is restricted, out of PP, or not implemented. + * @see {@linkcode getMove} - returns {@linkcode Move} object by looking it up via ID. + * @see {@linkcode usePp} - removes a point of PP from the move. + * @see {@linkcode getMovePp} - returns amount of PP a move currently has. + * @see {@linkcode getPpRatio} - returns the current PP amount / max PP amount. + * @see {@linkcode getName} - returns name of {@linkcode Move}. + **/ +export class PokemonMove { + public moveId: Moves; + public ppUsed: number; + public ppUp: number; + public virtual: boolean; + + /** + * If defined and nonzero, overrides the maximum PP of the move (e.g., due to move being copied by Transform). + * This also nullifies all effects of `ppUp`. + */ + public maxPpOverride?: number; + + constructor(moveId: Moves, ppUsed = 0, ppUp = 0, virtual = false, maxPpOverride?: number) { + this.moveId = moveId; + this.ppUsed = ppUsed; + this.ppUp = ppUp; + this.virtual = virtual; + this.maxPpOverride = maxPpOverride; + } + + /** + * Checks whether the move can be selected or performed by a Pokemon, without consideration for the move's targets. + * The move is unusable if it is out of PP, restricted by an effect, or unimplemented. + * + * @param pokemon - {@linkcode Pokemon} that would be using this move + * @param ignorePp - If `true`, skips the PP check + * @param ignoreRestrictionTags - If `true`, skips the check for move restriction tags (see {@link MoveRestrictionBattlerTag}) + * @returns `true` if the move can be selected and used by the Pokemon, otherwise `false`. + */ + isUsable(pokemon: Pokemon, ignorePp = false, ignoreRestrictionTags = false): boolean { + if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)) { + return false; + } + + if (this.getMove().name.endsWith(" (N)")) { + return false; + } + + return ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1; + } + + getMove(): Move { + return allMoves[this.moveId]; + } + + /** + * Sets {@link ppUsed} for this move and ensures the value does not exceed {@link getMovePp} + * @param {number} count Amount of PP to use + */ + usePp(count = 1) { + this.ppUsed = Math.min(this.ppUsed + count, this.getMovePp()); + } + + getMovePp(): number { + return this.maxPpOverride || this.getMove().pp + this.ppUp * Utils.toDmgValue(this.getMove().pp / 5); + } + + getPpRatio(): number { + return 1 - this.ppUsed / this.getMovePp(); + } + + getName(): string { + return this.getMove().name; + } + + /** + * Copies an existing move or creates a valid PokemonMove object from json representing one + * @param source - The data for the move to copy + * @return A valid pokemonmove object + */ + static loadMove(source: PokemonMove | any): PokemonMove { + return new PokemonMove(source.moveId, source.ppUsed, source.ppUp, source.virtual, source.maxPpOverride); + } +} diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index 85f40a41e51..b781f14fad1 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -7,7 +7,8 @@ import { transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; +import { EnemyPokemon } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import type { BerryModifierType, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; 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 1e4c9a3b957..c6971c42364 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -24,7 +24,7 @@ import { TrainerType } from "#enums/trainer-type"; import { Species } from "#enums/species"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { LearnMovePhase } from "#app/phases/learn-move-phase"; import { Moves } from "#enums/moves"; @@ -50,7 +50,7 @@ import { } from "#app/modifier/modifier"; import i18next from "i18next"; import MoveInfoOverlay from "#app/ui/move-info-overlay"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index eca99fc0c13..15fad6dacbf 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -37,7 +37,7 @@ import { Mode } from "#app/ui/ui"; import i18next from "i18next"; import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler"; import type { PlayerPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { Ability } from "#app/data/ability"; import { BerryModifier } from "#app/modifier/modifier"; import { BerryType } from "#enums/berry-type"; diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index 75527e1f8c1..90ea6a69c0d 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -23,7 +23,8 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { TrainerSlot } from "#enums/trainer-slot"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; +import { EnemyPokemon } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { modifierTypes } from "#app/modifier/modifier-type"; import { LearnMovePhase } from "#app/phases/learn-move-phase"; diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index a1964aa5ab4..4e330fab3d9 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -7,7 +7,8 @@ import { setEncounterExp, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; +import type { PlayerPokemon } from "#app/field/pokemon"; +import type { PokemonMove } from "#app/data/moves/pokemon-move"; import { modifierTypes } from "#app/modifier/modifier-type"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 6118fe3d0de..d868184a7fa 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -26,7 +26,7 @@ import { Gender } from "#app/data/gender"; import { PokemonType } from "#enums/pokemon-type"; import { BattlerIndex } from "#app/battle"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { Moves } from "#enums/moves"; import { EncounterBattleAnim } from "#app/data/battle-anims"; import { WeatherType } from "#enums/weather-type"; 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 282c6c149ff..a9fc24c70b7 100644 --- a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts +++ b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts @@ -13,7 +13,7 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst import { TrainerSlot } from "#enums/trainer-slot"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { FieldPosition } from "#app/field/pokemon"; +import { FieldPosition } from "#enums/field-position"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; diff --git a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts index f80620647b0..fce496e5e17 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -26,7 +26,8 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode import { NumberHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle, randSeedItem } from "#app/utils"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; +import { EnemyPokemon } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import type { PokemonHeldItemModifier } from "#app/modifier/modifier"; import { HiddenAbilityRateBoosterModifier, diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 97fd5783ebb..030678b77b1 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -11,7 +11,7 @@ import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encount import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; const OPTION_1_REQUIRED_MOVE = Moves.SURF; const OPTION_2_REQUIRED_MOVE = Moves.FLY; diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index bfa1204a8ba..97a17af43d0 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -21,7 +21,8 @@ import { import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { Moves } from "#enums/moves"; import { BattlerIndex } from "#app/battle"; -import { AiType, PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { AiType } from "#enums/ai-type"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; 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 c994c6e993f..af0363f37e3 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -17,7 +17,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Species } from "#enums/species"; import { Nature } from "#enums/nature"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { Moves } from "#enums/moves"; diff --git a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts index e60fe0ddc18..2203ac041f8 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -25,7 +25,7 @@ import { ModifierTier } from "#app/modifier/modifier-tier"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Moves } from "#enums/moves"; import { BattlerIndex } from "#app/battle"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { randSeedInt } from "#app/utils"; diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index ed1866c7a1b..4e3c238aeba 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -10,7 +10,7 @@ import { import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import type Pokemon from "#app/field/pokemon"; import type { EnemyPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { globalScene } from "#app/global-scene"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 22ec52e976c..be0c0bdff54 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -16,7 +16,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { NumberHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils"; import type PokemonSpecies from "#app/data/pokemon-species"; import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species"; diff --git a/src/data/mystery-encounters/mystery-encounter-requirements.ts b/src/data/mystery-encounters/mystery-encounter-requirements.ts index f9aedf2c1a7..0c146fe485d 100644 --- a/src/data/mystery-encounters/mystery-encounter-requirements.ts +++ b/src/data/mystery-encounters/mystery-encounter-requirements.ts @@ -1,6 +1,7 @@ import { globalScene } from "#app/global-scene"; import { allAbilities } from "#app/data/ability"; -import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; +import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; +import { EvolutionItem } from "#enums/evolution-item"; import { Nature } from "#enums/nature"; import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms"; import { StatusEffect } from "#enums/status-effect"; diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index ff098d4d7dd..8010983f9f3 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -1,5 +1,6 @@ import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; +import type { PlayerPokemon } from "#app/field/pokemon"; +import type { PokemonMove } from "../moves/pokemon-move"; import type Pokemon from "#app/field/pokemon"; import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; diff --git a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts index a7ffe3e26ca..598b0ffae70 100644 --- a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts +++ b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts @@ -1,6 +1,6 @@ import type { Moves } from "#app/enums/moves"; import type { PlayerPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { isNullOrUndefined } from "#app/utils"; import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { globalScene } from "#app/global-scene"; diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index a9f6b787878..6ab650d5f9b 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -7,9 +7,12 @@ import { WEIGHT_INCREMENT_ON_SPAWN_MISS, } from "#app/data/mystery-encounters/mystery-encounters"; import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import type { AiType, PlayerPokemon } from "#app/field/pokemon"; +import type { PlayerPokemon } from "#app/field/pokemon"; +import type { AiType } from "#enums/ai-type"; import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon, FieldPosition, PokemonMove, PokemonSummonData } from "#app/field/pokemon"; +import { EnemyPokemon, PokemonSummonData } from "#app/field/pokemon"; +import { FieldPosition } from "#enums/field-position"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import type { CustomModifierSettings, ModifierType } from "#app/modifier/modifier-type"; import { getPartyLuckValue, diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index 63e166c7fc4..6f36bfde74f 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -1,7 +1,7 @@ import { PokemonFormChangeItemModifier } from "../modifier/modifier"; import type Pokemon from "../field/pokemon"; import { StatusEffect } from "#enums/status-effect"; -import { allMoves } from "./moves/move"; +import { allMoves } from "./moves/all-moves"; import { MoveCategory } from "#enums/MoveCategory"; import type { Constructor, nil } from "#app/utils"; import { Abilities } from "#enums/abilities"; diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index a27c00121dc..ced828fbc6b 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -13,11 +13,8 @@ import { uncatchableSpecies } from "#app/data/balance/biomes"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { GrowthRate } from "#app/data/exp"; import type { EvolutionLevel } from "#app/data/balance/pokemon-evolutions"; -import { - SpeciesWildEvolutionDelay, - pokemonEvolutions, - pokemonPrevolutions, -} from "#app/data/balance/pokemon-evolutions"; +import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; +import { SpeciesWildEvolutionDelay } from "#enums/species-wild-evolution-delay"; import { PokemonType } from "#enums/pokemon-type"; import type { LevelMoves } from "#app/data/balance/pokemon-level-moves"; import { diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index 0ab7119dab9..c87f72bd912 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { toReadableString, isNullOrUndefined, randSeedItem, randSeedInt } from "#app/utils"; import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { getPokemonSpecies } from "#app/data/pokemon-species"; diff --git a/src/enums/ai-type.ts b/src/enums/ai-type.ts new file mode 100644 index 00000000000..13931172a4a --- /dev/null +++ b/src/enums/ai-type.ts @@ -0,0 +1,5 @@ +export enum AiType { + RANDOM, + SMART_RANDOM, + SMART +} diff --git a/src/enums/evolution-item.ts b/src/enums/evolution-item.ts new file mode 100644 index 00000000000..3b5e493b378 --- /dev/null +++ b/src/enums/evolution-item.ts @@ -0,0 +1,48 @@ +export enum EvolutionItem { + NONE, + + LINKING_CORD, + SUN_STONE, + MOON_STONE, + LEAF_STONE, + FIRE_STONE, + WATER_STONE, + THUNDER_STONE, + ICE_STONE, + DUSK_STONE, + DAWN_STONE, + SHINY_STONE, + CRACKED_POT, + SWEET_APPLE, + TART_APPLE, + STRAWBERRY_SWEET, + UNREMARKABLE_TEACUP, + UPGRADE, + DUBIOUS_DISC, + DRAGON_SCALE, + PRISM_SCALE, + RAZOR_CLAW, + RAZOR_FANG, + REAPER_CLOTH, + ELECTIRIZER, + MAGMARIZER, + PROTECTOR, + SACHET, + WHIPPED_DREAM, + SYRUPY_APPLE, + CHIPPED_POT, + GALARICA_CUFF, + GALARICA_WREATH, + AUSPICIOUS_ARMOR, + MALICIOUS_ARMOR, + MASTERPIECE_TEACUP, + SUN_FLUTE, + MOON_FLUTE, + + BLACK_AUGURITE = 51, + PEAT_BLOCK, + METAL_ALLOY, + SCROLL_OF_DARKNESS, + SCROLL_OF_WATERS, + LEADERS_CREST +} diff --git a/src/enums/field-position.ts b/src/enums/field-position.ts new file mode 100644 index 00000000000..5b7f9c6c570 --- /dev/null +++ b/src/enums/field-position.ts @@ -0,0 +1,5 @@ +export enum FieldPosition { + CENTER, + LEFT, + RIGHT +} diff --git a/src/enums/hit-result.ts b/src/enums/hit-result.ts new file mode 100644 index 00000000000..3e62587dd6c --- /dev/null +++ b/src/enums/hit-result.ts @@ -0,0 +1,15 @@ +export enum HitResult { + EFFECTIVE = 1, + SUPER_EFFECTIVE, + NOT_VERY_EFFECTIVE, + ONE_HIT_KO, + NO_EFFECT, + STATUS, + HEAL, + FAIL, + MISS, + INDIRECT, + IMMUNE, + CONFUSION, + INDIRECT_KO +} diff --git a/src/enums/learn-move-context.ts b/src/enums/learn-move-context.ts new file mode 100644 index 00000000000..26001cbcce8 --- /dev/null +++ b/src/enums/learn-move-context.ts @@ -0,0 +1,8 @@ +export enum LearnMoveContext { + MISC, + LEVEL_UP, + RELEARN, + EVOLUTION, + EVOLUTION_FUSED, // If fusionSpecies has Evolved + EVOLUTION_FUSED_BASE, // If fusion's base species has Evolved +} diff --git a/src/enums/species-wild-evolution-delay.ts b/src/enums/species-wild-evolution-delay.ts new file mode 100644 index 00000000000..7555dc0e8f6 --- /dev/null +++ b/src/enums/species-wild-evolution-delay.ts @@ -0,0 +1,8 @@ +export enum SpeciesWildEvolutionDelay { + NONE, + SHORT, + MEDIUM, + LONG, + VERY_LONG, + NEVER +} diff --git a/src/field/damage-number-handler.ts b/src/field/damage-number-handler.ts index a527b148fff..3bb001bf005 100644 --- a/src/field/damage-number-handler.ts +++ b/src/field/damage-number-handler.ts @@ -1,7 +1,7 @@ import { TextStyle, addTextObject } from "../ui/text"; -import type { DamageResult } from "./pokemon"; +import type { DamageResult } from "#app/@types/damage-result"; import type Pokemon from "./pokemon"; -import { HitResult } from "./pokemon"; +import { HitResult } from "#enums/hit-result"; import { formatStat, fixedInt } from "#app/utils"; import type { BattlerIndex } from "../battle"; import { globalScene } from "#app/global-scene"; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index b59b7ba01fe..162a5118f65 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -17,7 +17,6 @@ import { applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, - allMoves, TypelessAttr, CritOnlyAttr, getMoveTargets, @@ -42,6 +41,7 @@ import { VariableMoveTypeChartAttr, HpSplitAttr, } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; import type { PokemonSpeciesForm } from "#app/data/pokemon-species"; @@ -214,7 +214,7 @@ import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeLapseTeraTrigger, SpeciesFormChangeMoveLearnedTrigger, - SpeciesFormChangePostMoveTrigger + SpeciesFormChangePostMoveTrigger, } from "#app/data/pokemon-forms"; import { TerrainType } from "#app/data/terrain"; import type { TrainerSlot } from "#enums/trainer-slot"; @@ -259,21 +259,15 @@ 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, - LEVEL_UP, - RELEARN, - EVOLUTION, - EVOLUTION_FUSED, // If fusionSpecies has Evolved - EVOLUTION_FUSED_BASE, // If fusion's base species has Evolved -} - -export enum FieldPosition { - CENTER, - LEFT, - RIGHT, -} +import { LearnMoveContext } from "#enums/learn-move-context"; +import { TurnMove } from "#app/interfaces/turn-move"; +import { AiType } from "#enums/ai-type"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { DamageCalculationResult } from "#app/interfaces/damage-calculation-result"; +import { FieldPosition } from "#enums/field-position"; +import { AttackMoveResult } from "#app/interfaces/attack-move-result"; +import { HitResult } from "#enums/hit-result"; +import { DamageResult } from "#app/@types/damage-result"; export default abstract class Pokemon extends Phaser.GameObjects.Container { public id: number; @@ -2925,7 +2919,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { includeEvolutionMoves = false, simulateEvolutionChain = false, includeRelearnerMoves = false, - learnSituation: LearnMoveSituation = LearnMoveSituation.MISC, + learnSituation: LearnMoveContext = LearnMoveContext.MISC, ): LevelMoves { const ret: LevelMoves = []; let levelMoves: LevelMoves = []; @@ -2933,7 +2927,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { startingLevel = this.level; } if ( - learnSituation === LearnMoveSituation.EVOLUTION_FUSED && + learnSituation === LearnMoveContext.EVOLUTION_FUSED && this.fusionSpecies ) { // For fusion evolutions, get ONLY the moves of the component mon that evolved @@ -2985,7 +2979,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if ( this.fusionSpecies && - learnSituation !== LearnMoveSituation.EVOLUTION_FUSED_BASE + learnSituation !== LearnMoveContext.EVOLUTION_FUSED_BASE ) { // For fusion evolutions, get ONLY the moves of the component mon that evolved if (simulateEvolutionChain) { @@ -7779,24 +7773,6 @@ interface IllusionData { level?: number } -export interface TurnMove { - move: Moves; - targets: BattlerIndex[]; - result?: MoveResult; - virtual?: boolean; - turn?: number; - ignorePP?: boolean; -} - -export interface AttackMoveResult { - move: Moves; - result: DamageResult; - damage: number; - critical: boolean; - sourceId: number; - sourceBattlerIndex: BattlerIndex; -} - export class PokemonSummonData { /** [Atk, Def, SpAtk, SpDef, Spd, Acc, Eva] */ public statStages: number[] = [0, 0, 0, 0, 0, 0, 0]; @@ -7869,12 +7845,6 @@ export class PokemonTurnData { public extraTurns = 0; } -export enum AiType { - RANDOM, - SMART_RANDOM, - SMART, -} - export enum MoveResult { PENDING, SUCCESS, @@ -7882,151 +7852,3 @@ export enum MoveResult { MISS, OTHER, } - -export enum HitResult { - EFFECTIVE = 1, - SUPER_EFFECTIVE, - NOT_VERY_EFFECTIVE, - ONE_HIT_KO, - NO_EFFECT, - STATUS, - HEAL, - FAIL, - MISS, - INDIRECT, - IMMUNE, - CONFUSION, - INDIRECT_KO, -} - -export type DamageResult = - | HitResult.EFFECTIVE - | HitResult.SUPER_EFFECTIVE - | HitResult.NOT_VERY_EFFECTIVE - | HitResult.ONE_HIT_KO - | HitResult.CONFUSION - | HitResult.INDIRECT_KO - | HitResult.INDIRECT; - -/** Interface containing the results of a damage calculation for a given move */ -export interface DamageCalculationResult { - /** `true` if the move was cancelled (thus suppressing "No Effect" messages) */ - cancelled: boolean; - /** The effectiveness of the move */ - result: HitResult; - /** The damage dealt by the move */ - damage: number; -} - -/** - * Wrapper class for the {@linkcode Move} class for Pokemon to interact with. - * These are the moves assigned to a {@linkcode Pokemon} object. - * It links to {@linkcode Move} class via the move ID. - * Compared to {@linkcode Move}, this class also tracks if a move has received. - * PP Ups, amount of PP used, and things like that. - * @see {@linkcode isUsable} - checks if move is restricted, out of PP, or not implemented. - * @see {@linkcode getMove} - returns {@linkcode Move} object by looking it up via ID. - * @see {@linkcode usePp} - removes a point of PP from the move. - * @see {@linkcode getMovePp} - returns amount of PP a move currently has. - * @see {@linkcode getPpRatio} - returns the current PP amount / max PP amount. - * @see {@linkcode getName} - returns name of {@linkcode Move}. - **/ -export class PokemonMove { - public moveId: Moves; - public ppUsed: number; - public ppUp: number; - public virtual: boolean; - - /** - * If defined and nonzero, overrides the maximum PP of the move (e.g., due to move being copied by Transform). - * This also nullifies all effects of `ppUp`. - */ - public maxPpOverride?: number; - - constructor( - moveId: Moves, - ppUsed = 0, - ppUp = 0, - virtual = false, - maxPpOverride?: number, - ) { - this.moveId = moveId; - this.ppUsed = ppUsed; - this.ppUp = ppUp; - this.virtual = virtual; - this.maxPpOverride = maxPpOverride; - } - - /** - * Checks whether the move can be selected or performed by a Pokemon, without consideration for the move's targets. - * The move is unusable if it is out of PP, restricted by an effect, or unimplemented. - * - * @param {Pokemon} pokemon {@linkcode Pokemon} that would be using this move - * @param {boolean} ignorePp If `true`, skips the PP check - * @param {boolean} ignoreRestrictionTags If `true`, skips the check for move restriction tags (see {@link MoveRestrictionBattlerTag}) - * @returns `true` if the move can be selected and used by the Pokemon, otherwise `false`. - */ - isUsable( - pokemon: Pokemon, - ignorePp = false, - ignoreRestrictionTags = false, - ): boolean { - if ( - this.moveId && - !ignoreRestrictionTags && - pokemon.isMoveRestricted(this.moveId, pokemon) - ) { - return false; - } - - if (this.getMove().name.endsWith(" (N)")) { - return false; - } - - return ( - ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1 - ); - } - - getMove(): Move { - return allMoves[this.moveId]; - } - - /** - * Sets {@link ppUsed} for this move and ensures the value does not exceed {@link getMovePp} - * @param {number} count Amount of PP to use - */ - usePp(count = 1) { - this.ppUsed = Math.min(this.ppUsed + count, this.getMovePp()); - } - - getMovePp(): number { - return ( - this.maxPpOverride || - this.getMove().pp + this.ppUp * toDmgValue(this.getMove().pp / 5) - ); - } - - getPpRatio(): number { - return 1 - this.ppUsed / this.getMovePp(); - } - - getName(): string { - return this.getMove().name; - } - - /** - * Copies an existing move or creates a valid PokemonMove object from json representing one - * @param {PokemonMove | any} source The data for the move to copy - * @return {PokemonMove} A valid pokemonmove object - */ - static loadMove(source: PokemonMove | any): PokemonMove { - return new PokemonMove( - source.moveId, - source.ppUsed, - source.ppUp, - source.virtual, - source.maxPpOverride, - ); - } -} diff --git a/src/interfaces/attack-move-result.ts b/src/interfaces/attack-move-result.ts new file mode 100644 index 00000000000..f91d31a69ee --- /dev/null +++ b/src/interfaces/attack-move-result.ts @@ -0,0 +1,12 @@ +import type { BattlerIndex } from "#app/battle"; +import type { DamageResult } from "#app/@types/damage-result"; +import type { Moves } from "#enums/moves"; + +export interface AttackMoveResult { + move: Moves; + result: DamageResult; + damage: number; + critical: boolean; + sourceId: number; + sourceBattlerIndex: BattlerIndex; +} diff --git a/src/interfaces/damage-calculation-result.ts b/src/interfaces/damage-calculation-result.ts new file mode 100644 index 00000000000..1220ff7b57d --- /dev/null +++ b/src/interfaces/damage-calculation-result.ts @@ -0,0 +1,11 @@ +import type { HitResult } from "#enums/hit-result"; + +/** Interface containing the results of a damage calculation for a given move */ +export interface DamageCalculationResult { + /** `true` if the move was cancelled (thus suppressing "No Effect" messages) */ + cancelled: boolean; + /** The effectiveness of the move */ + result: HitResult; + /** The damage dealt by the move */ + damage: number; +} diff --git a/src/interfaces/turn-move.ts b/src/interfaces/turn-move.ts new file mode 100644 index 00000000000..639d309256e --- /dev/null +++ b/src/interfaces/turn-move.ts @@ -0,0 +1,12 @@ +import type { BattlerIndex } from "#app/battle"; +import type { MoveResult } from "#app/field/pokemon"; +import type { Moves } from "#enums/moves"; + +export interface TurnMove { + move: Moves; + targets: BattlerIndex[]; + result?: MoveResult; + virtual?: boolean; + turn?: number; + ignorePP?: boolean; +} diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 8feb60c7778..852593d922c 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -1,8 +1,10 @@ import { globalScene } from "#app/global-scene"; -import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; +import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; +import { EvolutionItem } from "#enums/evolution-item"; import { tmPoolTiers, tmSpecies } from "#app/data/balance/tms"; import { getBerryEffectDescription, getBerryName } from "#app/data/berry"; -import { allMoves, AttackMove } from "#app/data/moves/move"; +import { AttackMove } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { getNatureName, getNatureStatMultiplier } from "#app/data/nature"; import { getPokeballCatchMultiplier, getPokeballName, MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball"; import { @@ -13,7 +15,8 @@ import { } from "#app/data/pokemon-forms"; import { getStatusEffectDescriptor } from "#app/data/status-effect"; import { PokemonType } from "#enums/pokemon-type"; -import type { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon"; +import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; +import type { PokemonMove } from "#app/data/moves/pokemon-move"; import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 80f14ba22ce..7860d0f9296 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1,7 +1,7 @@ import { FusionSpeciesFormEvolution, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry"; import { getLevelTotalExp } from "#app/data/exp"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball"; import { type FormChangeItem, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms"; import { getStatusEffectHealText } from "#app/data/status-effect"; diff --git a/src/overrides.ts b/src/overrides.ts index 21c72cd7b98..49efb5eed33 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -1,5 +1,5 @@ import { type PokeballCounts } from "#app/battle-scene"; -import { EvolutionItem } from "#app/data/balance/pokemon-evolutions"; +import { EvolutionItem } from "#enums/evolution-item"; import { Gender } from "#app/data/gender"; import { FormChangeItem } from "#app/data/pokemon-forms"; import { Variant } from "#app/sprites/variant"; diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 8691ac453ca..c65f121d20e 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -11,8 +11,9 @@ import { BattlerTagType } from "#app/enums/battler-tag-type"; import { Biome } from "#app/enums/biome"; import { Moves } from "#app/enums/moves"; import { PokeballType } from "#enums/pokeball"; -import type { PlayerPokemon, TurnMove } from "#app/field/pokemon"; -import { FieldPosition } from "#app/field/pokemon"; +import type { PlayerPokemon } from "#app/field/pokemon"; +import type { TurnMove } from "#app/interfaces/turn-move"; +import { FieldPosition } from "#enums/field-position"; import { getPokemonNameWithAffix } from "#app/messages"; import { Command } from "#app/ui/command-ui-handler"; import { Mode } from "#app/ui/ui"; diff --git a/src/phases/damage-anim-phase.ts b/src/phases/damage-anim-phase.ts index 696a2e55b6f..91b21376515 100644 --- a/src/phases/damage-anim-phase.ts +++ b/src/phases/damage-anim-phase.ts @@ -1,7 +1,8 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#app/battle"; import { BattleSpec } from "#enums/battle-spec"; -import { type DamageResult, HitResult } from "#app/field/pokemon"; +import type { DamageResult } from "#app/@types/damage-result"; +import { HitResult } from "#enums/hit-result"; import { fixedInt } from "#app/utils"; import { PokemonPhase } from "#app/phases/pokemon-phase"; diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index 15f3d102e41..9e28de32c4a 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -11,7 +11,7 @@ import { TrainerSlot } from "#enums/trainer-slot"; import { getRandomWeatherType } from "#app/data/weather"; import { EncounterPhaseEvent } from "#app/events/battle-scene"; import type Pokemon from "#app/field/pokemon"; -import { FieldPosition } from "#app/field/pokemon"; +import { FieldPosition } from "#enums/field-position"; import { getPokemonNameWithAffix } from "#app/messages"; import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier"; import { ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; diff --git a/src/phases/evolution-phase.ts b/src/phases/evolution-phase.ts index 203c7542eff..076b7dec80d 100644 --- a/src/phases/evolution-phase.ts +++ b/src/phases/evolution-phase.ts @@ -10,7 +10,7 @@ import { Mode } from "#app/ui/ui"; import { cos, sin } from "#app/field/anims"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { LearnMoveSituation } from "#app/field/pokemon"; +import { LearnMoveContext } from "#enums/learn-move-context"; import { getTypeRgb } from "#app/data/type"; import i18next from "i18next"; import { getPokemonNameWithAffix } from "#app/messages"; @@ -343,11 +343,11 @@ export class EvolutionPhase extends Phase { this.evolutionHandler.canCancel = false; this.pokemon.evolve(this.evolution, this.pokemon.species).then(() => { - const learnSituation: LearnMoveSituation = this.fusionSpeciesEvolved - ? LearnMoveSituation.EVOLUTION_FUSED + const learnSituation: LearnMoveContext = this.fusionSpeciesEvolved + ? LearnMoveContext.EVOLUTION_FUSED : this.pokemon.fusionSpecies - ? LearnMoveSituation.EVOLUTION_FUSED_BASE - : LearnMoveSituation.EVOLUTION; + ? LearnMoveContext.EVOLUTION_FUSED_BASE + : LearnMoveContext.EVOLUTION; const levelMoves = this.pokemon .getLevelMoves(this.lastLevel + 1, true, false, false, learnSituation) .filter(lm => lm[0] === EVOLVE_MOVE); diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 7e1ae4ec07b..4c418679047 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -12,13 +12,16 @@ import { import type { DestinyBondTag, GrudgeTag } from "#app/data/battler-tags"; import { BattlerTagLapseType } from "#app/data/battler-tags"; import { battleSpecDialogue } from "#app/data/dialogue"; -import { allMoves, PostVictoryStatStageChangeAttr } from "#app/data/moves/move"; +import { PostVictoryStatStageChangeAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms"; 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 { PlayerPokemon } from "#app/field/pokemon"; +import { HitResult } from "#enums/hit-result"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { getPokemonNameWithAffix } from "#app/messages"; import { PokemonInstantReviveModifier } from "#app/modifier/modifier"; import { SwitchType } from "#enums/switch-type"; diff --git a/src/phases/learn-move-phase.ts b/src/phases/learn-move-phase.ts index 4107a9cf087..a939298f620 100644 --- a/src/phases/learn-move-phase.ts +++ b/src/phases/learn-move-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; import type Move from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms"; import { Moves } from "#enums/moves"; import { getPokemonNameWithAffix } from "#app/messages"; diff --git a/src/phases/move-charge-phase.ts b/src/phases/move-charge-phase.ts index 26ad85bbe03..ccaf6d054b9 100644 --- a/src/phases/move-charge-phase.ts +++ b/src/phases/move-charge-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#app/battle"; import { MoveChargeAnim } from "#app/data/battle-anims"; import { applyMoveChargeAttrs, MoveEffectAttr, InstantChargeAttr } from "#app/data/moves/move"; -import type { PokemonMove } from "#app/field/pokemon"; +import type { PokemonMove } from "#app/data/moves/pokemon-move"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import { BooleanHolder } from "#app/utils"; diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index acc7ac0f63a..c13c411be68 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -49,9 +49,10 @@ import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms"; import { PokemonType } from "#enums/pokemon-type"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import type Pokemon from "#app/field/pokemon"; -import { HitResult, MoveResult } from "#app/field/pokemon"; +import { MoveResult } from "#app/field/pokemon"; +import { HitResult } from "#enums/hit-result"; import { getPokemonNameWithAffix } from "#app/messages"; import { ContactHeldItemTransferChanceModifier, diff --git a/src/phases/move-header-phase.ts b/src/phases/move-header-phase.ts index c320df462d1..c255b45190b 100644 --- a/src/phases/move-header-phase.ts +++ b/src/phases/move-header-phase.ts @@ -1,5 +1,5 @@ import { applyMoveAttrs, MoveHeaderAttr } from "#app/data/moves/move"; -import type { PokemonMove } from "#app/field/pokemon"; +import type { PokemonMove } from "#app/data/moves/pokemon-move"; import type Pokemon from "#app/field/pokemon"; import { BattlePhase } from "./battle-phase"; diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 478229dcae8..032ac6d06ab 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -16,7 +16,6 @@ import { CommonAnim } from "#app/data/battle-anims"; import { BattlerTagLapseType, CenterOfAttentionTag } from "#app/data/battler-tags"; import { AddArenaTrapTagAttr, - allMoves, applyMoveAttrs, BypassRedirectAttr, BypassSleepAttr, @@ -27,13 +26,14 @@ import { PreMoveMessageAttr, PreUseInterruptAttr, } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { MoveFlags } from "#enums/MoveFlags"; import { SpeciesFormChangePreMoveTrigger } from "#app/data/pokemon-forms"; import { getStatusEffectActivationText, getStatusEffectHealText } from "#app/data/status-effect"; import { PokemonType } from "#enums/pokemon-type"; import { getTerrainBlockMessage, getWeatherBlockMessage } from "#app/data/weather"; import { MoveUsedEvent } from "#app/events/battle-scene"; -import type { PokemonMove } from "#app/field/pokemon"; +import type { PokemonMove } from "#app/data/moves/pokemon-move"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; diff --git a/src/phases/pokemon-heal-phase.ts b/src/phases/pokemon-heal-phase.ts index 651c625b23a..84dc8a5e116 100644 --- a/src/phases/pokemon-heal-phase.ts +++ b/src/phases/pokemon-heal-phase.ts @@ -3,7 +3,7 @@ import type { BattlerIndex } from "#app/battle"; import { CommonAnim } from "#app/data/battle-anims"; import { getStatusEffectHealText } from "#app/data/status-effect"; import { StatusEffect } from "#app/enums/status-effect"; -import { HitResult } from "#app/field/pokemon"; +import { HitResult } from "#enums/hit-result"; import { getPokemonNameWithAffix } from "#app/messages"; import { HealingBoosterModifier } from "#app/modifier/modifier"; import { HealAchv } from "#app/system/achv"; diff --git a/src/phases/pokemon-transform-phase.ts b/src/phases/pokemon-transform-phase.ts index b33689321b5..fb9a28a5a26 100644 --- a/src/phases/pokemon-transform-phase.ts +++ b/src/phases/pokemon-transform-phase.ts @@ -2,7 +2,7 @@ import type { BattlerIndex } from "#app/battle"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { EFFECTIVE_STATS, BATTLE_STATS } from "#enums/stat"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { globalScene } from "#app/global-scene"; import { PokemonPhase } from "./pokemon-phase"; import { getPokemonNameWithAffix } from "#app/messages"; diff --git a/src/phases/select-target-phase.ts b/src/phases/select-target-phase.ts index 035eaff41fa..edd56ba60ed 100644 --- a/src/phases/select-target-phase.ts +++ b/src/phases/select-target-phase.ts @@ -5,7 +5,7 @@ import { Mode } from "#app/ui/ui"; import { CommandPhase } from "./command-phase"; import { PokemonPhase } from "./pokemon-phase"; import i18next from "#app/plugins/i18n"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; export class SelectTargetPhase extends PokemonPhase { // biome-ignore lint/complexity/noUselessConstructor: This makes `fieldIndex` required diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index 7379d509e55..e053b18e4d7 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -5,7 +5,7 @@ import { TrainerSlot } from "#enums/trainer-slot"; import { PlayerGender } from "#app/enums/player-gender"; import { addPokeballOpenParticles } from "#app/field/anims"; import type Pokemon from "#app/field/pokemon"; -import { FieldPosition } from "#app/field/pokemon"; +import { FieldPosition } from "#enums/field-position"; import { getPokemonNameWithAffix } from "#app/messages"; import i18next from "i18next"; import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase"; diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index d63cdb90f25..f39a3e62bb6 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -6,7 +6,8 @@ import { PreSummonAbAttr, PreSwitchOutAbAttr, } from "#app/data/ability"; -import { allMoves, ForceSwitchOutAttr } from "#app/data/moves/move"; +import { ForceSwitchOutAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { getPokeballTintColor } from "#app/data/pokeball"; import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms"; import { TrainerSlot } from "#enums/trainer-slot"; diff --git a/src/phases/toggle-double-position-phase.ts b/src/phases/toggle-double-position-phase.ts index 37f47d5cf95..c4766f888aa 100644 --- a/src/phases/toggle-double-position-phase.ts +++ b/src/phases/toggle-double-position-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { FieldPosition } from "#app/field/pokemon"; +import { FieldPosition } from "#enums/field-position"; import { BattlePhase } from "./battle-phase"; export class ToggleDoublePositionPhase extends BattlePhase { diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index d5b4160fe1b..5941e0af163 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -1,9 +1,10 @@ import { applyAbAttrs, BypassSpeedChanceAbAttr, PreventBypassSpeedChanceAbAttr } from "#app/data/ability"; -import { allMoves, MoveHeaderAttr } from "#app/data/moves/move"; +import { MoveHeaderAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { Stat } from "#app/enums/stat"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { BypassSpeedChanceModifier } from "#app/modifier/modifier"; import { Command } from "#app/ui/command-ui-handler"; import { randSeedShuffle, BooleanHolder } from "#app/utils"; diff --git a/src/phases/weather-effect-phase.ts b/src/phases/weather-effect-phase.ts index 5284c9fba85..256894457fc 100644 --- a/src/phases/weather-effect-phase.ts +++ b/src/phases/weather-effect-phase.ts @@ -14,7 +14,7 @@ import { getWeatherDamageMessage, getWeatherLapseMessage } from "#app/data/weath import { BattlerTagType } from "#app/enums/battler-tag-type"; import { WeatherType } from "#app/enums/weather-type"; import type Pokemon from "#app/field/pokemon"; -import { HitResult } from "#app/field/pokemon"; +import { HitResult } from "#enums/hit-result"; import { BooleanHolder, toDmgValue } from "#app/utils"; import { CommonAnimPhase } from "./common-anim-phase"; diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 53146301666..e87c735f459 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -30,7 +30,7 @@ import { Nature } from "#enums/nature"; import { GameStats } from "#app/system/game-stats"; import { Tutorial } from "#app/tutorial"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { TrainerVariant } from "#app/field/trainer"; import type { Variant } from "#app/sprites/variant"; import { setSettingGamepad, SettingGamepad, settingGamepadDefaults } from "#app/system/settings/settings-gamepad"; diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 97ce494a43a..7579fc3b78d 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -5,7 +5,8 @@ import type { Nature } from "#enums/nature"; import type { PokeballType } from "#enums/pokeball"; import { getPokemonSpecies, getPokemonSpeciesForm } from "../data/pokemon-species"; import { Status } from "../data/status-effect"; -import Pokemon, { EnemyPokemon, PokemonMove, PokemonSummonData } from "../field/pokemon"; +import Pokemon, { EnemyPokemon, PokemonSummonData } from "../field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { TrainerSlot } from "#enums/trainer-slot"; import type { Variant } from "#app/sprites/variant"; import { loadBattlerTag } from "../data/battler-tags"; diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index 27985629e3d..63c0703fa18 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -10,7 +10,7 @@ import { getLocalizedSpriteKey, fixedInt, padInt } from "#app/utils"; import { MoveCategory } from "#enums/MoveCategory"; import i18next from "i18next"; import { Button } from "#enums/buttons"; -import type { PokemonMove } from "#app/field/pokemon"; +import type { PokemonMove } from "#app/data/moves/pokemon-move"; import type Pokemon from "#app/field/pokemon"; import type { CommandPhase } from "#app/phases/command-phase"; import MoveInfoOverlay from "./move-info-overlay"; diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 26351d4dbf1..f0ff351bb8a 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -9,7 +9,7 @@ import { LockModifierTiersModifier, PokemonHeldItemModifier, HealShopCostModifie import { handleTutorial, Tutorial } from "../tutorial"; import { Button } from "#enums/buttons"; import MoveInfoOverlay from "./move-info-overlay"; -import { allMoves } from "../data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { formatMoney, NumberHolder } from "#app/utils"; import Overrides from "#app/overrides"; import i18next from "i18next"; diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index ba90108c274..a42e0caadae 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1,4 +1,5 @@ -import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; +import type { PlayerPokemon } from "#app/field/pokemon"; +import type { PokemonMove } from "#app/data/moves/pokemon-move"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "#app/ui/text"; @@ -11,7 +12,8 @@ import { PokemonHeldItemModifier, SwitchEffectTransferModifier, } from "#app/modifier/modifier"; -import { allMoves, ForceSwitchOutAttr } from "#app/data/moves/move"; +import { ForceSwitchOutAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Gender, getGenderColor, getGenderSymbol } from "#app/data/gender"; import { StatusEffect } from "#enums/status-effect"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "#app/ui/pokemon-icon-anim-handler"; diff --git a/src/ui/pokedex-page-ui-handler.ts b/src/ui/pokedex-page-ui-handler.ts index 407ebfcd843..1011fc89ae0 100644 --- a/src/ui/pokedex-page-ui-handler.ts +++ b/src/ui/pokedex-page-ui-handler.ts @@ -9,7 +9,7 @@ import { allAbilities } from "#app/data/ability"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { GrowthRate, getGrowthRateColor } from "#app/data/exp"; import { Gender, getGenderColor, getGenderSymbol } from "#app/data/gender"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { getNatureName } from "#app/data/nature"; import type { SpeciesFormChange } from "#app/data/pokemon-forms"; import { pokemonFormChanges } from "#app/data/pokemon-forms"; diff --git a/src/ui/pokedex-scan-ui-handler.ts b/src/ui/pokedex-scan-ui-handler.ts index b34246b97d1..54c32fb34a1 100644 --- a/src/ui/pokedex-scan-ui-handler.ts +++ b/src/ui/pokedex-scan-ui-handler.ts @@ -7,7 +7,7 @@ import { isNullOrUndefined } from "#app/utils"; import { Mode } from "./ui"; import { FilterTextRow } from "./filter-text"; import { allAbilities } from "#app/data/ability"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { allSpecies } from "#app/data/pokemon-species"; import i18next from "i18next"; diff --git a/src/ui/pokedex-ui-handler.ts b/src/ui/pokedex-ui-handler.ts index 59b06d476a2..22ce5b833af 100644 --- a/src/ui/pokedex-ui-handler.ts +++ b/src/ui/pokedex-ui-handler.ts @@ -38,7 +38,7 @@ import type { OptionSelectConfig } from "./abstact-option-select-ui-handler"; import { FilterText, FilterTextRow } from "./filter-text"; import { allAbilities } from "#app/data/ability"; import { starterPassiveAbilities } from "#app/data/balance/passives"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { speciesTmMoves } from "#app/data/balance/tms"; import { pokemonPrevolutions, pokemonStarters } from "#app/data/balance/pokemon-evolutions"; import { Biome } from "#enums/biome"; diff --git a/src/ui/pokemon-hatch-info-container.ts b/src/ui/pokemon-hatch-info-container.ts index 692f0f1d374..77f9f5090a0 100644 --- a/src/ui/pokemon-hatch-info-container.ts +++ b/src/ui/pokemon-hatch-info-container.ts @@ -4,7 +4,7 @@ import { PokemonType } from "#enums/pokemon-type"; import { rgbHexToRgba, padInt } from "#app/utils"; import { TextStyle, addTextObject } from "#app/ui/text"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Species } from "#enums/species"; import { getEggTierForSpecies } from "#app/data/egg"; import { starterColors } from "#app/battle-scene"; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 3e2940f45b9..680f752096b 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -13,7 +13,7 @@ import { allAbilities } from "#app/data/ability"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { GrowthRate, getGrowthRateColor } from "#app/data/exp"; import { Gender, getGenderColor, getGenderSymbol } from "#app/data/gender"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { getNatureName } from "#app/data/nature"; import { pokemonFormChanges } from "#app/data/pokemon-forms"; import type { LevelMoves } from "#app/data/balance/pokemon-level-moves"; diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 04bcf71d7ae..d82082f0872 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -12,7 +12,8 @@ import { toReadableString, formatStat, } from "#app/utils"; -import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; +import type { PlayerPokemon } from "#app/field/pokemon"; +import type { PokemonMove } from "#app/data/moves/pokemon-move"; import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters"; import { argbFromRgba } from "@material/material-color-utilities"; import { getTypeRgb } from "#app/data/type"; diff --git a/test/abilities/aura_break.test.ts b/test/abilities/aura_break.test.ts index 86b6c69ec8b..30841fdbe0c 100644 --- a/test/abilities/aura_break.test.ts +++ b/test/abilities/aura_break.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/abilities/battery.test.ts b/test/abilities/battery.test.ts index cc7570c3d31..78db19e67ff 100644 --- a/test/abilities/battery.test.ts +++ b/test/abilities/battery.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; diff --git a/test/abilities/battle_bond.test.ts b/test/abilities/battle_bond.test.ts index 6305d7dedc5..e615b5746c0 100644 --- a/test/abilities/battle_bond.test.ts +++ b/test/abilities/battle_bond.test.ts @@ -1,4 +1,5 @@ -import { allMoves, MultiHitAttr } from "#app/data/moves/move"; +import { MultiHitAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { MultiHitType } from "#enums/MultiHitType"; import { Status } from "#app/data/status-effect"; import { Abilities } from "#enums/abilities"; diff --git a/test/abilities/flower_veil.test.ts b/test/abilities/flower_veil.test.ts index c26a952acff..d91c92e8c9f 100644 --- a/test/abilities/flower_veil.test.ts +++ b/test/abilities/flower_veil.test.ts @@ -7,7 +7,7 @@ import { StatusEffect } from "#enums/status-effect"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { BattlerTagType } from "#enums/battler-tag-type"; import { allAbilities } from "#app/data/ability"; diff --git a/test/abilities/friend_guard.test.ts b/test/abilities/friend_guard.test.ts index 30175fe37e0..cee82ca2c69 100644 --- a/test/abilities/friend_guard.test.ts +++ b/test/abilities/friend_guard.test.ts @@ -6,7 +6,7 @@ import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { BattlerIndex } from "#app/battle"; import { allAbilities } from "#app/data/ability"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { MoveCategory } from "#enums/MoveCategory"; describe("Moves - Friend Guard", () => { diff --git a/test/abilities/galvanize.test.ts b/test/abilities/galvanize.test.ts index c1e02c6c8d8..4efb6bb068f 100644 --- a/test/abilities/galvanize.test.ts +++ b/test/abilities/galvanize.test.ts @@ -1,10 +1,10 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { PokemonType } from "#enums/pokemon-type"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; -import { HitResult } from "#app/field/pokemon"; +import { HitResult } from "#enums/hit-result"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/hustle.test.ts b/test/abilities/hustle.test.ts index 40197cf9e97..fbfa23e90d6 100644 --- a/test/abilities/hustle.test.ts +++ b/test/abilities/hustle.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { Stat } from "#app/enums/stat"; import { Moves } from "#enums/moves"; diff --git a/test/abilities/infiltrator.test.ts b/test/abilities/infiltrator.test.ts index 6278439651c..e9ecf366a37 100644 --- a/test/abilities/infiltrator.test.ts +++ b/test/abilities/infiltrator.test.ts @@ -1,5 +1,5 @@ import { ArenaTagSide } from "#app/data/arena-tag"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Stat } from "#enums/stat"; diff --git a/test/abilities/libero.test.ts b/test/abilities/libero.test.ts index 22abf1c248f..96a6b3c5d93 100644 --- a/test/abilities/libero.test.ts +++ b/test/abilities/libero.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { PokemonType } from "#enums/pokemon-type"; import { Weather } from "#app/data/weather"; import type { PlayerPokemon } from "#app/field/pokemon"; diff --git a/test/abilities/magic_bounce.test.ts b/test/abilities/magic_bounce.test.ts index f9a076776aa..c785827c910 100644 --- a/test/abilities/magic_bounce.test.ts +++ b/test/abilities/magic_bounce.test.ts @@ -1,7 +1,7 @@ import { BattlerIndex } from "#app/battle"; import { allAbilities } from "#app/data/ability"; import { ArenaTagSide } from "#app/data/arena-tag"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { Stat } from "#app/enums/stat"; diff --git a/test/abilities/power_spot.test.ts b/test/abilities/power_spot.test.ts index e29b5ecf775..68ace696d4a 100644 --- a/test/abilities/power_spot.test.ts +++ b/test/abilities/power_spot.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; diff --git a/test/abilities/protean.test.ts b/test/abilities/protean.test.ts index 574033bb13f..ca5e67139e1 100644 --- a/test/abilities/protean.test.ts +++ b/test/abilities/protean.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { PokemonType } from "#enums/pokemon-type"; import { Weather } from "#app/data/weather"; import type { PlayerPokemon } from "#app/field/pokemon"; diff --git a/test/abilities/sap_sipper.test.ts b/test/abilities/sap_sipper.test.ts index f4f02844cbc..b27f97099b9 100644 --- a/test/abilities/sap_sipper.test.ts +++ b/test/abilities/sap_sipper.test.ts @@ -9,7 +9,8 @@ import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { allMoves, RandomMoveAttr } from "#app/data/moves/move"; +import { RandomMoveAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; // See also: TypeImmunityAbAttr describe("Abilities - Sap Sipper", () => { diff --git a/test/abilities/serene_grace.test.ts b/test/abilities/serene_grace.test.ts index 65ca96acbbc..30073f30b24 100644 --- a/test/abilities/serene_grace.test.ts +++ b/test/abilities/serene_grace.test.ts @@ -4,7 +4,7 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { FlinchAttr } from "#app/data/moves/move"; diff --git a/test/abilities/sheer_force.test.ts b/test/abilities/sheer_force.test.ts index 4a1c20cde5c..74c7b30a846 100644 --- a/test/abilities/sheer_force.test.ts +++ b/test/abilities/sheer_force.test.ts @@ -7,7 +7,8 @@ import { Stat } from "#enums/stat"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { allMoves, FlinchAttr } from "#app/data/moves/move"; +import { FlinchAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; describe("Abilities - Sheer Force", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/steely_spirit.test.ts b/test/abilities/steely_spirit.test.ts index b180ff8919e..6e8331ea51a 100644 --- a/test/abilities/steely_spirit.test.ts +++ b/test/abilities/steely_spirit.test.ts @@ -1,5 +1,5 @@ import { allAbilities } from "#app/data/ability"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/abilities/supreme_overlord.test.ts b/test/abilities/supreme_overlord.test.ts index a71bf0a9354..69ff4f393b6 100644 --- a/test/abilities/supreme_overlord.test.ts +++ b/test/abilities/supreme_overlord.test.ts @@ -7,7 +7,7 @@ import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; describe("Abilities - Supreme Overlord", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/tera_shell.test.ts b/test/abilities/tera_shell.test.ts index a99ecfd4ce1..bd88c21f52d 100644 --- a/test/abilities/tera_shell.test.ts +++ b/test/abilities/tera_shell.test.ts @@ -2,7 +2,7 @@ import { BattlerIndex } from "#app/battle"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; -import { HitResult } from "#app/field/pokemon"; +import { HitResult } from "#enums/hit-result"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/unburden.test.ts b/test/abilities/unburden.test.ts index 8f18604011c..7012c4cf065 100644 --- a/test/abilities/unburden.test.ts +++ b/test/abilities/unburden.test.ts @@ -1,6 +1,7 @@ import { BattlerIndex } from "#app/battle"; import { PostItemLostAbAttr } from "#app/data/ability"; -import { allMoves, StealHeldItemChanceAttr } from "#app/data/moves/move"; +import { StealHeldItemChanceAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import type Pokemon from "#app/field/pokemon"; import type { ContactHeldItemTransferChanceModifier } from "#app/modifier/modifier"; import { Abilities } from "#enums/abilities"; diff --git a/test/abilities/wimp_out.test.ts b/test/abilities/wimp_out.test.ts index 294025a10e7..c81fa2071c5 100644 --- a/test/abilities/wimp_out.test.ts +++ b/test/abilities/wimp_out.test.ts @@ -1,6 +1,6 @@ import { BattlerIndex } from "#app/battle"; import { ArenaTagSide } from "#app/data/arena-tag"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import GameManager from "#test/testUtils/gameManager"; import { toDmgValue } from "#app/utils"; import { Abilities } from "#enums/abilities"; @@ -534,12 +534,12 @@ describe("Abilities - Wimp Out", () => { .enemyAbility(Abilities.WIMP_OUT) .startingLevel(50) .enemyLevel(1) - .enemyMoveset([ Moves.SPLASH, Moves.ENDURE ]) + .enemyMoveset([Moves.SPLASH, Moves.ENDURE]) .battleType("double") - .moveset([ Moves.DRAGON_ENERGY, Moves.SPLASH ]) + .moveset([Moves.DRAGON_ENERGY, Moves.SPLASH]) .startingWave(wave); - await game.classicMode.startBattle([ Species.REGIDRAGO, Species.MAGIKARP ]); + await game.classicMode.startBattle([Species.REGIDRAGO, Species.MAGIKARP]); // turn 1 game.move.select(Moves.DRAGON_ENERGY, 0); @@ -549,6 +549,5 @@ describe("Abilities - Wimp Out", () => { await game.phaseInterceptor.to("SelectModifierPhase"); expect(game.scene.currentBattle.waveIndex).toBe(wave + 1); - }); }); diff --git a/test/abilities/wonder_skin.test.ts b/test/abilities/wonder_skin.test.ts index 18d5be36aef..fe24cdad5ec 100644 --- a/test/abilities/wonder_skin.test.ts +++ b/test/abilities/wonder_skin.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; diff --git a/test/arena/arena_gravity.test.ts b/test/arena/arena_gravity.test.ts index a5ce84667f0..7e72d14460a 100644 --- a/test/arena/arena_gravity.test.ts +++ b/test/arena/arena_gravity.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; diff --git a/test/arena/grassy_terrain.test.ts b/test/arena/grassy_terrain.test.ts index d92fb24be5a..9ee9d2ef434 100644 --- a/test/arena/grassy_terrain.test.ts +++ b/test/arena/grassy_terrain.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/arena/weather_fog.test.ts b/test/arena/weather_fog.test.ts index 784c4886648..b240bfa7386 100644 --- a/test/arena/weather_fog.test.ts +++ b/test/arena/weather_fog.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; diff --git a/test/arena/weather_strong_winds.test.ts b/test/arena/weather_strong_winds.test.ts index 3a9235d9eb9..50d25947612 100644 --- a/test/arena/weather_strong_winds.test.ts +++ b/test/arena/weather_strong_winds.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { StatusEffect } from "#app/enums/status-effect"; import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { Abilities } from "#enums/abilities"; diff --git a/test/battle/damage_calculation.test.ts b/test/battle/damage_calculation.test.ts index dab1fc81caa..11bb8246ca1 100644 --- a/test/battle/damage_calculation.test.ts +++ b/test/battle/damage_calculation.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import type { EnemyPersistentModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import { Abilities } from "#enums/abilities"; diff --git a/test/battlerTags/substitute.test.ts b/test/battlerTags/substitute.test.ts index fca3dc5ef7e..f2ee741bca2 100644 --- a/test/battlerTags/substitute.test.ts +++ b/test/battlerTags/substitute.test.ts @@ -1,5 +1,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import type { PokemonTurnData, TurnMove, PokemonMove } from "#app/field/pokemon"; +import type { PokemonTurnData } from "#app/field/pokemon"; +import type { PokemonMove } from "#app/data/moves/pokemon-move"; +import type { TurnMove } from "#app/interfaces/turn-move"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import type BattleScene from "#app/battle-scene"; @@ -7,7 +9,7 @@ import { BattlerTagLapseType, BindTag, SubstituteTag } from "#app/data/battler-t import { Moves } from "#app/enums/moves"; import { PokemonAnimType } from "#app/enums/pokemon-anim-type"; import * as messages from "#app/messages"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import type { MoveEffectPhase } from "#app/phases/move-effect-phase"; import GameManager from "#test/testUtils/gameManager"; diff --git a/test/enemy_command.test.ts b/test/enemy_command.test.ts index 6d5cc2698a3..cfa141cf89e 100644 --- a/test/enemy_command.test.ts +++ b/test/enemy_command.test.ts @@ -1,11 +1,11 @@ import type BattleScene from "#app/battle-scene"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { MoveCategory } from "#enums/MoveCategory"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import type { EnemyPokemon } from "#app/field/pokemon"; -import { AiType } from "#app/field/pokemon"; +import { AiType } from "#enums/ai-type"; import { randSeedInt } from "#app/utils"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; diff --git a/test/evolution.test.ts b/test/evolution.test.ts index dd6795bf161..62a06f868e8 100644 --- a/test/evolution.test.ts +++ b/test/evolution.test.ts @@ -1,8 +1,5 @@ -import { - pokemonEvolutions, - SpeciesFormEvolution, - SpeciesWildEvolutionDelay, -} from "#app/data/balance/pokemon-evolutions"; +import { pokemonEvolutions, SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; +import { SpeciesWildEvolutionDelay } from "#enums/species-wild-evolution-delay"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; diff --git a/test/imports.test.ts b/test/imports.test.ts index 128308dbd14..ada7eff0109 100644 --- a/test/imports.test.ts +++ b/test/imports.test.ts @@ -4,7 +4,7 @@ import { describe, expect, it } from "vitest"; async function importModule() { try { initStatsKeys(); - const { PokemonMove } = await import("#app/field/pokemon"); + const { PokemonMove } = await import("#app/data/moves/pokemon-move"); const { Species } = await import("#enums/species"); return { PokemonMove, diff --git a/test/items/reviver_seed.test.ts b/test/items/reviver_seed.test.ts index c06f354a94a..e1e7e0d554e 100644 --- a/test/items/reviver_seed.test.ts +++ b/test/items/reviver_seed.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import type { PokemonInstantReviveModifier } from "#app/modifier/modifier"; import { Abilities } from "#enums/abilities"; diff --git a/test/moves/astonish.test.ts b/test/moves/astonish.test.ts index 53922060ae6..69a312d4517 100644 --- a/test/moves/astonish.test.ts +++ b/test/moves/astonish.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { BerryPhase } from "#app/phases/berry-phase"; import { CommandPhase } from "#app/phases/command-phase"; diff --git a/test/moves/aurora_veil.test.ts b/test/moves/aurora_veil.test.ts index 31f6497bae5..06637d0764e 100644 --- a/test/moves/aurora_veil.test.ts +++ b/test/moves/aurora_veil.test.ts @@ -1,7 +1,8 @@ import type BattleScene from "#app/battle-scene"; import { ArenaTagSide } from "#app/data/arena-tag"; import type Move from "#app/data/moves/move"; -import { allMoves, CritOnlyAttr } from "#app/data/moves/move"; +import { CritOnlyAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import type Pokemon from "#app/field/pokemon"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; diff --git a/test/moves/burning_jealousy.test.ts b/test/moves/burning_jealousy.test.ts index 60387df4226..c618b46e842 100644 --- a/test/moves/burning_jealousy.test.ts +++ b/test/moves/burning_jealousy.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { StatusEffect } from "#app/enums/status-effect"; import { Moves } from "#enums/moves"; diff --git a/test/moves/ceaseless_edge.test.ts b/test/moves/ceaseless_edge.test.ts index d54f1bd9f21..227645df360 100644 --- a/test/moves/ceaseless_edge.test.ts +++ b/test/moves/ceaseless_edge.test.ts @@ -1,5 +1,5 @@ import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; diff --git a/test/moves/copycat.test.ts b/test/moves/copycat.test.ts index 0d9b0951f89..615206275d4 100644 --- a/test/moves/copycat.test.ts +++ b/test/moves/copycat.test.ts @@ -1,5 +1,6 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves, RandomMoveAttr } from "#app/data/moves/move"; +import { RandomMoveAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Stat } from "#app/enums/stat"; import { MoveResult } from "#app/field/pokemon"; import { Abilities } from "#enums/abilities"; diff --git a/test/moves/destiny_bond.test.ts b/test/moves/destiny_bond.test.ts index c39d40427ad..9873d678b8c 100644 --- a/test/moves/destiny_bond.test.ts +++ b/test/moves/destiny_bond.test.ts @@ -1,6 +1,6 @@ import type { ArenaTrapTag } from "#app/data/arena-tag"; import { ArenaTagSide } from "#app/data/arena-tag"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; import { Moves } from "#enums/moves"; diff --git a/test/moves/diamond_storm.test.ts b/test/moves/diamond_storm.test.ts index 2363122f0d7..73a1aee3fd2 100644 --- a/test/moves/diamond_storm.test.ts +++ b/test/moves/diamond_storm.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/moves/dig.test.ts b/test/moves/dig.test.ts index 81339111656..14e7efee19b 100644 --- a/test/moves/dig.test.ts +++ b/test/moves/dig.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; diff --git a/test/moves/dragon_tail.test.ts b/test/moves/dragon_tail.test.ts index 37e8aa2fe1b..a571312473d 100644 --- a/test/moves/dragon_tail.test.ts +++ b/test/moves/dragon_tail.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Status } from "#app/data/status-effect"; import { Challenges } from "#enums/challenges"; import { StatusEffect } from "#enums/status-effect"; diff --git a/test/moves/dynamax_cannon.test.ts b/test/moves/dynamax_cannon.test.ts index 9cf3106b9c1..b2590449e4e 100644 --- a/test/moves/dynamax_cannon.test.ts +++ b/test/moves/dynamax_cannon.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; diff --git a/test/moves/effectiveness.test.ts b/test/moves/effectiveness.test.ts index fb03f1c10a0..efcbc9c3293 100644 --- a/test/moves/effectiveness.test.ts +++ b/test/moves/effectiveness.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { TrainerSlot } from "#enums/trainer-slot"; import { PokemonType } from "#enums/pokemon-type"; diff --git a/test/moves/fell_stinger.test.ts b/test/moves/fell_stinger.test.ts index 2ffa44c5a3a..766fedf68dc 100644 --- a/test/moves/fell_stinger.test.ts +++ b/test/moves/fell_stinger.test.ts @@ -7,7 +7,7 @@ import { Moves } from "#enums/moves"; import { Stat } from "#enums/stat"; import { StatusEffect } from "#app/enums/status-effect"; import { WeatherType } from "#app/enums/weather-type"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; describe("Moves - Fell Stinger", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/fly.test.ts b/test/moves/fly.test.ts index 0bd7d22b2a7..37fa42b608d 100644 --- a/test/moves/fly.test.ts +++ b/test/moves/fly.test.ts @@ -8,7 +8,7 @@ import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; describe("Moves - Fly", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/freezy_frost.test.ts b/test/moves/freezy_frost.test.ts index c1ac4054e70..d764600bc78 100644 --- a/test/moves/freezy_frost.test.ts +++ b/test/moves/freezy_frost.test.ts @@ -5,7 +5,7 @@ import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { CommandPhase } from "#app/phases/command-phase"; describe("Moves - Freezy Frost", () => { diff --git a/test/moves/fusion_flare_bolt.test.ts b/test/moves/fusion_flare_bolt.test.ts index c340aeea63f..32df10b4c7c 100644 --- a/test/moves/fusion_flare_bolt.test.ts +++ b/test/moves/fusion_flare_bolt.test.ts @@ -1,6 +1,6 @@ import { Stat } from "#enums/stat"; import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import type Move from "#app/data/moves/move"; import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; diff --git a/test/moves/glaive_rush.test.ts b/test/moves/glaive_rush.test.ts index d3531b172e2..28d6328c095 100644 --- a/test/moves/glaive_rush.test.ts +++ b/test/moves/glaive_rush.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; diff --git a/test/moves/hard_press.test.ts b/test/moves/hard_press.test.ts index 8891f0bf0e2..425993fb1a9 100644 --- a/test/moves/hard_press.test.ts +++ b/test/moves/hard_press.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; diff --git a/test/moves/hyper_beam.test.ts b/test/moves/hyper_beam.test.ts index 5cd54e9b46a..b1a244f2ea4 100644 --- a/test/moves/hyper_beam.test.ts +++ b/test/moves/hyper_beam.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { Moves } from "#app/enums/moves"; diff --git a/test/moves/lash_out.test.ts b/test/moves/lash_out.test.ts index 8395633f5c0..16632ec0065 100644 --- a/test/moves/lash_out.test.ts +++ b/test/moves/lash_out.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/moves/last_respects.test.ts b/test/moves/last_respects.test.ts index ccab8a43415..891b287dece 100644 --- a/test/moves/last_respects.test.ts +++ b/test/moves/last_respects.test.ts @@ -3,7 +3,7 @@ import { BattlerIndex } from "#app/battle"; import { Species } from "#enums/species"; import { Abilities } from "#enums/abilities"; import GameManager from "#test/testUtils/gameManager"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import type Move from "#app/data/moves/move"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import Phaser from "phaser"; diff --git a/test/moves/light_screen.test.ts b/test/moves/light_screen.test.ts index 9cc6944ed3e..b77bb1c790b 100644 --- a/test/moves/light_screen.test.ts +++ b/test/moves/light_screen.test.ts @@ -1,7 +1,8 @@ import type BattleScene from "#app/battle-scene"; import { ArenaTagSide } from "#app/data/arena-tag"; import type Move from "#app/data/moves/move"; -import { allMoves, CritOnlyAttr } from "#app/data/moves/move"; +import { CritOnlyAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import type Pokemon from "#app/field/pokemon"; diff --git a/test/moves/magic_coat.test.ts b/test/moves/magic_coat.test.ts index 2cc8dea8938..e96125a23ac 100644 --- a/test/moves/magic_coat.test.ts +++ b/test/moves/magic_coat.test.ts @@ -1,6 +1,6 @@ import { BattlerIndex } from "#app/battle"; import { ArenaTagSide } from "#app/data/arena-tag"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { Stat } from "#app/enums/stat"; diff --git a/test/moves/metronome.test.ts b/test/moves/metronome.test.ts index 80f32a3a6fb..bf045f5e9f9 100644 --- a/test/moves/metronome.test.ts +++ b/test/moves/metronome.test.ts @@ -1,5 +1,6 @@ import { RechargingTag, SemiInvulnerableTag } from "#app/data/battler-tags"; -import { allMoves, RandomMoveAttr } from "#app/data/moves/move"; +import { RandomMoveAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { Stat } from "#app/enums/stat"; import { CommandPhase } from "#app/phases/command-phase"; diff --git a/test/moves/moongeist_beam.test.ts b/test/moves/moongeist_beam.test.ts index 117fe513e17..94197683ea4 100644 --- a/test/moves/moongeist_beam.test.ts +++ b/test/moves/moongeist_beam.test.ts @@ -1,4 +1,5 @@ -import { allMoves, RandomMoveAttr } from "#app/data/moves/move"; +import { RandomMoveAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/moves/pledge_moves.test.ts b/test/moves/pledge_moves.test.ts index c866d15357c..d3b8e60ac62 100644 --- a/test/moves/pledge_moves.test.ts +++ b/test/moves/pledge_moves.test.ts @@ -1,7 +1,8 @@ import { BattlerIndex } from "#app/battle"; import { allAbilities } from "#app/data/ability"; import { ArenaTagSide } from "#app/data/arena-tag"; -import { allMoves, FlinchAttr } from "#app/data/moves/move"; +import { FlinchAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { PokemonType } from "#enums/pokemon-type"; import { ArenaTagType } from "#enums/arena-tag-type"; import { Stat } from "#enums/stat"; diff --git a/test/moves/powder.test.ts b/test/moves/powder.test.ts index 522b0b74ca7..510564e0f53 100644 --- a/test/moves/powder.test.ts +++ b/test/moves/powder.test.ts @@ -5,7 +5,8 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { BerryPhase } from "#app/phases/berry-phase"; -import { MoveResult, PokemonMove } from "#app/field/pokemon"; +import { MoveResult } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { PokemonType } from "#enums/pokemon-type"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { StatusEffect } from "#enums/status-effect"; diff --git a/test/moves/protect.test.ts b/test/moves/protect.test.ts index d50c490f7d3..65de079982f 100644 --- a/test/moves/protect.test.ts +++ b/test/moves/protect.test.ts @@ -5,7 +5,7 @@ import { Species } from "#enums/species"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Stat } from "#enums/stat"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; import { BattlerIndex } from "#app/battle"; import { MoveResult } from "#app/field/pokemon"; diff --git a/test/moves/rage_fist.test.ts b/test/moves/rage_fist.test.ts index f44901c5aba..73d83f4929c 100644 --- a/test/moves/rage_fist.test.ts +++ b/test/moves/rage_fist.test.ts @@ -2,7 +2,7 @@ import { BattlerIndex } from "#app/battle"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import type Move from "#app/data/moves/move"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; diff --git a/test/moves/reflect.test.ts b/test/moves/reflect.test.ts index ac879a7cc2b..272e5c2972c 100644 --- a/test/moves/reflect.test.ts +++ b/test/moves/reflect.test.ts @@ -1,7 +1,8 @@ import type BattleScene from "#app/battle-scene"; import { ArenaTagSide } from "#app/data/arena-tag"; import type Move from "#app/data/moves/move"; -import { allMoves, CritOnlyAttr } from "#app/data/moves/move"; +import { CritOnlyAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#app/enums/abilities"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import type Pokemon from "#app/field/pokemon"; diff --git a/test/moves/retaliate.test.ts b/test/moves/retaliate.test.ts index e916c9ffeaa..57d29b4fdfc 100644 --- a/test/moves/retaliate.test.ts +++ b/test/moves/retaliate.test.ts @@ -3,7 +3,7 @@ import Phaser from "phaser"; import GameManager from "#test/testUtils/gameManager"; import { Species } from "#enums/species"; import { Moves } from "#enums/moves"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import type Move from "#app/data/moves/move"; describe("Moves - Retaliate", () => { diff --git a/test/moves/rollout.test.ts b/test/moves/rollout.test.ts index 89270c2dfc7..456f029cda1 100644 --- a/test/moves/rollout.test.ts +++ b/test/moves/rollout.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { CommandPhase } from "#app/phases/command-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; diff --git a/test/moves/round.test.ts b/test/moves/round.test.ts index 82f080a25ea..ec9f3f69a5e 100644 --- a/test/moves/round.test.ts +++ b/test/moves/round.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import type { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; diff --git a/test/moves/scale_shot.test.ts b/test/moves/scale_shot.test.ts index 2be632adb54..ee759b8404a 100644 --- a/test/moves/scale_shot.test.ts +++ b/test/moves/scale_shot.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; diff --git a/test/moves/secret_power.test.ts b/test/moves/secret_power.test.ts index 37f1664251b..40802dcc51f 100644 --- a/test/moves/secret_power.test.ts +++ b/test/moves/secret_power.test.ts @@ -2,7 +2,7 @@ import { Abilities } from "#enums/abilities"; import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Stat } from "#enums/stat"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; diff --git a/test/moves/shell_side_arm.test.ts b/test/moves/shell_side_arm.test.ts index a5b065b76cb..232182ffef0 100644 --- a/test/moves/shell_side_arm.test.ts +++ b/test/moves/shell_side_arm.test.ts @@ -1,5 +1,6 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves, ShellSideArmCategoryAttr } from "#app/data/moves/move"; +import { ShellSideArmCategoryAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import type Move from "#app/data/moves/move"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; diff --git a/test/moves/shell_trap.test.ts b/test/moves/shell_trap.test.ts index 2df94cdb828..d3ba67843ac 100644 --- a/test/moves/shell_trap.test.ts +++ b/test/moves/shell_trap.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import { MoveResult } from "#app/field/pokemon"; diff --git a/test/moves/sketch.test.ts b/test/moves/sketch.test.ts index dfbf2eca713..94f37757a6a 100644 --- a/test/moves/sketch.test.ts +++ b/test/moves/sketch.test.ts @@ -1,13 +1,15 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { MoveResult, PokemonMove } from "#app/field/pokemon"; +import { MoveResult } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { StatusEffect } from "#app/enums/status-effect"; import { BattlerIndex } from "#app/battle"; -import { allMoves, RandomMoveAttr } from "#app/data/moves/move"; +import { RandomMoveAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; describe("Moves - Sketch", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/solar_beam.test.ts b/test/moves/solar_beam.test.ts index dffd4f210e5..b8a28065b64 100644 --- a/test/moves/solar_beam.test.ts +++ b/test/moves/solar_beam.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { BattlerTagType } from "#enums/battler-tag-type"; import { WeatherType } from "#enums/weather-type"; import { MoveResult } from "#app/field/pokemon"; diff --git a/test/moves/sparkly_swirl.test.ts b/test/moves/sparkly_swirl.test.ts index 6cd357c7e0e..1908772598a 100644 --- a/test/moves/sparkly_swirl.test.ts +++ b/test/moves/sparkly_swirl.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { StatusEffect } from "#app/enums/status-effect"; import { CommandPhase } from "#app/phases/command-phase"; import { Abilities } from "#enums/abilities"; diff --git a/test/moves/spectral_thief.test.ts b/test/moves/spectral_thief.test.ts index 2e52b118a74..271cb03073a 100644 --- a/test/moves/spectral_thief.test.ts +++ b/test/moves/spectral_thief.test.ts @@ -1,7 +1,7 @@ import { Abilities } from "#enums/abilities"; import { BattlerIndex } from "#app/battle"; import { Stat } from "#enums/stat"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; diff --git a/test/moves/spit_up.test.ts b/test/moves/spit_up.test.ts index d71647bda52..7ef6e5e5b14 100644 --- a/test/moves/spit_up.test.ts +++ b/test/moves/spit_up.test.ts @@ -1,8 +1,8 @@ import { Stat } from "#enums/stat"; import { StockpilingTag } from "#app/data/battler-tags"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { BattlerTagType } from "#app/enums/battler-tag-type"; -import type { TurnMove } from "#app/field/pokemon"; +import type { TurnMove } from "#app/interfaces/turn-move"; import { MoveResult } from "#app/field/pokemon"; import GameManager from "#test/testUtils/gameManager"; import { Abilities } from "#enums/abilities"; diff --git a/test/moves/steamroller.test.ts b/test/moves/steamroller.test.ts index ba96928e01d..a0e4c29cce5 100644 --- a/test/moves/steamroller.test.ts +++ b/test/moves/steamroller.test.ts @@ -1,7 +1,7 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { BattlerTagType } from "#app/enums/battler-tag-type"; -import type { DamageCalculationResult } from "#app/field/pokemon"; +import type { DamageCalculationResult } from "#app/interfaces/damage-calculation-result"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/moves/stockpile.test.ts b/test/moves/stockpile.test.ts index 033f24d5229..f6e6a0087f6 100644 --- a/test/moves/stockpile.test.ts +++ b/test/moves/stockpile.test.ts @@ -1,6 +1,6 @@ import { Stat } from "#enums/stat"; import { StockpilingTag } from "#app/data/battler-tags"; -import type { TurnMove } from "#app/field/pokemon"; +import type { TurnMove } from "#app/interfaces/turn-move"; import { MoveResult } from "#app/field/pokemon"; import { CommandPhase } from "#app/phases/command-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase"; diff --git a/test/moves/substitute.test.ts b/test/moves/substitute.test.ts index 23f7f4af4b9..68b90bf7cf8 100644 --- a/test/moves/substitute.test.ts +++ b/test/moves/substitute.test.ts @@ -1,7 +1,8 @@ import { BattlerIndex } from "#app/battle"; import { ArenaTagSide } from "#app/data/arena-tag"; import { SubstituteTag, TrappedTag } from "#app/data/battler-tags"; -import { allMoves, StealHeldItemChanceAttr } from "#app/data/moves/move"; +import { StealHeldItemChanceAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { MoveResult } from "#app/field/pokemon"; import type { CommandPhase } from "#app/phases/command-phase"; import GameManager from "#test/testUtils/gameManager"; diff --git a/test/moves/swallow.test.ts b/test/moves/swallow.test.ts index baa03801079..86af584a174 100644 --- a/test/moves/swallow.test.ts +++ b/test/moves/swallow.test.ts @@ -1,7 +1,7 @@ import { Stat } from "#enums/stat"; import { StockpilingTag } from "#app/data/battler-tags"; import { BattlerTagType } from "#app/enums/battler-tag-type"; -import type { TurnMove } from "#app/field/pokemon"; +import type { TurnMove } from "#app/interfaces/turn-move"; import { MoveResult } from "#app/field/pokemon"; import { MovePhase } from "#app/phases/move-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase"; diff --git a/test/moves/telekinesis.test.ts b/test/moves/telekinesis.test.ts index 1355cb975f3..7537ba0168a 100644 --- a/test/moves/telekinesis.test.ts +++ b/test/moves/telekinesis.test.ts @@ -1,5 +1,5 @@ import { BattlerTagType } from "#enums/battler-tag-type"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/moves/tera_blast.test.ts b/test/moves/tera_blast.test.ts index c1a2b999fa0..9d17ea6a3cc 100644 --- a/test/moves/tera_blast.test.ts +++ b/test/moves/tera_blast.test.ts @@ -1,10 +1,11 @@ import { BattlerIndex } from "#app/battle"; import { Stat } from "#enums/stat"; -import { allMoves, TeraMoveCategoryAttr } from "#app/data/moves/move"; +import { TeraMoveCategoryAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import type Move from "#app/data/moves/move"; import { PokemonType } from "#enums/pokemon-type"; import { Abilities } from "#app/enums/abilities"; -import { HitResult } from "#app/field/pokemon"; +import { HitResult } from "#enums/hit-result"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; diff --git a/test/moves/toxic.test.ts b/test/moves/toxic.test.ts index f2b1f82fe02..ab536364f6a 100644 --- a/test/moves/toxic.test.ts +++ b/test/moves/toxic.test.ts @@ -5,7 +5,7 @@ import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { StatusEffect } from "#enums/status-effect"; import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; describe("Moves - Toxic", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/triple_arrows.test.ts b/test/moves/triple_arrows.test.ts index eb434b25815..d1d14f7d3e6 100644 --- a/test/moves/triple_arrows.test.ts +++ b/test/moves/triple_arrows.test.ts @@ -1,4 +1,5 @@ -import { allMoves, FlinchAttr, StatStageChangeAttr } from "#app/data/moves/move"; +import { FlinchAttr, StatStageChangeAttr } from "#app/data/moves/move"; +import { allMoves } from "#app/data/moves/all-moves"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import type Move from "#app/data/moves/move"; 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 3c7bda8febd..728129007e7 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,8 @@ 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 { PlayerPokemon } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; 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"; 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 9befe77e688..c1e6a635f31 100644 --- a/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts +++ b/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts @@ -11,7 +11,7 @@ import { } from "#test/mystery-encounter/encounter-test-utils"; import { Moves } from "#enums/moves"; import type BattleScene from "#app/battle-scene"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { Mode } from "#app/ui/ui"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; diff --git a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts index 4bbe76e5c72..7b3d87463bf 100644 --- a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts +++ b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts @@ -15,7 +15,7 @@ import { import { Moves } from "#enums/moves"; import type BattleScene from "#app/battle-scene"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { Mode } from "#app/ui/ui"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; diff --git a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts index 77cd65e51b9..5ea836d8aa6 100644 --- a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts +++ b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts @@ -17,7 +17,7 @@ import { Moves } from "#enums/moves"; import { DancingLessonsEncounter } from "#app/data/mystery-encounters/encounters/dancing-lessons-encounter"; import { Mode } from "#app/ui/ui"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { CommandPhase } from "#app/phases/command-phase"; import { MovePhase } from "#app/phases/move-phase"; 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 d233e72932a..82d80bc3970 100644 --- a/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts +++ b/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts @@ -11,7 +11,7 @@ import { } from "#test/mystery-encounter/encounter-test-utils"; import { Moves } from "#enums/moves"; import type BattleScene from "#app/battle-scene"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { Mode } from "#app/ui/ui"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; diff --git a/test/mystery-encounter/encounters/part-timer-encounter.test.ts b/test/mystery-encounter/encounters/part-timer-encounter.test.ts index 639a2e140ff..308aa9839e9 100644 --- a/test/mystery-encounter/encounters/part-timer-encounter.test.ts +++ b/test/mystery-encounter/encounters/part-timer-encounter.test.ts @@ -14,7 +14,7 @@ import { CIVILIZATION_ENCOUNTER_BIOMES } from "#app/data/mystery-encounters/myst import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { PartTimerEncounter } from "#app/data/mystery-encounters/encounters/part-timer-encounter"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { Moves } from "#enums/moves"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; 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 a9e6a339d36..0d0298901d0 100644 --- a/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts @@ -17,7 +17,7 @@ import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters import { Nature } from "#enums/nature"; import { BerryType } from "#enums/berry-type"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { Mode } from "#app/ui/ui"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { BerryModifier, PokemonBaseStatTotalModifier } from "#app/modifier/modifier"; 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 df7bbb9f424..44c8e7a8915 100644 --- a/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts +++ b/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts @@ -12,7 +12,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Biome } from "#app/enums/biome"; import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; import { Species } from "#app/enums/species"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { HealShopCostModifier, HitHealModifier, TurnHealModifier } from "#app/modifier/modifier"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { modifierTypes, type PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; diff --git a/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts b/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts index 452dfcf3784..e4928406a18 100644 --- a/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts +++ b/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts @@ -10,7 +10,7 @@ import { } from "#test/mystery-encounter/encounter-test-utils"; import { Moves } from "#enums/moves"; import type BattleScene from "#app/battle-scene"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index 543f46b2026..333f95f2014 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -1,7 +1,7 @@ import type { BattlerIndex } from "#app/battle"; import { Button } from "#app/enums/buttons"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/field/pokemon"; +import { PokemonMove } from "#app/data/moves/pokemon-move"; import Overrides from "#app/overrides"; import type { CommandPhase } from "#app/phases/command-phase"; import { LearnMovePhase } from "#app/phases/learn-move-phase"; From b41eee3c7f64be3b2c83ea30fb33210e87f73766 Mon Sep 17 00:00:00 2001 From: damocleas Date: Mon, 14 Apr 2025 12:28:36 -0400 Subject: [PATCH 19/52] Revert "[Refactor] Move many interfaces and enums to their own file" (#5661) Revert "[Refactor] Move many interfaces and enums to their own file (#5646)" This reverts commit c82e01eed377cacc5d3ffd2ef9caa27270e8a2dc. --- src/@types/damage-result.ts | 10 - src/battle-scene.ts | 2 +- src/battle.ts | 3 +- src/data/ability.ts | 9 +- src/data/arena-tag.ts | 5 +- src/data/balance/egg-moves.ts | 2 +- src/data/balance/pokemon-evolutions.ts | 60 ++++- src/data/battle-anims.ts | 3 +- src/data/battler-tags.ts | 5 +- src/data/berry.ts | 2 +- src/data/challenge.ts | 2 +- src/data/moves/all-moves.ts | 3 - src/data/moves/move.ts | 21 +- src/data/moves/pokemon-move.ts | 93 -------- .../encounters/absolute-avarice-encounter.ts | 3 +- .../encounters/bug-type-superfan-encounter.ts | 4 +- .../encounters/clowning-around-encounter.ts | 2 +- .../encounters/dancing-lessons-encounter.ts | 3 +- .../encounters/field-trip-encounter.ts | 3 +- .../encounters/fiery-fallout-encounter.ts | 2 +- .../encounters/fun-and-games-encounter.ts | 2 +- .../global-trade-system-encounter.ts | 3 +- .../encounters/lost-at-sea-encounter.ts | 2 +- .../slumbering-snorlax-encounter.ts | 3 +- .../encounters/the-strong-stuff-encounter.ts | 2 +- .../encounters/trash-to-treasure-encounter.ts | 2 +- .../encounters/uncommon-breed-encounter.ts | 2 +- .../encounters/weird-dream-encounter.ts | 2 +- .../mystery-encounter-requirements.ts | 3 +- .../mystery-encounters/mystery-encounter.ts | 3 +- .../can-learn-move-requirement.ts | 2 +- .../utils/encounter-phase-utils.ts | 7 +- src/data/pokemon-forms.ts | 2 +- src/data/pokemon-species.ts | 7 +- src/data/trainers/trainer-config.ts | 2 +- src/enums/ai-type.ts | 5 - src/enums/evolution-item.ts | 48 ---- src/enums/field-position.ts | 5 - src/enums/hit-result.ts | 15 -- src/enums/learn-move-context.ts | 8 - src/enums/species-wild-evolution-delay.ts | 8 - src/field/damage-number-handler.ts | 4 +- src/field/pokemon.ts | 206 ++++++++++++++++-- src/interfaces/attack-move-result.ts | 12 - src/interfaces/damage-calculation-result.ts | 11 - src/interfaces/turn-move.ts | 12 - src/modifier/modifier-type.ts | 9 +- src/modifier/modifier.ts | 2 +- src/overrides.ts | 2 +- src/phases/command-phase.ts | 5 +- src/phases/damage-anim-phase.ts | 3 +- src/phases/encounter-phase.ts | 2 +- src/phases/evolution-phase.ts | 10 +- src/phases/faint-phase.ts | 7 +- src/phases/learn-move-phase.ts | 2 +- src/phases/move-charge-phase.ts | 2 +- src/phases/move-effect-phase.ts | 5 +- src/phases/move-header-phase.ts | 2 +- src/phases/move-phase.ts | 4 +- src/phases/pokemon-heal-phase.ts | 2 +- src/phases/pokemon-transform-phase.ts | 2 +- src/phases/select-target-phase.ts | 2 +- src/phases/summon-phase.ts | 2 +- src/phases/switch-summon-phase.ts | 3 +- src/phases/toggle-double-position-phase.ts | 2 +- src/phases/turn-start-phase.ts | 5 +- src/phases/weather-effect-phase.ts | 2 +- src/system/game-data.ts | 2 +- src/system/pokemon-data.ts | 3 +- src/ui/fight-ui-handler.ts | 2 +- src/ui/modifier-select-ui-handler.ts | 2 +- src/ui/party-ui-handler.ts | 6 +- src/ui/pokedex-page-ui-handler.ts | 2 +- src/ui/pokedex-scan-ui-handler.ts | 2 +- src/ui/pokedex-ui-handler.ts | 2 +- src/ui/pokemon-hatch-info-container.ts | 2 +- src/ui/starter-select-ui-handler.ts | 2 +- src/ui/summary-ui-handler.ts | 3 +- test/abilities/aura_break.test.ts | 2 +- test/abilities/battery.test.ts | 2 +- test/abilities/battle_bond.test.ts | 3 +- test/abilities/flower_veil.test.ts | 2 +- test/abilities/friend_guard.test.ts | 2 +- test/abilities/galvanize.test.ts | 4 +- test/abilities/hustle.test.ts | 2 +- test/abilities/infiltrator.test.ts | 2 +- test/abilities/libero.test.ts | 2 +- test/abilities/magic_bounce.test.ts | 2 +- test/abilities/power_spot.test.ts | 2 +- test/abilities/protean.test.ts | 2 +- test/abilities/sap_sipper.test.ts | 3 +- test/abilities/serene_grace.test.ts | 2 +- test/abilities/sheer_force.test.ts | 3 +- test/abilities/steely_spirit.test.ts | 2 +- test/abilities/supreme_overlord.test.ts | 2 +- test/abilities/tera_shell.test.ts | 2 +- test/abilities/unburden.test.ts | 3 +- test/abilities/wimp_out.test.ts | 9 +- test/abilities/wonder_skin.test.ts | 2 +- test/arena/arena_gravity.test.ts | 2 +- test/arena/grassy_terrain.test.ts | 2 +- test/arena/weather_fog.test.ts | 2 +- test/arena/weather_strong_winds.test.ts | 2 +- test/battle/damage_calculation.test.ts | 2 +- test/battlerTags/substitute.test.ts | 6 +- test/enemy_command.test.ts | 4 +- test/evolution.test.ts | 7 +- test/imports.test.ts | 2 +- test/items/reviver_seed.test.ts | 2 +- test/moves/astonish.test.ts | 2 +- test/moves/aurora_veil.test.ts | 3 +- test/moves/burning_jealousy.test.ts | 2 +- test/moves/ceaseless_edge.test.ts | 2 +- test/moves/copycat.test.ts | 3 +- test/moves/destiny_bond.test.ts | 2 +- test/moves/diamond_storm.test.ts | 2 +- test/moves/dig.test.ts | 2 +- test/moves/dragon_tail.test.ts | 2 +- test/moves/dynamax_cannon.test.ts | 2 +- test/moves/effectiveness.test.ts | 2 +- test/moves/fell_stinger.test.ts | 2 +- test/moves/fly.test.ts | 2 +- test/moves/freezy_frost.test.ts | 2 +- test/moves/fusion_flare_bolt.test.ts | 2 +- test/moves/glaive_rush.test.ts | 2 +- test/moves/hard_press.test.ts | 2 +- test/moves/hyper_beam.test.ts | 2 +- test/moves/lash_out.test.ts | 2 +- test/moves/last_respects.test.ts | 2 +- test/moves/light_screen.test.ts | 3 +- test/moves/magic_coat.test.ts | 2 +- test/moves/metronome.test.ts | 3 +- test/moves/moongeist_beam.test.ts | 3 +- test/moves/pledge_moves.test.ts | 3 +- test/moves/powder.test.ts | 3 +- test/moves/protect.test.ts | 2 +- test/moves/rage_fist.test.ts | 2 +- test/moves/reflect.test.ts | 3 +- test/moves/retaliate.test.ts | 2 +- test/moves/rollout.test.ts | 2 +- test/moves/round.test.ts | 2 +- test/moves/scale_shot.test.ts | 2 +- test/moves/secret_power.test.ts | 2 +- test/moves/shell_side_arm.test.ts | 3 +- test/moves/shell_trap.test.ts | 2 +- test/moves/sketch.test.ts | 6 +- test/moves/solar_beam.test.ts | 2 +- test/moves/sparkly_swirl.test.ts | 2 +- test/moves/spectral_thief.test.ts | 2 +- test/moves/spit_up.test.ts | 4 +- test/moves/steamroller.test.ts | 4 +- test/moves/stockpile.test.ts | 2 +- test/moves/substitute.test.ts | 3 +- test/moves/swallow.test.ts | 2 +- test/moves/telekinesis.test.ts | 2 +- test/moves/tera_blast.test.ts | 5 +- test/moves/toxic.test.ts | 2 +- test/moves/triple_arrows.test.ts | 3 +- ...an-offer-you-cant-refuse-encounter.test.ts | 3 +- .../bug-type-superfan-encounter.test.ts | 2 +- .../clowning-around-encounter.test.ts | 2 +- .../dancing-lessons-encounter.test.ts | 2 +- .../fight-or-flight-encounter.test.ts | 2 +- .../encounters/part-timer-encounter.test.ts | 2 +- .../the-strong-stuff-encounter.test.ts | 2 +- .../trash-to-treasure-encounter.test.ts | 2 +- .../uncommon-breed-encounter.test.ts | 2 +- test/testUtils/helpers/moveHelper.ts | 2 +- 168 files changed, 455 insertions(+), 490 deletions(-) delete mode 100644 src/@types/damage-result.ts delete mode 100644 src/data/moves/all-moves.ts delete mode 100644 src/data/moves/pokemon-move.ts delete mode 100644 src/enums/ai-type.ts delete mode 100644 src/enums/evolution-item.ts delete mode 100644 src/enums/field-position.ts delete mode 100644 src/enums/hit-result.ts delete mode 100644 src/enums/learn-move-context.ts delete mode 100644 src/enums/species-wild-evolution-delay.ts delete mode 100644 src/interfaces/attack-move-result.ts delete mode 100644 src/interfaces/damage-calculation-result.ts delete mode 100644 src/interfaces/turn-move.ts diff --git a/src/@types/damage-result.ts b/src/@types/damage-result.ts deleted file mode 100644 index 7086d843cf4..00000000000 --- a/src/@types/damage-result.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { HitResult } from "#enums/hit-result"; - -export type DamageResult = - | HitResult.EFFECTIVE - | HitResult.SUPER_EFFECTIVE - | HitResult.NOT_VERY_EFFECTIVE - | HitResult.ONE_HIT_KO - | HitResult.CONFUSION - | HitResult.INDIRECT_KO - | HitResult.INDIRECT; diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 12dbfca68e8..dd983f2b397 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -51,7 +51,7 @@ import { initGameSpeed } from "#app/system/game-speed"; import { Arena, ArenaBase } from "#app/field/arena"; import { GameData } from "#app/system/game-data"; import { addTextObject, getTextColor, TextStyle } from "#app/ui/text"; -import { allMoves } from "./data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { MusicPreference } from "#app/system/settings/settings"; import { getDefaultModifierTypeForTier, diff --git a/src/battle.ts b/src/battle.ts index 1122db2679a..fb5af223b8f 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -15,8 +15,7 @@ import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/mod import type { PokeballType } from "#enums/pokeball"; import { trainerConfigs } from "#app/data/trainers/trainer-config"; import { SpeciesFormKey } from "#enums/species-form-key"; -import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import type { TurnMove } from "./interfaces/turn-move"; +import type { EnemyPokemon, PlayerPokemon, TurnMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattleSpec } from "#enums/battle-spec"; diff --git a/src/data/ability.ts b/src/data/ability.ts index 02cc12dd0f4..3e32a624f9f 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1,8 +1,6 @@ -import type { EnemyPokemon } from "../field/pokemon"; -import type { PokemonMove } from "./moves/pokemon-move"; +import type { EnemyPokemon, PokemonMove } from "../field/pokemon"; import type Pokemon from "../field/pokemon"; -import { MoveResult, PlayerPokemon } from "../field/pokemon"; -import { HitResult } from "#enums/hit-result"; +import { HitResult, MoveResult, PlayerPokemon } from "../field/pokemon"; import { PokemonType } from "#enums/pokemon-type"; import { BooleanHolder, NumberHolder, toDmgValue, isNullOrUndefined, randSeedItem, randSeedInt, type Constructor } from "#app/utils"; import { getPokemonNameWithAffix } from "../messages"; @@ -12,8 +10,7 @@ import { BattlerTagLapseType, GroundedTag } from "./battler-tags"; import { getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "#app/data/status-effect"; import { Gender } from "./gender"; import type Move from "./moves/move"; -import { AttackMove, FlinchAttr, OneHitKOAttr, HitHealAttr, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./moves/move"; -import { allMoves } from "./moves/all-moves"; +import { AttackMove, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./moves/move"; import { MoveFlags } from "#enums/MoveFlags"; import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index c6a1515685f..871f622f70a 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -2,13 +2,12 @@ import { globalScene } from "#app/global-scene"; import type { Arena } from "#app/field/arena"; import { PokemonType } from "#enums/pokemon-type"; import { BooleanHolder, NumberHolder, toDmgValue } from "#app/utils"; -import { allMoves } from "./moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; import { getPokemonNameWithAffix } from "#app/messages"; import type Pokemon from "#app/field/pokemon"; -import { HitResult } from "#enums/hit-result"; -import { PokemonMove } from "./moves/pokemon-move"; +import { HitResult, PokemonMove } from "#app/field/pokemon"; import { StatusEffect } from "#enums/status-effect"; import type { BattlerIndex } from "#app/battle"; import { diff --git a/src/data/balance/egg-moves.ts b/src/data/balance/egg-moves.ts index 98f3347764c..74f6a2c1afb 100644 --- a/src/data/balance/egg-moves.ts +++ b/src/data/balance/egg-moves.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { getEnumKeys, getEnumValues } from "#app/utils"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/src/data/balance/pokemon-evolutions.ts b/src/data/balance/pokemon-evolutions.ts index 70b616be8e5..17f71f3c3c9 100644 --- a/src/data/balance/pokemon-evolutions.ts +++ b/src/data/balance/pokemon-evolutions.ts @@ -14,10 +14,66 @@ import { DamageMoneyRewardModifier, ExtraModifierModifier, MoneyMultiplierModifi import { SpeciesFormKey } from "#enums/species-form-key"; import { speciesStarterCosts } from "./starters"; import i18next from "i18next"; -import { SpeciesWildEvolutionDelay } from "#enums/species-wild-evolution-delay"; -import { EvolutionItem } from "#enums/evolution-item"; +export enum SpeciesWildEvolutionDelay { + NONE, + SHORT, + MEDIUM, + LONG, + VERY_LONG, + NEVER +} + +export enum EvolutionItem { + NONE, + + LINKING_CORD, + SUN_STONE, + MOON_STONE, + LEAF_STONE, + FIRE_STONE, + WATER_STONE, + THUNDER_STONE, + ICE_STONE, + DUSK_STONE, + DAWN_STONE, + SHINY_STONE, + CRACKED_POT, + SWEET_APPLE, + TART_APPLE, + STRAWBERRY_SWEET, + UNREMARKABLE_TEACUP, + UPGRADE, + DUBIOUS_DISC, + DRAGON_SCALE, + PRISM_SCALE, + RAZOR_CLAW, + RAZOR_FANG, + REAPER_CLOTH, + ELECTIRIZER, + MAGMARIZER, + PROTECTOR, + SACHET, + WHIPPED_DREAM, + SYRUPY_APPLE, + CHIPPED_POT, + GALARICA_CUFF, + GALARICA_WREATH, + AUSPICIOUS_ARMOR, + MALICIOUS_ARMOR, + MASTERPIECE_TEACUP, + SUN_FLUTE, + MOON_FLUTE, + + BLACK_AUGURITE = 51, + PEAT_BLOCK, + METAL_ALLOY, + SCROLL_OF_DARKNESS, + SCROLL_OF_WATERS, + LEADERS_CREST +} + /** * Pokemon Evolution tuple type consisting of: * @property 0 {@linkcode Species} The species of the Pokemon. diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 396cf71d984..511c80bee72 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -1,6 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, SelfStatusMove } from "./moves/move"; -import { allMoves } from "./moves/all-moves"; +import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, SelfStatusMove, allMoves } from "./moves/move"; import { MoveFlags } from "#enums/MoveFlags"; import type Pokemon from "../field/pokemon"; import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName } from "../utils"; diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index c3dcfc49ef6..76e91485460 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -11,12 +11,12 @@ import { import { ChargeAnim, CommonAnim, CommonBattleAnim, MoveChargeAnim } from "#app/data/battle-anims"; import type Move from "#app/data/moves/move"; import { + allMoves, applyMoveAttrs, ConsecutiveUseDoublePowerAttr, HealOnAllyAttr, StatusCategoryOnAllyAttr, } from "#app/data/moves/move"; -import { allMoves } from "./moves/all-moves"; import { MoveFlags } from "#enums/MoveFlags"; import { MoveCategory } from "#enums/MoveCategory"; import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms"; @@ -24,8 +24,7 @@ import { getStatusEffectHealText } from "#app/data/status-effect"; import { TerrainType } from "#app/data/terrain"; import { PokemonType } from "#enums/pokemon-type"; import type Pokemon from "#app/field/pokemon"; -import { MoveResult } from "#app/field/pokemon"; -import { HitResult } from "#enums/hit-result"; +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"; diff --git a/src/data/berry.ts b/src/data/berry.ts index aaa0dda6e7f..8a58d337aa4 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -1,6 +1,6 @@ import { getPokemonNameWithAffix } from "../messages"; import type Pokemon from "../field/pokemon"; -import { HitResult } from "#enums/hit-result"; +import { HitResult } from "../field/pokemon"; import { getStatusEffectHealText } from "./status-effect"; import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils"; import { diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 9a3c329a70b..51616c3f00f 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -6,7 +6,7 @@ import type PokemonSpecies from "#app/data/pokemon-species"; import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; import { speciesStarterCosts } from "#app/data/balance/starters"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "./moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import type { FixedBattleConfig } from "#app/battle"; import { ClassicFixedBossWaves, BattleType, getRandomTrainerFunc } from "#app/battle"; import Trainer, { TrainerVariant } from "#app/field/trainer"; diff --git a/src/data/moves/all-moves.ts b/src/data/moves/all-moves.ts deleted file mode 100644 index c7b6d11a08d..00000000000 --- a/src/data/moves/all-moves.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type Move from "./move"; - -export const allMoves: Move[] = []; diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 591894f5f1e..962a13bb840 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -12,17 +12,16 @@ import { TypeBoostTag, } from "../battler-tags"; import { getPokemonNameWithAffix } from "../../messages"; -import type { AttackMoveResult } from "#app/interfaces/attack-move-result"; -import type { TurnMove } from "#app/interfaces/turn-move"; +import type { AttackMoveResult, TurnMove } from "../../field/pokemon"; import type Pokemon from "../../field/pokemon"; import { EnemyPokemon, + FieldPosition, + HitResult, MoveResult, PlayerPokemon, + PokemonMove, } from "../../field/pokemon"; -import { HitResult } from "#enums/hit-result"; -import { FieldPosition } from "#enums/field-position"; -import { PokemonMove } from "./pokemon-move"; import { getNonVolatileStatusEffects, getStatusEffectHealText, @@ -122,7 +121,6 @@ import { MoveFlags } from "#enums/MoveFlags"; import { MoveEffectTrigger } from "#enums/MoveEffectTrigger"; import { MultiHitType } from "#enums/MultiHitType"; import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves } from "./invalid-moves"; -import { allMoves } from "./all-moves"; type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean; type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean; @@ -8259,7 +8257,11 @@ export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveT return { targets: set.filter(p => p?.isActive(true)).map(p => p.getBattlerIndex()).filter(t => t !== undefined), multiple }; } -allMoves.push(new SelfStatusMove(Moves.NONE, PokemonType.NORMAL, MoveCategory.STATUS, -1, -1, 0, 1)); +export const allMoves: Move[] = [ + new SelfStatusMove(Moves.NONE, PokemonType.NORMAL, MoveCategory.STATUS, -1, -1, 0, 1), +]; + +export const selfStatLowerMoves: Moves[] = []; export function initMoves() { allMoves.push( @@ -11248,4 +11250,9 @@ export function initMoves() { new AttackMove(Moves.MALIGNANT_CHAIN, PokemonType.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9) .attr(StatusEffectAttr, StatusEffect.TOXIC) ); + allMoves.map(m => { + if (m.getAttrs(StatStageChangeAttr).some(a => a.selfTarget && a.stages < 0)) { + selfStatLowerMoves.push(m.id); + } + }); } diff --git a/src/data/moves/pokemon-move.ts b/src/data/moves/pokemon-move.ts deleted file mode 100644 index 49ccaba698b..00000000000 --- a/src/data/moves/pokemon-move.ts +++ /dev/null @@ -1,93 +0,0 @@ -import * as Utils from "#app/utils"; -import { allMoves } from "./all-moves"; -import type { Moves } from "#enums/moves"; -import type Pokemon from "#app/field/pokemon"; -import type Move from "./move"; - -/** - * Wrapper class for the {@linkcode Move} class for Pokemon to interact with. - * These are the moves assigned to a {@linkcode Pokemon} object. - * It links to {@linkcode Move} class via the move ID. - * Compared to {@linkcode Move}, this class also tracks if a move has received. - * PP Ups, amount of PP used, and things like that. - * @see {@linkcode isUsable} - checks if move is restricted, out of PP, or not implemented. - * @see {@linkcode getMove} - returns {@linkcode Move} object by looking it up via ID. - * @see {@linkcode usePp} - removes a point of PP from the move. - * @see {@linkcode getMovePp} - returns amount of PP a move currently has. - * @see {@linkcode getPpRatio} - returns the current PP amount / max PP amount. - * @see {@linkcode getName} - returns name of {@linkcode Move}. - **/ -export class PokemonMove { - public moveId: Moves; - public ppUsed: number; - public ppUp: number; - public virtual: boolean; - - /** - * If defined and nonzero, overrides the maximum PP of the move (e.g., due to move being copied by Transform). - * This also nullifies all effects of `ppUp`. - */ - public maxPpOverride?: number; - - constructor(moveId: Moves, ppUsed = 0, ppUp = 0, virtual = false, maxPpOverride?: number) { - this.moveId = moveId; - this.ppUsed = ppUsed; - this.ppUp = ppUp; - this.virtual = virtual; - this.maxPpOverride = maxPpOverride; - } - - /** - * Checks whether the move can be selected or performed by a Pokemon, without consideration for the move's targets. - * The move is unusable if it is out of PP, restricted by an effect, or unimplemented. - * - * @param pokemon - {@linkcode Pokemon} that would be using this move - * @param ignorePp - If `true`, skips the PP check - * @param ignoreRestrictionTags - If `true`, skips the check for move restriction tags (see {@link MoveRestrictionBattlerTag}) - * @returns `true` if the move can be selected and used by the Pokemon, otherwise `false`. - */ - isUsable(pokemon: Pokemon, ignorePp = false, ignoreRestrictionTags = false): boolean { - if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)) { - return false; - } - - if (this.getMove().name.endsWith(" (N)")) { - return false; - } - - return ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1; - } - - getMove(): Move { - return allMoves[this.moveId]; - } - - /** - * Sets {@link ppUsed} for this move and ensures the value does not exceed {@link getMovePp} - * @param {number} count Amount of PP to use - */ - usePp(count = 1) { - this.ppUsed = Math.min(this.ppUsed + count, this.getMovePp()); - } - - getMovePp(): number { - return this.maxPpOverride || this.getMove().pp + this.ppUp * Utils.toDmgValue(this.getMove().pp / 5); - } - - getPpRatio(): number { - return 1 - this.ppUsed / this.getMovePp(); - } - - getName(): string { - return this.getMove().name; - } - - /** - * Copies an existing move or creates a valid PokemonMove object from json representing one - * @param source - The data for the move to copy - * @return A valid pokemonmove object - */ - static loadMove(source: PokemonMove | any): PokemonMove { - return new PokemonMove(source.moveId, source.ppUsed, source.ppUp, source.virtual, source.maxPpOverride); - } -} diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index b781f14fad1..85f40a41e51 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -7,8 +7,7 @@ import { transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; import type { BerryModifierType, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; 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 c6971c42364..1e4c9a3b957 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -24,7 +24,7 @@ import { TrainerType } from "#enums/trainer-type"; import { Species } from "#enums/species"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +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 { Moves } from "#enums/moves"; @@ -50,7 +50,7 @@ import { } from "#app/modifier/modifier"; import i18next from "i18next"; import MoveInfoOverlay from "#app/ui/move-info-overlay"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 15fad6dacbf..eca99fc0c13 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -37,7 +37,7 @@ import { Mode } from "#app/ui/ui"; import i18next from "i18next"; import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler"; import type { PlayerPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { Ability } from "#app/data/ability"; import { BerryModifier } from "#app/modifier/modifier"; import { BerryType } from "#enums/berry-type"; diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index 90ea6a69c0d..75527e1f8c1 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -23,8 +23,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { TrainerSlot } from "#enums/trainer-slot"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { modifierTypes } from "#app/modifier/modifier-type"; import { LearnMovePhase } from "#app/phases/learn-move-phase"; diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index 4e330fab3d9..a1964aa5ab4 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -7,8 +7,7 @@ import { setEncounterExp, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; +import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import { modifierTypes } from "#app/modifier/modifier-type"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index d868184a7fa..6118fe3d0de 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -26,7 +26,7 @@ import { Gender } from "#app/data/gender"; import { PokemonType } from "#enums/pokemon-type"; import { BattlerIndex } from "#app/battle"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { Moves } from "#enums/moves"; import { EncounterBattleAnim } from "#app/data/battle-anims"; import { WeatherType } from "#enums/weather-type"; 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 a9fc24c70b7..282c6c149ff 100644 --- a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts +++ b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts @@ -13,7 +13,7 @@ import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/myst import { TrainerSlot } from "#enums/trainer-slot"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { FieldPosition } from "#enums/field-position"; +import { FieldPosition } from "#app/field/pokemon"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; diff --git a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts index fce496e5e17..f80620647b0 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -26,8 +26,7 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode import { NumberHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle, randSeedItem } from "#app/utils"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; import type { PokemonHeldItemModifier } from "#app/modifier/modifier"; import { HiddenAbilityRateBoosterModifier, diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 030678b77b1..97fd5783ebb 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -11,7 +11,7 @@ import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encount import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; const OPTION_1_REQUIRED_MOVE = Moves.SURF; const OPTION_2_REQUIRED_MOVE = Moves.FLY; diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index 97a17af43d0..bfa1204a8ba 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -21,8 +21,7 @@ import { import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { Moves } from "#enums/moves"; import { BattlerIndex } from "#app/battle"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { AiType } from "#enums/ai-type"; +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"; 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 af0363f37e3..c994c6e993f 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -17,7 +17,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Species } from "#enums/species"; import { Nature } from "#enums/nature"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { Moves } from "#enums/moves"; diff --git a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts index 2203ac041f8..e60fe0ddc18 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -25,7 +25,7 @@ import { ModifierTier } from "#app/modifier/modifier-tier"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Moves } from "#enums/moves"; import { BattlerIndex } from "#app/battle"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { randSeedInt } from "#app/utils"; diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index 4e3c238aeba..ed1866c7a1b 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -10,7 +10,7 @@ import { import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups"; import type Pokemon from "#app/field/pokemon"; import type { EnemyPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { globalScene } from "#app/global-scene"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index be0c0bdff54..22ec52e976c 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -16,7 +16,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { NumberHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils"; import type PokemonSpecies from "#app/data/pokemon-species"; import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species"; diff --git a/src/data/mystery-encounters/mystery-encounter-requirements.ts b/src/data/mystery-encounters/mystery-encounter-requirements.ts index 0c146fe485d..f9aedf2c1a7 100644 --- a/src/data/mystery-encounters/mystery-encounter-requirements.ts +++ b/src/data/mystery-encounters/mystery-encounter-requirements.ts @@ -1,7 +1,6 @@ import { globalScene } from "#app/global-scene"; import { allAbilities } from "#app/data/ability"; -import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; -import { EvolutionItem } from "#enums/evolution-item"; +import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { Nature } from "#enums/nature"; import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms"; import { StatusEffect } from "#enums/status-effect"; diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index 8010983f9f3..ff098d4d7dd 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -1,6 +1,5 @@ import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type { PokemonMove } from "../moves/pokemon-move"; +import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; diff --git a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts index 598b0ffae70..a7ffe3e26ca 100644 --- a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts +++ b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts @@ -1,6 +1,6 @@ import type { Moves } from "#app/enums/moves"; import type { PlayerPokemon } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { isNullOrUndefined } from "#app/utils"; import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { globalScene } from "#app/global-scene"; diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 6ab650d5f9b..a9f6b787878 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -7,12 +7,9 @@ import { WEIGHT_INCREMENT_ON_SPAWN_MISS, } from "#app/data/mystery-encounters/mystery-encounters"; import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type { AiType } from "#enums/ai-type"; +import type { AiType, PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { EnemyPokemon, PokemonSummonData } from "#app/field/pokemon"; -import { FieldPosition } from "#enums/field-position"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { EnemyPokemon, FieldPosition, PokemonMove, PokemonSummonData } from "#app/field/pokemon"; import type { CustomModifierSettings, ModifierType } from "#app/modifier/modifier-type"; import { getPartyLuckValue, diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index 6f36bfde74f..63e166c7fc4 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -1,7 +1,7 @@ import { PokemonFormChangeItemModifier } from "../modifier/modifier"; import type Pokemon from "../field/pokemon"; import { StatusEffect } from "#enums/status-effect"; -import { allMoves } from "./moves/all-moves"; +import { allMoves } from "./moves/move"; import { MoveCategory } from "#enums/MoveCategory"; import type { Constructor, nil } from "#app/utils"; import { Abilities } from "#enums/abilities"; diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index ced828fbc6b..a27c00121dc 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -13,8 +13,11 @@ import { uncatchableSpecies } from "#app/data/balance/biomes"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { GrowthRate } from "#app/data/exp"; import type { EvolutionLevel } from "#app/data/balance/pokemon-evolutions"; -import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; -import { SpeciesWildEvolutionDelay } from "#enums/species-wild-evolution-delay"; +import { + SpeciesWildEvolutionDelay, + pokemonEvolutions, + pokemonPrevolutions, +} from "#app/data/balance/pokemon-evolutions"; import { PokemonType } from "#enums/pokemon-type"; import type { LevelMoves } from "#app/data/balance/pokemon-level-moves"; import { diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index c87f72bd912..0ab7119dab9 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { toReadableString, isNullOrUndefined, randSeedItem, randSeedInt } from "#app/utils"; import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { getPokemonSpecies } from "#app/data/pokemon-species"; diff --git a/src/enums/ai-type.ts b/src/enums/ai-type.ts deleted file mode 100644 index 13931172a4a..00000000000 --- a/src/enums/ai-type.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum AiType { - RANDOM, - SMART_RANDOM, - SMART -} diff --git a/src/enums/evolution-item.ts b/src/enums/evolution-item.ts deleted file mode 100644 index 3b5e493b378..00000000000 --- a/src/enums/evolution-item.ts +++ /dev/null @@ -1,48 +0,0 @@ -export enum EvolutionItem { - NONE, - - LINKING_CORD, - SUN_STONE, - MOON_STONE, - LEAF_STONE, - FIRE_STONE, - WATER_STONE, - THUNDER_STONE, - ICE_STONE, - DUSK_STONE, - DAWN_STONE, - SHINY_STONE, - CRACKED_POT, - SWEET_APPLE, - TART_APPLE, - STRAWBERRY_SWEET, - UNREMARKABLE_TEACUP, - UPGRADE, - DUBIOUS_DISC, - DRAGON_SCALE, - PRISM_SCALE, - RAZOR_CLAW, - RAZOR_FANG, - REAPER_CLOTH, - ELECTIRIZER, - MAGMARIZER, - PROTECTOR, - SACHET, - WHIPPED_DREAM, - SYRUPY_APPLE, - CHIPPED_POT, - GALARICA_CUFF, - GALARICA_WREATH, - AUSPICIOUS_ARMOR, - MALICIOUS_ARMOR, - MASTERPIECE_TEACUP, - SUN_FLUTE, - MOON_FLUTE, - - BLACK_AUGURITE = 51, - PEAT_BLOCK, - METAL_ALLOY, - SCROLL_OF_DARKNESS, - SCROLL_OF_WATERS, - LEADERS_CREST -} diff --git a/src/enums/field-position.ts b/src/enums/field-position.ts deleted file mode 100644 index 5b7f9c6c570..00000000000 --- a/src/enums/field-position.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum FieldPosition { - CENTER, - LEFT, - RIGHT -} diff --git a/src/enums/hit-result.ts b/src/enums/hit-result.ts deleted file mode 100644 index 3e62587dd6c..00000000000 --- a/src/enums/hit-result.ts +++ /dev/null @@ -1,15 +0,0 @@ -export enum HitResult { - EFFECTIVE = 1, - SUPER_EFFECTIVE, - NOT_VERY_EFFECTIVE, - ONE_HIT_KO, - NO_EFFECT, - STATUS, - HEAL, - FAIL, - MISS, - INDIRECT, - IMMUNE, - CONFUSION, - INDIRECT_KO -} diff --git a/src/enums/learn-move-context.ts b/src/enums/learn-move-context.ts deleted file mode 100644 index 26001cbcce8..00000000000 --- a/src/enums/learn-move-context.ts +++ /dev/null @@ -1,8 +0,0 @@ -export enum LearnMoveContext { - MISC, - LEVEL_UP, - RELEARN, - EVOLUTION, - EVOLUTION_FUSED, // If fusionSpecies has Evolved - EVOLUTION_FUSED_BASE, // If fusion's base species has Evolved -} diff --git a/src/enums/species-wild-evolution-delay.ts b/src/enums/species-wild-evolution-delay.ts deleted file mode 100644 index 7555dc0e8f6..00000000000 --- a/src/enums/species-wild-evolution-delay.ts +++ /dev/null @@ -1,8 +0,0 @@ -export enum SpeciesWildEvolutionDelay { - NONE, - SHORT, - MEDIUM, - LONG, - VERY_LONG, - NEVER -} diff --git a/src/field/damage-number-handler.ts b/src/field/damage-number-handler.ts index 3bb001bf005..a527b148fff 100644 --- a/src/field/damage-number-handler.ts +++ b/src/field/damage-number-handler.ts @@ -1,7 +1,7 @@ import { TextStyle, addTextObject } from "../ui/text"; -import type { DamageResult } from "#app/@types/damage-result"; +import type { DamageResult } from "./pokemon"; import type Pokemon from "./pokemon"; -import { HitResult } from "#enums/hit-result"; +import { HitResult } from "./pokemon"; import { formatStat, fixedInt } from "#app/utils"; import type { BattlerIndex } from "../battle"; import { globalScene } from "#app/global-scene"; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 162a5118f65..b59b7ba01fe 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -17,6 +17,7 @@ import { applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, + allMoves, TypelessAttr, CritOnlyAttr, getMoveTargets, @@ -41,7 +42,6 @@ import { VariableMoveTypeChartAttr, HpSplitAttr, } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; import type { PokemonSpeciesForm } from "#app/data/pokemon-species"; @@ -214,7 +214,7 @@ import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeLapseTeraTrigger, SpeciesFormChangeMoveLearnedTrigger, - SpeciesFormChangePostMoveTrigger, + SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms"; import { TerrainType } from "#app/data/terrain"; import type { TrainerSlot } from "#enums/trainer-slot"; @@ -259,15 +259,21 @@ 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"; -import { LearnMoveContext } from "#enums/learn-move-context"; -import { TurnMove } from "#app/interfaces/turn-move"; -import { AiType } from "#enums/ai-type"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; -import { DamageCalculationResult } from "#app/interfaces/damage-calculation-result"; -import { FieldPosition } from "#enums/field-position"; -import { AttackMoveResult } from "#app/interfaces/attack-move-result"; -import { HitResult } from "#enums/hit-result"; -import { DamageResult } from "#app/@types/damage-result"; + +export enum LearnMoveSituation { + MISC, + LEVEL_UP, + RELEARN, + EVOLUTION, + EVOLUTION_FUSED, // If fusionSpecies has Evolved + EVOLUTION_FUSED_BASE, // If fusion's base species has Evolved +} + +export enum FieldPosition { + CENTER, + LEFT, + RIGHT, +} export default abstract class Pokemon extends Phaser.GameObjects.Container { public id: number; @@ -2919,7 +2925,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { includeEvolutionMoves = false, simulateEvolutionChain = false, includeRelearnerMoves = false, - learnSituation: LearnMoveContext = LearnMoveContext.MISC, + learnSituation: LearnMoveSituation = LearnMoveSituation.MISC, ): LevelMoves { const ret: LevelMoves = []; let levelMoves: LevelMoves = []; @@ -2927,7 +2933,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { startingLevel = this.level; } if ( - learnSituation === LearnMoveContext.EVOLUTION_FUSED && + learnSituation === LearnMoveSituation.EVOLUTION_FUSED && this.fusionSpecies ) { // For fusion evolutions, get ONLY the moves of the component mon that evolved @@ -2979,7 +2985,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if ( this.fusionSpecies && - learnSituation !== LearnMoveContext.EVOLUTION_FUSED_BASE + learnSituation !== LearnMoveSituation.EVOLUTION_FUSED_BASE ) { // For fusion evolutions, get ONLY the moves of the component mon that evolved if (simulateEvolutionChain) { @@ -7773,6 +7779,24 @@ interface IllusionData { level?: number } +export interface TurnMove { + move: Moves; + targets: BattlerIndex[]; + result?: MoveResult; + virtual?: boolean; + turn?: number; + ignorePP?: boolean; +} + +export interface AttackMoveResult { + move: Moves; + result: DamageResult; + damage: number; + critical: boolean; + sourceId: number; + sourceBattlerIndex: BattlerIndex; +} + export class PokemonSummonData { /** [Atk, Def, SpAtk, SpDef, Spd, Acc, Eva] */ public statStages: number[] = [0, 0, 0, 0, 0, 0, 0]; @@ -7845,6 +7869,12 @@ export class PokemonTurnData { public extraTurns = 0; } +export enum AiType { + RANDOM, + SMART_RANDOM, + SMART, +} + export enum MoveResult { PENDING, SUCCESS, @@ -7852,3 +7882,151 @@ export enum MoveResult { MISS, OTHER, } + +export enum HitResult { + EFFECTIVE = 1, + SUPER_EFFECTIVE, + NOT_VERY_EFFECTIVE, + ONE_HIT_KO, + NO_EFFECT, + STATUS, + HEAL, + FAIL, + MISS, + INDIRECT, + IMMUNE, + CONFUSION, + INDIRECT_KO, +} + +export type DamageResult = + | HitResult.EFFECTIVE + | HitResult.SUPER_EFFECTIVE + | HitResult.NOT_VERY_EFFECTIVE + | HitResult.ONE_HIT_KO + | HitResult.CONFUSION + | HitResult.INDIRECT_KO + | HitResult.INDIRECT; + +/** Interface containing the results of a damage calculation for a given move */ +export interface DamageCalculationResult { + /** `true` if the move was cancelled (thus suppressing "No Effect" messages) */ + cancelled: boolean; + /** The effectiveness of the move */ + result: HitResult; + /** The damage dealt by the move */ + damage: number; +} + +/** + * Wrapper class for the {@linkcode Move} class for Pokemon to interact with. + * These are the moves assigned to a {@linkcode Pokemon} object. + * It links to {@linkcode Move} class via the move ID. + * Compared to {@linkcode Move}, this class also tracks if a move has received. + * PP Ups, amount of PP used, and things like that. + * @see {@linkcode isUsable} - checks if move is restricted, out of PP, or not implemented. + * @see {@linkcode getMove} - returns {@linkcode Move} object by looking it up via ID. + * @see {@linkcode usePp} - removes a point of PP from the move. + * @see {@linkcode getMovePp} - returns amount of PP a move currently has. + * @see {@linkcode getPpRatio} - returns the current PP amount / max PP amount. + * @see {@linkcode getName} - returns name of {@linkcode Move}. + **/ +export class PokemonMove { + public moveId: Moves; + public ppUsed: number; + public ppUp: number; + public virtual: boolean; + + /** + * If defined and nonzero, overrides the maximum PP of the move (e.g., due to move being copied by Transform). + * This also nullifies all effects of `ppUp`. + */ + public maxPpOverride?: number; + + constructor( + moveId: Moves, + ppUsed = 0, + ppUp = 0, + virtual = false, + maxPpOverride?: number, + ) { + this.moveId = moveId; + this.ppUsed = ppUsed; + this.ppUp = ppUp; + this.virtual = virtual; + this.maxPpOverride = maxPpOverride; + } + + /** + * Checks whether the move can be selected or performed by a Pokemon, without consideration for the move's targets. + * The move is unusable if it is out of PP, restricted by an effect, or unimplemented. + * + * @param {Pokemon} pokemon {@linkcode Pokemon} that would be using this move + * @param {boolean} ignorePp If `true`, skips the PP check + * @param {boolean} ignoreRestrictionTags If `true`, skips the check for move restriction tags (see {@link MoveRestrictionBattlerTag}) + * @returns `true` if the move can be selected and used by the Pokemon, otherwise `false`. + */ + isUsable( + pokemon: Pokemon, + ignorePp = false, + ignoreRestrictionTags = false, + ): boolean { + if ( + this.moveId && + !ignoreRestrictionTags && + pokemon.isMoveRestricted(this.moveId, pokemon) + ) { + return false; + } + + if (this.getMove().name.endsWith(" (N)")) { + return false; + } + + return ( + ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1 + ); + } + + getMove(): Move { + return allMoves[this.moveId]; + } + + /** + * Sets {@link ppUsed} for this move and ensures the value does not exceed {@link getMovePp} + * @param {number} count Amount of PP to use + */ + usePp(count = 1) { + this.ppUsed = Math.min(this.ppUsed + count, this.getMovePp()); + } + + getMovePp(): number { + return ( + this.maxPpOverride || + this.getMove().pp + this.ppUp * toDmgValue(this.getMove().pp / 5) + ); + } + + getPpRatio(): number { + return 1 - this.ppUsed / this.getMovePp(); + } + + getName(): string { + return this.getMove().name; + } + + /** + * Copies an existing move or creates a valid PokemonMove object from json representing one + * @param {PokemonMove | any} source The data for the move to copy + * @return {PokemonMove} A valid pokemonmove object + */ + static loadMove(source: PokemonMove | any): PokemonMove { + return new PokemonMove( + source.moveId, + source.ppUsed, + source.ppUp, + source.virtual, + source.maxPpOverride, + ); + } +} diff --git a/src/interfaces/attack-move-result.ts b/src/interfaces/attack-move-result.ts deleted file mode 100644 index f91d31a69ee..00000000000 --- a/src/interfaces/attack-move-result.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { BattlerIndex } from "#app/battle"; -import type { DamageResult } from "#app/@types/damage-result"; -import type { Moves } from "#enums/moves"; - -export interface AttackMoveResult { - move: Moves; - result: DamageResult; - damage: number; - critical: boolean; - sourceId: number; - sourceBattlerIndex: BattlerIndex; -} diff --git a/src/interfaces/damage-calculation-result.ts b/src/interfaces/damage-calculation-result.ts deleted file mode 100644 index 1220ff7b57d..00000000000 --- a/src/interfaces/damage-calculation-result.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { HitResult } from "#enums/hit-result"; - -/** Interface containing the results of a damage calculation for a given move */ -export interface DamageCalculationResult { - /** `true` if the move was cancelled (thus suppressing "No Effect" messages) */ - cancelled: boolean; - /** The effectiveness of the move */ - result: HitResult; - /** The damage dealt by the move */ - damage: number; -} diff --git a/src/interfaces/turn-move.ts b/src/interfaces/turn-move.ts deleted file mode 100644 index 639d309256e..00000000000 --- a/src/interfaces/turn-move.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { BattlerIndex } from "#app/battle"; -import type { MoveResult } from "#app/field/pokemon"; -import type { Moves } from "#enums/moves"; - -export interface TurnMove { - move: Moves; - targets: BattlerIndex[]; - result?: MoveResult; - virtual?: boolean; - turn?: number; - ignorePP?: boolean; -} diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 852593d922c..8feb60c7778 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -1,10 +1,8 @@ import { globalScene } from "#app/global-scene"; -import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; -import { EvolutionItem } from "#enums/evolution-item"; +import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { tmPoolTiers, tmSpecies } from "#app/data/balance/tms"; import { getBerryEffectDescription, getBerryName } from "#app/data/berry"; -import { AttackMove } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, AttackMove } from "#app/data/moves/move"; import { getNatureName, getNatureStatMultiplier } from "#app/data/nature"; import { getPokeballCatchMultiplier, getPokeballName, MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball"; import { @@ -15,8 +13,7 @@ import { } from "#app/data/pokemon-forms"; import { getStatusEffectDescriptor } from "#app/data/status-effect"; import { PokemonType } from "#enums/pokemon-type"; -import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; +import type { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 7860d0f9296..80f14ba22ce 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1,7 +1,7 @@ import { FusionSpeciesFormEvolution, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry"; import { getLevelTotalExp } from "#app/data/exp"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball"; import { type FormChangeItem, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms"; import { getStatusEffectHealText } from "#app/data/status-effect"; diff --git a/src/overrides.ts b/src/overrides.ts index 49efb5eed33..21c72cd7b98 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -1,5 +1,5 @@ import { type PokeballCounts } from "#app/battle-scene"; -import { EvolutionItem } from "#enums/evolution-item"; +import { EvolutionItem } from "#app/data/balance/pokemon-evolutions"; import { Gender } from "#app/data/gender"; import { FormChangeItem } from "#app/data/pokemon-forms"; import { Variant } from "#app/sprites/variant"; diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index c65f121d20e..8691ac453ca 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -11,9 +11,8 @@ import { BattlerTagType } from "#app/enums/battler-tag-type"; import { Biome } from "#app/enums/biome"; import { Moves } from "#app/enums/moves"; import { PokeballType } from "#enums/pokeball"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type { TurnMove } from "#app/interfaces/turn-move"; -import { FieldPosition } from "#enums/field-position"; +import type { PlayerPokemon, TurnMove } from "#app/field/pokemon"; +import { FieldPosition } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { Command } from "#app/ui/command-ui-handler"; import { Mode } from "#app/ui/ui"; diff --git a/src/phases/damage-anim-phase.ts b/src/phases/damage-anim-phase.ts index 91b21376515..696a2e55b6f 100644 --- a/src/phases/damage-anim-phase.ts +++ b/src/phases/damage-anim-phase.ts @@ -1,8 +1,7 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#app/battle"; import { BattleSpec } from "#enums/battle-spec"; -import type { DamageResult } from "#app/@types/damage-result"; -import { HitResult } from "#enums/hit-result"; +import { type DamageResult, HitResult } from "#app/field/pokemon"; import { fixedInt } from "#app/utils"; import { PokemonPhase } from "#app/phases/pokemon-phase"; diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index 9e28de32c4a..15f3d102e41 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -11,7 +11,7 @@ import { TrainerSlot } from "#enums/trainer-slot"; import { getRandomWeatherType } from "#app/data/weather"; import { EncounterPhaseEvent } from "#app/events/battle-scene"; import type Pokemon from "#app/field/pokemon"; -import { FieldPosition } from "#enums/field-position"; +import { FieldPosition } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier"; import { ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; diff --git a/src/phases/evolution-phase.ts b/src/phases/evolution-phase.ts index 076b7dec80d..203c7542eff 100644 --- a/src/phases/evolution-phase.ts +++ b/src/phases/evolution-phase.ts @@ -10,7 +10,7 @@ import { Mode } from "#app/ui/ui"; import { cos, sin } from "#app/field/anims"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { LearnMoveContext } from "#enums/learn-move-context"; +import { LearnMoveSituation } from "#app/field/pokemon"; import { getTypeRgb } from "#app/data/type"; import i18next from "i18next"; import { getPokemonNameWithAffix } from "#app/messages"; @@ -343,11 +343,11 @@ export class EvolutionPhase extends Phase { this.evolutionHandler.canCancel = false; this.pokemon.evolve(this.evolution, this.pokemon.species).then(() => { - const learnSituation: LearnMoveContext = this.fusionSpeciesEvolved - ? LearnMoveContext.EVOLUTION_FUSED + const learnSituation: LearnMoveSituation = this.fusionSpeciesEvolved + ? LearnMoveSituation.EVOLUTION_FUSED : this.pokemon.fusionSpecies - ? LearnMoveContext.EVOLUTION_FUSED_BASE - : LearnMoveContext.EVOLUTION; + ? LearnMoveSituation.EVOLUTION_FUSED_BASE + : LearnMoveSituation.EVOLUTION; const levelMoves = this.pokemon .getLevelMoves(this.lastLevel + 1, true, false, false, learnSituation) .filter(lm => lm[0] === EVOLVE_MOVE); diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 4c418679047..7e1ae4ec07b 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -12,16 +12,13 @@ import { import type { DestinyBondTag, GrudgeTag } from "#app/data/battler-tags"; import { BattlerTagLapseType } from "#app/data/battler-tags"; import { battleSpecDialogue } from "#app/data/dialogue"; -import { PostVictoryStatStageChangeAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, PostVictoryStatStageChangeAttr } from "#app/data/moves/move"; import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms"; 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 { PlayerPokemon } from "#app/field/pokemon"; -import { HitResult } from "#enums/hit-result"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { HitResult, PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { PokemonInstantReviveModifier } from "#app/modifier/modifier"; import { SwitchType } from "#enums/switch-type"; diff --git a/src/phases/learn-move-phase.ts b/src/phases/learn-move-phase.ts index a939298f620..4107a9cf087 100644 --- a/src/phases/learn-move-phase.ts +++ b/src/phases/learn-move-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; import type Move from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms"; import { Moves } from "#enums/moves"; import { getPokemonNameWithAffix } from "#app/messages"; diff --git a/src/phases/move-charge-phase.ts b/src/phases/move-charge-phase.ts index ccaf6d054b9..26ad85bbe03 100644 --- a/src/phases/move-charge-phase.ts +++ b/src/phases/move-charge-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#app/battle"; import { MoveChargeAnim } from "#app/data/battle-anims"; import { applyMoveChargeAttrs, MoveEffectAttr, InstantChargeAttr } from "#app/data/moves/move"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; +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"; diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index c13c411be68..acc7ac0f63a 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -49,10 +49,9 @@ import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms"; import { PokemonType } from "#enums/pokemon-type"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { MoveResult } from "#app/field/pokemon"; -import { HitResult } from "#enums/hit-result"; +import { HitResult, MoveResult } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { ContactHeldItemTransferChanceModifier, diff --git a/src/phases/move-header-phase.ts b/src/phases/move-header-phase.ts index c255b45190b..c320df462d1 100644 --- a/src/phases/move-header-phase.ts +++ b/src/phases/move-header-phase.ts @@ -1,5 +1,5 @@ import { applyMoveAttrs, MoveHeaderAttr } from "#app/data/moves/move"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; +import type { PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { BattlePhase } from "./battle-phase"; diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 032ac6d06ab..478229dcae8 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -16,6 +16,7 @@ import { CommonAnim } from "#app/data/battle-anims"; import { BattlerTagLapseType, CenterOfAttentionTag } from "#app/data/battler-tags"; import { AddArenaTrapTagAttr, + allMoves, applyMoveAttrs, BypassRedirectAttr, BypassSleepAttr, @@ -26,14 +27,13 @@ import { PreMoveMessageAttr, PreUseInterruptAttr, } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; import { MoveFlags } from "#enums/MoveFlags"; import { SpeciesFormChangePreMoveTrigger } from "#app/data/pokemon-forms"; import { getStatusEffectActivationText, getStatusEffectHealText } from "#app/data/status-effect"; import { PokemonType } from "#enums/pokemon-type"; import { getTerrainBlockMessage, getWeatherBlockMessage } from "#app/data/weather"; import { MoveUsedEvent } from "#app/events/battle-scene"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; +import type { PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; diff --git a/src/phases/pokemon-heal-phase.ts b/src/phases/pokemon-heal-phase.ts index 84dc8a5e116..651c625b23a 100644 --- a/src/phases/pokemon-heal-phase.ts +++ b/src/phases/pokemon-heal-phase.ts @@ -3,7 +3,7 @@ import type { BattlerIndex } from "#app/battle"; import { CommonAnim } from "#app/data/battle-anims"; import { getStatusEffectHealText } from "#app/data/status-effect"; import { StatusEffect } from "#app/enums/status-effect"; -import { HitResult } from "#enums/hit-result"; +import { HitResult } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { HealingBoosterModifier } from "#app/modifier/modifier"; import { HealAchv } from "#app/system/achv"; diff --git a/src/phases/pokemon-transform-phase.ts b/src/phases/pokemon-transform-phase.ts index fb9a28a5a26..b33689321b5 100644 --- a/src/phases/pokemon-transform-phase.ts +++ b/src/phases/pokemon-transform-phase.ts @@ -2,7 +2,7 @@ import type { BattlerIndex } from "#app/battle"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { EFFECTIVE_STATS, BATTLE_STATS } from "#enums/stat"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { globalScene } from "#app/global-scene"; import { PokemonPhase } from "./pokemon-phase"; import { getPokemonNameWithAffix } from "#app/messages"; diff --git a/src/phases/select-target-phase.ts b/src/phases/select-target-phase.ts index edd56ba60ed..035eaff41fa 100644 --- a/src/phases/select-target-phase.ts +++ b/src/phases/select-target-phase.ts @@ -5,7 +5,7 @@ import { Mode } from "#app/ui/ui"; import { CommandPhase } from "./command-phase"; import { PokemonPhase } from "./pokemon-phase"; import i18next from "#app/plugins/i18n"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; export class SelectTargetPhase extends PokemonPhase { // biome-ignore lint/complexity/noUselessConstructor: This makes `fieldIndex` required diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index e053b18e4d7..7379d509e55 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -5,7 +5,7 @@ import { TrainerSlot } from "#enums/trainer-slot"; import { PlayerGender } from "#app/enums/player-gender"; import { addPokeballOpenParticles } from "#app/field/anims"; import type Pokemon from "#app/field/pokemon"; -import { FieldPosition } from "#enums/field-position"; +import { FieldPosition } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import i18next from "i18next"; import { PartyMemberPokemonPhase } from "./party-member-pokemon-phase"; diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index f39a3e62bb6..d63cdb90f25 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -6,8 +6,7 @@ import { PreSummonAbAttr, PreSwitchOutAbAttr, } from "#app/data/ability"; -import { ForceSwitchOutAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, ForceSwitchOutAttr } from "#app/data/moves/move"; import { getPokeballTintColor } from "#app/data/pokeball"; import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms"; import { TrainerSlot } from "#enums/trainer-slot"; diff --git a/src/phases/toggle-double-position-phase.ts b/src/phases/toggle-double-position-phase.ts index c4766f888aa..37f47d5cf95 100644 --- a/src/phases/toggle-double-position-phase.ts +++ b/src/phases/toggle-double-position-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { FieldPosition } from "#enums/field-position"; +import { FieldPosition } from "#app/field/pokemon"; import { BattlePhase } from "./battle-phase"; export class ToggleDoublePositionPhase extends BattlePhase { diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index 5941e0af163..d5b4160fe1b 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -1,10 +1,9 @@ import { applyAbAttrs, BypassSpeedChanceAbAttr, PreventBypassSpeedChanceAbAttr } from "#app/data/ability"; -import { MoveHeaderAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, MoveHeaderAttr } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { Stat } from "#app/enums/stat"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +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"; diff --git a/src/phases/weather-effect-phase.ts b/src/phases/weather-effect-phase.ts index 256894457fc..5284c9fba85 100644 --- a/src/phases/weather-effect-phase.ts +++ b/src/phases/weather-effect-phase.ts @@ -14,7 +14,7 @@ import { getWeatherDamageMessage, getWeatherLapseMessage } from "#app/data/weath import { BattlerTagType } from "#app/enums/battler-tag-type"; import { WeatherType } from "#app/enums/weather-type"; import type Pokemon from "#app/field/pokemon"; -import { HitResult } from "#enums/hit-result"; +import { HitResult } from "#app/field/pokemon"; import { BooleanHolder, toDmgValue } from "#app/utils"; import { CommonAnimPhase } from "./common-anim-phase"; diff --git a/src/system/game-data.ts b/src/system/game-data.ts index e87c735f459..53146301666 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -30,7 +30,7 @@ import { Nature } from "#enums/nature"; import { GameStats } from "#app/system/game-stats"; import { Tutorial } from "#app/tutorial"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { TrainerVariant } from "#app/field/trainer"; import type { Variant } from "#app/sprites/variant"; import { setSettingGamepad, SettingGamepad, settingGamepadDefaults } from "#app/system/settings/settings-gamepad"; diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 7579fc3b78d..97ce494a43a 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -5,8 +5,7 @@ import type { Nature } from "#enums/nature"; import type { PokeballType } from "#enums/pokeball"; import { getPokemonSpecies, getPokemonSpeciesForm } from "../data/pokemon-species"; import { Status } from "../data/status-effect"; -import Pokemon, { EnemyPokemon, PokemonSummonData } from "../field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import Pokemon, { EnemyPokemon, PokemonMove, PokemonSummonData } from "../field/pokemon"; import { TrainerSlot } from "#enums/trainer-slot"; import type { Variant } from "#app/sprites/variant"; import { loadBattlerTag } from "../data/battler-tags"; diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index 63c0703fa18..27985629e3d 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -10,7 +10,7 @@ import { getLocalizedSpriteKey, fixedInt, padInt } from "#app/utils"; import { MoveCategory } from "#enums/MoveCategory"; import i18next from "i18next"; import { Button } from "#enums/buttons"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; +import type { PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import type { CommandPhase } from "#app/phases/command-phase"; import MoveInfoOverlay from "./move-info-overlay"; diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index f0ff351bb8a..26351d4dbf1 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -9,7 +9,7 @@ import { LockModifierTiersModifier, PokemonHeldItemModifier, HealShopCostModifie import { handleTutorial, Tutorial } from "../tutorial"; import { Button } from "#enums/buttons"; import MoveInfoOverlay from "./move-info-overlay"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "../data/moves/move"; import { formatMoney, NumberHolder } from "#app/utils"; import Overrides from "#app/overrides"; import i18next from "i18next"; diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index a42e0caadae..ba90108c274 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1,5 +1,4 @@ -import type { PlayerPokemon } from "#app/field/pokemon"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; +import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "#app/ui/text"; @@ -12,8 +11,7 @@ import { PokemonHeldItemModifier, SwitchEffectTransferModifier, } from "#app/modifier/modifier"; -import { ForceSwitchOutAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, ForceSwitchOutAttr } from "#app/data/moves/move"; import { Gender, getGenderColor, getGenderSymbol } from "#app/data/gender"; import { StatusEffect } from "#enums/status-effect"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "#app/ui/pokemon-icon-anim-handler"; diff --git a/src/ui/pokedex-page-ui-handler.ts b/src/ui/pokedex-page-ui-handler.ts index 1011fc89ae0..407ebfcd843 100644 --- a/src/ui/pokedex-page-ui-handler.ts +++ b/src/ui/pokedex-page-ui-handler.ts @@ -9,7 +9,7 @@ import { allAbilities } from "#app/data/ability"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { GrowthRate, getGrowthRateColor } from "#app/data/exp"; import { Gender, getGenderColor, getGenderSymbol } from "#app/data/gender"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { getNatureName } from "#app/data/nature"; import type { SpeciesFormChange } from "#app/data/pokemon-forms"; import { pokemonFormChanges } from "#app/data/pokemon-forms"; diff --git a/src/ui/pokedex-scan-ui-handler.ts b/src/ui/pokedex-scan-ui-handler.ts index 54c32fb34a1..b34246b97d1 100644 --- a/src/ui/pokedex-scan-ui-handler.ts +++ b/src/ui/pokedex-scan-ui-handler.ts @@ -7,7 +7,7 @@ import { isNullOrUndefined } from "#app/utils"; import { Mode } from "./ui"; import { FilterTextRow } from "./filter-text"; import { allAbilities } from "#app/data/ability"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { allSpecies } from "#app/data/pokemon-species"; import i18next from "i18next"; diff --git a/src/ui/pokedex-ui-handler.ts b/src/ui/pokedex-ui-handler.ts index 22ce5b833af..59b06d476a2 100644 --- a/src/ui/pokedex-ui-handler.ts +++ b/src/ui/pokedex-ui-handler.ts @@ -38,7 +38,7 @@ import type { OptionSelectConfig } from "./abstact-option-select-ui-handler"; import { FilterText, FilterTextRow } from "./filter-text"; import { allAbilities } from "#app/data/ability"; import { starterPassiveAbilities } from "#app/data/balance/passives"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { speciesTmMoves } from "#app/data/balance/tms"; import { pokemonPrevolutions, pokemonStarters } from "#app/data/balance/pokemon-evolutions"; import { Biome } from "#enums/biome"; diff --git a/src/ui/pokemon-hatch-info-container.ts b/src/ui/pokemon-hatch-info-container.ts index 77f9f5090a0..692f0f1d374 100644 --- a/src/ui/pokemon-hatch-info-container.ts +++ b/src/ui/pokemon-hatch-info-container.ts @@ -4,7 +4,7 @@ import { PokemonType } from "#enums/pokemon-type"; import { rgbHexToRgba, padInt } from "#app/utils"; import { TextStyle, addTextObject } from "#app/ui/text"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Species } from "#enums/species"; import { getEggTierForSpecies } from "#app/data/egg"; import { starterColors } from "#app/battle-scene"; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 680f752096b..3e2940f45b9 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -13,7 +13,7 @@ import { allAbilities } from "#app/data/ability"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { GrowthRate, getGrowthRateColor } from "#app/data/exp"; import { Gender, getGenderColor, getGenderSymbol } from "#app/data/gender"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { getNatureName } from "#app/data/nature"; import { pokemonFormChanges } from "#app/data/pokemon-forms"; import type { LevelMoves } from "#app/data/balance/pokemon-level-moves"; diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index d82082f0872..04bcf71d7ae 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -12,8 +12,7 @@ import { toReadableString, formatStat, } from "#app/utils"; -import type { PlayerPokemon } from "#app/field/pokemon"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; +import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters"; import { argbFromRgba } from "@material/material-color-utilities"; import { getTypeRgb } from "#app/data/type"; diff --git a/test/abilities/aura_break.test.ts b/test/abilities/aura_break.test.ts index 30841fdbe0c..86b6c69ec8b 100644 --- a/test/abilities/aura_break.test.ts +++ b/test/abilities/aura_break.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/abilities/battery.test.ts b/test/abilities/battery.test.ts index 78db19e67ff..cc7570c3d31 100644 --- a/test/abilities/battery.test.ts +++ b/test/abilities/battery.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; diff --git a/test/abilities/battle_bond.test.ts b/test/abilities/battle_bond.test.ts index e615b5746c0..6305d7dedc5 100644 --- a/test/abilities/battle_bond.test.ts +++ b/test/abilities/battle_bond.test.ts @@ -1,5 +1,4 @@ -import { MultiHitAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, MultiHitAttr } from "#app/data/moves/move"; import { MultiHitType } from "#enums/MultiHitType"; import { Status } from "#app/data/status-effect"; import { Abilities } from "#enums/abilities"; diff --git a/test/abilities/flower_veil.test.ts b/test/abilities/flower_veil.test.ts index d91c92e8c9f..c26a952acff 100644 --- a/test/abilities/flower_veil.test.ts +++ b/test/abilities/flower_veil.test.ts @@ -7,7 +7,7 @@ import { StatusEffect } from "#enums/status-effect"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { BattlerTagType } from "#enums/battler-tag-type"; import { allAbilities } from "#app/data/ability"; diff --git a/test/abilities/friend_guard.test.ts b/test/abilities/friend_guard.test.ts index cee82ca2c69..30175fe37e0 100644 --- a/test/abilities/friend_guard.test.ts +++ b/test/abilities/friend_guard.test.ts @@ -6,7 +6,7 @@ import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { BattlerIndex } from "#app/battle"; import { allAbilities } from "#app/data/ability"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { MoveCategory } from "#enums/MoveCategory"; describe("Moves - Friend Guard", () => { diff --git a/test/abilities/galvanize.test.ts b/test/abilities/galvanize.test.ts index 4efb6bb068f..c1e02c6c8d8 100644 --- a/test/abilities/galvanize.test.ts +++ b/test/abilities/galvanize.test.ts @@ -1,10 +1,10 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { PokemonType } from "#enums/pokemon-type"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; -import { HitResult } from "#enums/hit-result"; +import { HitResult } from "#app/field/pokemon"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/hustle.test.ts b/test/abilities/hustle.test.ts index fbfa23e90d6..40197cf9e97 100644 --- a/test/abilities/hustle.test.ts +++ b/test/abilities/hustle.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { Stat } from "#app/enums/stat"; import { Moves } from "#enums/moves"; diff --git a/test/abilities/infiltrator.test.ts b/test/abilities/infiltrator.test.ts index e9ecf366a37..6278439651c 100644 --- a/test/abilities/infiltrator.test.ts +++ b/test/abilities/infiltrator.test.ts @@ -1,5 +1,5 @@ import { ArenaTagSide } from "#app/data/arena-tag"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Stat } from "#enums/stat"; diff --git a/test/abilities/libero.test.ts b/test/abilities/libero.test.ts index 96a6b3c5d93..22abf1c248f 100644 --- a/test/abilities/libero.test.ts +++ b/test/abilities/libero.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { PokemonType } from "#enums/pokemon-type"; import { Weather } from "#app/data/weather"; import type { PlayerPokemon } from "#app/field/pokemon"; diff --git a/test/abilities/magic_bounce.test.ts b/test/abilities/magic_bounce.test.ts index c785827c910..f9a076776aa 100644 --- a/test/abilities/magic_bounce.test.ts +++ b/test/abilities/magic_bounce.test.ts @@ -1,7 +1,7 @@ import { BattlerIndex } from "#app/battle"; import { allAbilities } from "#app/data/ability"; import { ArenaTagSide } from "#app/data/arena-tag"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { Stat } from "#app/enums/stat"; diff --git a/test/abilities/power_spot.test.ts b/test/abilities/power_spot.test.ts index 68ace696d4a..e29b5ecf775 100644 --- a/test/abilities/power_spot.test.ts +++ b/test/abilities/power_spot.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; diff --git a/test/abilities/protean.test.ts b/test/abilities/protean.test.ts index ca5e67139e1..574033bb13f 100644 --- a/test/abilities/protean.test.ts +++ b/test/abilities/protean.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { PokemonType } from "#enums/pokemon-type"; import { Weather } from "#app/data/weather"; import type { PlayerPokemon } from "#app/field/pokemon"; diff --git a/test/abilities/sap_sipper.test.ts b/test/abilities/sap_sipper.test.ts index b27f97099b9..f4f02844cbc 100644 --- a/test/abilities/sap_sipper.test.ts +++ b/test/abilities/sap_sipper.test.ts @@ -9,8 +9,7 @@ import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { RandomMoveAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, RandomMoveAttr } from "#app/data/moves/move"; // See also: TypeImmunityAbAttr describe("Abilities - Sap Sipper", () => { diff --git a/test/abilities/serene_grace.test.ts b/test/abilities/serene_grace.test.ts index 30073f30b24..65ca96acbbc 100644 --- a/test/abilities/serene_grace.test.ts +++ b/test/abilities/serene_grace.test.ts @@ -4,7 +4,7 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { FlinchAttr } from "#app/data/moves/move"; diff --git a/test/abilities/sheer_force.test.ts b/test/abilities/sheer_force.test.ts index 74c7b30a846..4a1c20cde5c 100644 --- a/test/abilities/sheer_force.test.ts +++ b/test/abilities/sheer_force.test.ts @@ -7,8 +7,7 @@ import { Stat } from "#enums/stat"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { FlinchAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, FlinchAttr } from "#app/data/moves/move"; describe("Abilities - Sheer Force", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/steely_spirit.test.ts b/test/abilities/steely_spirit.test.ts index 6e8331ea51a..b180ff8919e 100644 --- a/test/abilities/steely_spirit.test.ts +++ b/test/abilities/steely_spirit.test.ts @@ -1,5 +1,5 @@ import { allAbilities } from "#app/data/ability"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/abilities/supreme_overlord.test.ts b/test/abilities/supreme_overlord.test.ts index 69ff4f393b6..a71bf0a9354 100644 --- a/test/abilities/supreme_overlord.test.ts +++ b/test/abilities/supreme_overlord.test.ts @@ -7,7 +7,7 @@ import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; describe("Abilities - Supreme Overlord", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/tera_shell.test.ts b/test/abilities/tera_shell.test.ts index bd88c21f52d..a99ecfd4ce1 100644 --- a/test/abilities/tera_shell.test.ts +++ b/test/abilities/tera_shell.test.ts @@ -2,7 +2,7 @@ import { BattlerIndex } from "#app/battle"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; -import { HitResult } from "#enums/hit-result"; +import { HitResult } from "#app/field/pokemon"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/abilities/unburden.test.ts b/test/abilities/unburden.test.ts index 7012c4cf065..8f18604011c 100644 --- a/test/abilities/unburden.test.ts +++ b/test/abilities/unburden.test.ts @@ -1,7 +1,6 @@ import { BattlerIndex } from "#app/battle"; import { PostItemLostAbAttr } from "#app/data/ability"; -import { StealHeldItemChanceAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, StealHeldItemChanceAttr } from "#app/data/moves/move"; import type Pokemon from "#app/field/pokemon"; import type { ContactHeldItemTransferChanceModifier } from "#app/modifier/modifier"; import { Abilities } from "#enums/abilities"; diff --git a/test/abilities/wimp_out.test.ts b/test/abilities/wimp_out.test.ts index c81fa2071c5..294025a10e7 100644 --- a/test/abilities/wimp_out.test.ts +++ b/test/abilities/wimp_out.test.ts @@ -1,6 +1,6 @@ import { BattlerIndex } from "#app/battle"; import { ArenaTagSide } from "#app/data/arena-tag"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import GameManager from "#test/testUtils/gameManager"; import { toDmgValue } from "#app/utils"; import { Abilities } from "#enums/abilities"; @@ -534,12 +534,12 @@ describe("Abilities - Wimp Out", () => { .enemyAbility(Abilities.WIMP_OUT) .startingLevel(50) .enemyLevel(1) - .enemyMoveset([Moves.SPLASH, Moves.ENDURE]) + .enemyMoveset([ Moves.SPLASH, Moves.ENDURE ]) .battleType("double") - .moveset([Moves.DRAGON_ENERGY, Moves.SPLASH]) + .moveset([ Moves.DRAGON_ENERGY, Moves.SPLASH ]) .startingWave(wave); - await game.classicMode.startBattle([Species.REGIDRAGO, Species.MAGIKARP]); + await game.classicMode.startBattle([ Species.REGIDRAGO, Species.MAGIKARP ]); // turn 1 game.move.select(Moves.DRAGON_ENERGY, 0); @@ -549,5 +549,6 @@ describe("Abilities - Wimp Out", () => { await game.phaseInterceptor.to("SelectModifierPhase"); expect(game.scene.currentBattle.waveIndex).toBe(wave + 1); + }); }); diff --git a/test/abilities/wonder_skin.test.ts b/test/abilities/wonder_skin.test.ts index fe24cdad5ec..18d5be36aef 100644 --- a/test/abilities/wonder_skin.test.ts +++ b/test/abilities/wonder_skin.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; diff --git a/test/arena/arena_gravity.test.ts b/test/arena/arena_gravity.test.ts index 7e72d14460a..a5ce84667f0 100644 --- a/test/arena/arena_gravity.test.ts +++ b/test/arena/arena_gravity.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; diff --git a/test/arena/grassy_terrain.test.ts b/test/arena/grassy_terrain.test.ts index 9ee9d2ef434..d92fb24be5a 100644 --- a/test/arena/grassy_terrain.test.ts +++ b/test/arena/grassy_terrain.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/arena/weather_fog.test.ts b/test/arena/weather_fog.test.ts index b240bfa7386..784c4886648 100644 --- a/test/arena/weather_fog.test.ts +++ b/test/arena/weather_fog.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; diff --git a/test/arena/weather_strong_winds.test.ts b/test/arena/weather_strong_winds.test.ts index 50d25947612..3a9235d9eb9 100644 --- a/test/arena/weather_strong_winds.test.ts +++ b/test/arena/weather_strong_winds.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { StatusEffect } from "#app/enums/status-effect"; import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { Abilities } from "#enums/abilities"; diff --git a/test/battle/damage_calculation.test.ts b/test/battle/damage_calculation.test.ts index 11bb8246ca1..dab1fc81caa 100644 --- a/test/battle/damage_calculation.test.ts +++ b/test/battle/damage_calculation.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import type { EnemyPersistentModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import { Abilities } from "#enums/abilities"; diff --git a/test/battlerTags/substitute.test.ts b/test/battlerTags/substitute.test.ts index f2ee741bca2..fca3dc5ef7e 100644 --- a/test/battlerTags/substitute.test.ts +++ b/test/battlerTags/substitute.test.ts @@ -1,7 +1,5 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import type { PokemonTurnData } from "#app/field/pokemon"; -import type { PokemonMove } from "#app/data/moves/pokemon-move"; -import type { TurnMove } from "#app/interfaces/turn-move"; +import type { PokemonTurnData, TurnMove, PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import type BattleScene from "#app/battle-scene"; @@ -9,7 +7,7 @@ import { BattlerTagLapseType, BindTag, SubstituteTag } from "#app/data/battler-t import { Moves } from "#app/enums/moves"; import { PokemonAnimType } from "#app/enums/pokemon-anim-type"; import * as messages from "#app/messages"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import type { MoveEffectPhase } from "#app/phases/move-effect-phase"; import GameManager from "#test/testUtils/gameManager"; diff --git a/test/enemy_command.test.ts b/test/enemy_command.test.ts index cfa141cf89e..6d5cc2698a3 100644 --- a/test/enemy_command.test.ts +++ b/test/enemy_command.test.ts @@ -1,11 +1,11 @@ import type BattleScene from "#app/battle-scene"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { MoveCategory } from "#enums/MoveCategory"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import type { EnemyPokemon } from "#app/field/pokemon"; -import { AiType } from "#enums/ai-type"; +import { AiType } from "#app/field/pokemon"; import { randSeedInt } from "#app/utils"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; diff --git a/test/evolution.test.ts b/test/evolution.test.ts index 62a06f868e8..dd6795bf161 100644 --- a/test/evolution.test.ts +++ b/test/evolution.test.ts @@ -1,5 +1,8 @@ -import { pokemonEvolutions, SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; -import { SpeciesWildEvolutionDelay } from "#enums/species-wild-evolution-delay"; +import { + pokemonEvolutions, + SpeciesFormEvolution, + SpeciesWildEvolutionDelay, +} from "#app/data/balance/pokemon-evolutions"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; diff --git a/test/imports.test.ts b/test/imports.test.ts index ada7eff0109..128308dbd14 100644 --- a/test/imports.test.ts +++ b/test/imports.test.ts @@ -4,7 +4,7 @@ import { describe, expect, it } from "vitest"; async function importModule() { try { initStatsKeys(); - const { PokemonMove } = await import("#app/data/moves/pokemon-move"); + const { PokemonMove } = await import("#app/field/pokemon"); const { Species } = await import("#enums/species"); return { PokemonMove, diff --git a/test/items/reviver_seed.test.ts b/test/items/reviver_seed.test.ts index e1e7e0d554e..c06f354a94a 100644 --- a/test/items/reviver_seed.test.ts +++ b/test/items/reviver_seed.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import type { PokemonInstantReviveModifier } from "#app/modifier/modifier"; import { Abilities } from "#enums/abilities"; diff --git a/test/moves/astonish.test.ts b/test/moves/astonish.test.ts index 69a312d4517..53922060ae6 100644 --- a/test/moves/astonish.test.ts +++ b/test/moves/astonish.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { BerryPhase } from "#app/phases/berry-phase"; import { CommandPhase } from "#app/phases/command-phase"; diff --git a/test/moves/aurora_veil.test.ts b/test/moves/aurora_veil.test.ts index 06637d0764e..31f6497bae5 100644 --- a/test/moves/aurora_veil.test.ts +++ b/test/moves/aurora_veil.test.ts @@ -1,8 +1,7 @@ import type BattleScene from "#app/battle-scene"; import { ArenaTagSide } from "#app/data/arena-tag"; import type Move from "#app/data/moves/move"; -import { CritOnlyAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, CritOnlyAttr } from "#app/data/moves/move"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import type Pokemon from "#app/field/pokemon"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; diff --git a/test/moves/burning_jealousy.test.ts b/test/moves/burning_jealousy.test.ts index c618b46e842..60387df4226 100644 --- a/test/moves/burning_jealousy.test.ts +++ b/test/moves/burning_jealousy.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { StatusEffect } from "#app/enums/status-effect"; import { Moves } from "#enums/moves"; diff --git a/test/moves/ceaseless_edge.test.ts b/test/moves/ceaseless_edge.test.ts index 227645df360..d54f1bd9f21 100644 --- a/test/moves/ceaseless_edge.test.ts +++ b/test/moves/ceaseless_edge.test.ts @@ -1,5 +1,5 @@ import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; diff --git a/test/moves/copycat.test.ts b/test/moves/copycat.test.ts index 615206275d4..0d9b0951f89 100644 --- a/test/moves/copycat.test.ts +++ b/test/moves/copycat.test.ts @@ -1,6 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { RandomMoveAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, RandomMoveAttr } from "#app/data/moves/move"; import { Stat } from "#app/enums/stat"; import { MoveResult } from "#app/field/pokemon"; import { Abilities } from "#enums/abilities"; diff --git a/test/moves/destiny_bond.test.ts b/test/moves/destiny_bond.test.ts index 9873d678b8c..c39d40427ad 100644 --- a/test/moves/destiny_bond.test.ts +++ b/test/moves/destiny_bond.test.ts @@ -1,6 +1,6 @@ import type { ArenaTrapTag } from "#app/data/arena-tag"; import { ArenaTagSide } from "#app/data/arena-tag"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; import { Moves } from "#enums/moves"; diff --git a/test/moves/diamond_storm.test.ts b/test/moves/diamond_storm.test.ts index 73a1aee3fd2..2363122f0d7 100644 --- a/test/moves/diamond_storm.test.ts +++ b/test/moves/diamond_storm.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/moves/dig.test.ts b/test/moves/dig.test.ts index 14e7efee19b..81339111656 100644 --- a/test/moves/dig.test.ts +++ b/test/moves/dig.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; diff --git a/test/moves/dragon_tail.test.ts b/test/moves/dragon_tail.test.ts index a571312473d..37e8aa2fe1b 100644 --- a/test/moves/dragon_tail.test.ts +++ b/test/moves/dragon_tail.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Status } from "#app/data/status-effect"; import { Challenges } from "#enums/challenges"; import { StatusEffect } from "#enums/status-effect"; diff --git a/test/moves/dynamax_cannon.test.ts b/test/moves/dynamax_cannon.test.ts index b2590449e4e..9cf3106b9c1 100644 --- a/test/moves/dynamax_cannon.test.ts +++ b/test/moves/dynamax_cannon.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Moves } from "#enums/moves"; diff --git a/test/moves/effectiveness.test.ts b/test/moves/effectiveness.test.ts index efcbc9c3293..fb03f1c10a0 100644 --- a/test/moves/effectiveness.test.ts +++ b/test/moves/effectiveness.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { TrainerSlot } from "#enums/trainer-slot"; import { PokemonType } from "#enums/pokemon-type"; diff --git a/test/moves/fell_stinger.test.ts b/test/moves/fell_stinger.test.ts index 766fedf68dc..2ffa44c5a3a 100644 --- a/test/moves/fell_stinger.test.ts +++ b/test/moves/fell_stinger.test.ts @@ -7,7 +7,7 @@ import { Moves } from "#enums/moves"; import { Stat } from "#enums/stat"; import { StatusEffect } from "#app/enums/status-effect"; import { WeatherType } from "#app/enums/weather-type"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; describe("Moves - Fell Stinger", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/fly.test.ts b/test/moves/fly.test.ts index 37fa42b608d..0bd7d22b2a7 100644 --- a/test/moves/fly.test.ts +++ b/test/moves/fly.test.ts @@ -8,7 +8,7 @@ import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; describe("Moves - Fly", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/freezy_frost.test.ts b/test/moves/freezy_frost.test.ts index d764600bc78..c1ac4054e70 100644 --- a/test/moves/freezy_frost.test.ts +++ b/test/moves/freezy_frost.test.ts @@ -5,7 +5,7 @@ import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { CommandPhase } from "#app/phases/command-phase"; describe("Moves - Freezy Frost", () => { diff --git a/test/moves/fusion_flare_bolt.test.ts b/test/moves/fusion_flare_bolt.test.ts index 32df10b4c7c..c340aeea63f 100644 --- a/test/moves/fusion_flare_bolt.test.ts +++ b/test/moves/fusion_flare_bolt.test.ts @@ -1,6 +1,6 @@ import { Stat } from "#enums/stat"; import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import type Move from "#app/data/moves/move"; import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; diff --git a/test/moves/glaive_rush.test.ts b/test/moves/glaive_rush.test.ts index 28d6328c095..d3531b172e2 100644 --- a/test/moves/glaive_rush.test.ts +++ b/test/moves/glaive_rush.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; diff --git a/test/moves/hard_press.test.ts b/test/moves/hard_press.test.ts index 425993fb1a9..8891f0bf0e2 100644 --- a/test/moves/hard_press.test.ts +++ b/test/moves/hard_press.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; diff --git a/test/moves/hyper_beam.test.ts b/test/moves/hyper_beam.test.ts index b1a244f2ea4..5cd54e9b46a 100644 --- a/test/moves/hyper_beam.test.ts +++ b/test/moves/hyper_beam.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { Moves } from "#app/enums/moves"; diff --git a/test/moves/lash_out.test.ts b/test/moves/lash_out.test.ts index 16632ec0065..8395633f5c0 100644 --- a/test/moves/lash_out.test.ts +++ b/test/moves/lash_out.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/moves/last_respects.test.ts b/test/moves/last_respects.test.ts index 891b287dece..ccab8a43415 100644 --- a/test/moves/last_respects.test.ts +++ b/test/moves/last_respects.test.ts @@ -3,7 +3,7 @@ import { BattlerIndex } from "#app/battle"; import { Species } from "#enums/species"; import { Abilities } from "#enums/abilities"; import GameManager from "#test/testUtils/gameManager"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import type Move from "#app/data/moves/move"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import Phaser from "phaser"; diff --git a/test/moves/light_screen.test.ts b/test/moves/light_screen.test.ts index b77bb1c790b..9cc6944ed3e 100644 --- a/test/moves/light_screen.test.ts +++ b/test/moves/light_screen.test.ts @@ -1,8 +1,7 @@ import type BattleScene from "#app/battle-scene"; import { ArenaTagSide } from "#app/data/arena-tag"; import type Move from "#app/data/moves/move"; -import { CritOnlyAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, CritOnlyAttr } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import type Pokemon from "#app/field/pokemon"; diff --git a/test/moves/magic_coat.test.ts b/test/moves/magic_coat.test.ts index e96125a23ac..2cc8dea8938 100644 --- a/test/moves/magic_coat.test.ts +++ b/test/moves/magic_coat.test.ts @@ -1,6 +1,6 @@ import { BattlerIndex } from "#app/battle"; import { ArenaTagSide } from "#app/data/arena-tag"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { Stat } from "#app/enums/stat"; diff --git a/test/moves/metronome.test.ts b/test/moves/metronome.test.ts index bf045f5e9f9..80f32a3a6fb 100644 --- a/test/moves/metronome.test.ts +++ b/test/moves/metronome.test.ts @@ -1,6 +1,5 @@ import { RechargingTag, SemiInvulnerableTag } from "#app/data/battler-tags"; -import { RandomMoveAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, RandomMoveAttr } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { Stat } from "#app/enums/stat"; import { CommandPhase } from "#app/phases/command-phase"; diff --git a/test/moves/moongeist_beam.test.ts b/test/moves/moongeist_beam.test.ts index 94197683ea4..117fe513e17 100644 --- a/test/moves/moongeist_beam.test.ts +++ b/test/moves/moongeist_beam.test.ts @@ -1,5 +1,4 @@ -import { RandomMoveAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, RandomMoveAttr } from "#app/data/moves/move"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/moves/pledge_moves.test.ts b/test/moves/pledge_moves.test.ts index d3b8e60ac62..c866d15357c 100644 --- a/test/moves/pledge_moves.test.ts +++ b/test/moves/pledge_moves.test.ts @@ -1,8 +1,7 @@ import { BattlerIndex } from "#app/battle"; import { allAbilities } from "#app/data/ability"; import { ArenaTagSide } from "#app/data/arena-tag"; -import { FlinchAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, FlinchAttr } from "#app/data/moves/move"; import { PokemonType } from "#enums/pokemon-type"; import { ArenaTagType } from "#enums/arena-tag-type"; import { Stat } from "#enums/stat"; diff --git a/test/moves/powder.test.ts b/test/moves/powder.test.ts index 510564e0f53..522b0b74ca7 100644 --- a/test/moves/powder.test.ts +++ b/test/moves/powder.test.ts @@ -5,8 +5,7 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { BerryPhase } from "#app/phases/berry-phase"; -import { MoveResult } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { MoveResult, PokemonMove } from "#app/field/pokemon"; import { PokemonType } from "#enums/pokemon-type"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { StatusEffect } from "#enums/status-effect"; diff --git a/test/moves/protect.test.ts b/test/moves/protect.test.ts index 65de079982f..d50c490f7d3 100644 --- a/test/moves/protect.test.ts +++ b/test/moves/protect.test.ts @@ -5,7 +5,7 @@ import { Species } from "#enums/species"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Stat } from "#enums/stat"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; import { BattlerIndex } from "#app/battle"; import { MoveResult } from "#app/field/pokemon"; diff --git a/test/moves/rage_fist.test.ts b/test/moves/rage_fist.test.ts index 73d83f4929c..f44901c5aba 100644 --- a/test/moves/rage_fist.test.ts +++ b/test/moves/rage_fist.test.ts @@ -2,7 +2,7 @@ import { BattlerIndex } from "#app/battle"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import type Move from "#app/data/moves/move"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; diff --git a/test/moves/reflect.test.ts b/test/moves/reflect.test.ts index 272e5c2972c..ac879a7cc2b 100644 --- a/test/moves/reflect.test.ts +++ b/test/moves/reflect.test.ts @@ -1,8 +1,7 @@ import type BattleScene from "#app/battle-scene"; import { ArenaTagSide } from "#app/data/arena-tag"; import type Move from "#app/data/moves/move"; -import { CritOnlyAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, CritOnlyAttr } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import type Pokemon from "#app/field/pokemon"; diff --git a/test/moves/retaliate.test.ts b/test/moves/retaliate.test.ts index 57d29b4fdfc..e916c9ffeaa 100644 --- a/test/moves/retaliate.test.ts +++ b/test/moves/retaliate.test.ts @@ -3,7 +3,7 @@ import Phaser from "phaser"; import GameManager from "#test/testUtils/gameManager"; import { Species } from "#enums/species"; import { Moves } from "#enums/moves"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import type Move from "#app/data/moves/move"; describe("Moves - Retaliate", () => { diff --git a/test/moves/rollout.test.ts b/test/moves/rollout.test.ts index 456f029cda1..89270c2dfc7 100644 --- a/test/moves/rollout.test.ts +++ b/test/moves/rollout.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { CommandPhase } from "#app/phases/command-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; diff --git a/test/moves/round.test.ts b/test/moves/round.test.ts index ec9f3f69a5e..82f080a25ea 100644 --- a/test/moves/round.test.ts +++ b/test/moves/round.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import type { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; diff --git a/test/moves/scale_shot.test.ts b/test/moves/scale_shot.test.ts index ee759b8404a..2be632adb54 100644 --- a/test/moves/scale_shot.test.ts +++ b/test/moves/scale_shot.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; diff --git a/test/moves/secret_power.test.ts b/test/moves/secret_power.test.ts index 40802dcc51f..37f1664251b 100644 --- a/test/moves/secret_power.test.ts +++ b/test/moves/secret_power.test.ts @@ -2,7 +2,7 @@ import { Abilities } from "#enums/abilities"; import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Stat } from "#enums/stat"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; diff --git a/test/moves/shell_side_arm.test.ts b/test/moves/shell_side_arm.test.ts index 232182ffef0..a5b065b76cb 100644 --- a/test/moves/shell_side_arm.test.ts +++ b/test/moves/shell_side_arm.test.ts @@ -1,6 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { ShellSideArmCategoryAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, ShellSideArmCategoryAttr } from "#app/data/moves/move"; import type Move from "#app/data/moves/move"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; diff --git a/test/moves/shell_trap.test.ts b/test/moves/shell_trap.test.ts index d3ba67843ac..2df94cdb828 100644 --- a/test/moves/shell_trap.test.ts +++ b/test/moves/shell_trap.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import { MoveResult } from "#app/field/pokemon"; diff --git a/test/moves/sketch.test.ts b/test/moves/sketch.test.ts index 94f37757a6a..dfbf2eca713 100644 --- a/test/moves/sketch.test.ts +++ b/test/moves/sketch.test.ts @@ -1,15 +1,13 @@ import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { MoveResult } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { MoveResult, PokemonMove } from "#app/field/pokemon"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { StatusEffect } from "#app/enums/status-effect"; import { BattlerIndex } from "#app/battle"; -import { RandomMoveAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, RandomMoveAttr } from "#app/data/moves/move"; describe("Moves - Sketch", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/solar_beam.test.ts b/test/moves/solar_beam.test.ts index b8a28065b64..dffd4f210e5 100644 --- a/test/moves/solar_beam.test.ts +++ b/test/moves/solar_beam.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { BattlerTagType } from "#enums/battler-tag-type"; import { WeatherType } from "#enums/weather-type"; import { MoveResult } from "#app/field/pokemon"; diff --git a/test/moves/sparkly_swirl.test.ts b/test/moves/sparkly_swirl.test.ts index 1908772598a..6cd357c7e0e 100644 --- a/test/moves/sparkly_swirl.test.ts +++ b/test/moves/sparkly_swirl.test.ts @@ -1,4 +1,4 @@ -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { StatusEffect } from "#app/enums/status-effect"; import { CommandPhase } from "#app/phases/command-phase"; import { Abilities } from "#enums/abilities"; diff --git a/test/moves/spectral_thief.test.ts b/test/moves/spectral_thief.test.ts index 271cb03073a..2e52b118a74 100644 --- a/test/moves/spectral_thief.test.ts +++ b/test/moves/spectral_thief.test.ts @@ -1,7 +1,7 @@ import { Abilities } from "#enums/abilities"; import { BattlerIndex } from "#app/battle"; import { Stat } from "#enums/stat"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; diff --git a/test/moves/spit_up.test.ts b/test/moves/spit_up.test.ts index 7ef6e5e5b14..d71647bda52 100644 --- a/test/moves/spit_up.test.ts +++ b/test/moves/spit_up.test.ts @@ -1,8 +1,8 @@ import { Stat } from "#enums/stat"; import { StockpilingTag } from "#app/data/battler-tags"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { BattlerTagType } from "#app/enums/battler-tag-type"; -import type { TurnMove } from "#app/interfaces/turn-move"; +import type { TurnMove } from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import GameManager from "#test/testUtils/gameManager"; import { Abilities } from "#enums/abilities"; diff --git a/test/moves/steamroller.test.ts b/test/moves/steamroller.test.ts index a0e4c29cce5..ba96928e01d 100644 --- a/test/moves/steamroller.test.ts +++ b/test/moves/steamroller.test.ts @@ -1,7 +1,7 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { BattlerTagType } from "#app/enums/battler-tag-type"; -import type { DamageCalculationResult } from "#app/interfaces/damage-calculation-result"; +import type { DamageCalculationResult } from "#app/field/pokemon"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/moves/stockpile.test.ts b/test/moves/stockpile.test.ts index f6e6a0087f6..033f24d5229 100644 --- a/test/moves/stockpile.test.ts +++ b/test/moves/stockpile.test.ts @@ -1,6 +1,6 @@ import { Stat } from "#enums/stat"; import { StockpilingTag } from "#app/data/battler-tags"; -import type { TurnMove } from "#app/interfaces/turn-move"; +import type { TurnMove } from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import { CommandPhase } from "#app/phases/command-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase"; diff --git a/test/moves/substitute.test.ts b/test/moves/substitute.test.ts index 68b90bf7cf8..23f7f4af4b9 100644 --- a/test/moves/substitute.test.ts +++ b/test/moves/substitute.test.ts @@ -1,8 +1,7 @@ import { BattlerIndex } from "#app/battle"; import { ArenaTagSide } from "#app/data/arena-tag"; import { SubstituteTag, TrappedTag } from "#app/data/battler-tags"; -import { StealHeldItemChanceAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, StealHeldItemChanceAttr } from "#app/data/moves/move"; import { MoveResult } from "#app/field/pokemon"; import type { CommandPhase } from "#app/phases/command-phase"; import GameManager from "#test/testUtils/gameManager"; diff --git a/test/moves/swallow.test.ts b/test/moves/swallow.test.ts index 86af584a174..baa03801079 100644 --- a/test/moves/swallow.test.ts +++ b/test/moves/swallow.test.ts @@ -1,7 +1,7 @@ import { Stat } from "#enums/stat"; import { StockpilingTag } from "#app/data/battler-tags"; import { BattlerTagType } from "#app/enums/battler-tag-type"; -import type { TurnMove } from "#app/interfaces/turn-move"; +import type { TurnMove } from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import { MovePhase } from "#app/phases/move-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase"; diff --git a/test/moves/telekinesis.test.ts b/test/moves/telekinesis.test.ts index 7537ba0168a..1355cb975f3 100644 --- a/test/moves/telekinesis.test.ts +++ b/test/moves/telekinesis.test.ts @@ -1,5 +1,5 @@ import { BattlerTagType } from "#enums/battler-tag-type"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/moves/tera_blast.test.ts b/test/moves/tera_blast.test.ts index 9d17ea6a3cc..c1a2b999fa0 100644 --- a/test/moves/tera_blast.test.ts +++ b/test/moves/tera_blast.test.ts @@ -1,11 +1,10 @@ import { BattlerIndex } from "#app/battle"; import { Stat } from "#enums/stat"; -import { TeraMoveCategoryAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, TeraMoveCategoryAttr } from "#app/data/moves/move"; import type Move from "#app/data/moves/move"; import { PokemonType } from "#enums/pokemon-type"; import { Abilities } from "#app/enums/abilities"; -import { HitResult } from "#enums/hit-result"; +import { HitResult } from "#app/field/pokemon"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; diff --git a/test/moves/toxic.test.ts b/test/moves/toxic.test.ts index ab536364f6a..f2b1f82fe02 100644 --- a/test/moves/toxic.test.ts +++ b/test/moves/toxic.test.ts @@ -5,7 +5,7 @@ import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { StatusEffect } from "#enums/status-effect"; import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves } from "#app/data/moves/move"; describe("Moves - Toxic", () => { let phaserGame: Phaser.Game; diff --git a/test/moves/triple_arrows.test.ts b/test/moves/triple_arrows.test.ts index d1d14f7d3e6..eb434b25815 100644 --- a/test/moves/triple_arrows.test.ts +++ b/test/moves/triple_arrows.test.ts @@ -1,5 +1,4 @@ -import { FlinchAttr, StatStageChangeAttr } from "#app/data/moves/move"; -import { allMoves } from "#app/data/moves/all-moves"; +import { allMoves, FlinchAttr, StatStageChangeAttr } from "#app/data/moves/move"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import type Move from "#app/data/moves/move"; 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 728129007e7..3c7bda8febd 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,8 +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 } from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PlayerPokemon, 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"; 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 c1e6a635f31..9befe77e688 100644 --- a/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts +++ b/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts @@ -11,7 +11,7 @@ import { } from "#test/mystery-encounter/encounter-test-utils"; import { Moves } from "#enums/moves"; import type BattleScene from "#app/battle-scene"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { Mode } from "#app/ui/ui"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; diff --git a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts index 7b3d87463bf..4bbe76e5c72 100644 --- a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts +++ b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts @@ -15,7 +15,7 @@ import { import { Moves } from "#enums/moves"; import type BattleScene from "#app/battle-scene"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { Mode } from "#app/ui/ui"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; diff --git a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts index 5ea836d8aa6..77cd65e51b9 100644 --- a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts +++ b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts @@ -17,7 +17,7 @@ import { Moves } from "#enums/moves"; import { DancingLessonsEncounter } from "#app/data/mystery-encounters/encounters/dancing-lessons-encounter"; import { Mode } from "#app/ui/ui"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { CommandPhase } from "#app/phases/command-phase"; import { MovePhase } from "#app/phases/move-phase"; 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 82d80bc3970..d233e72932a 100644 --- a/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts +++ b/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts @@ -11,7 +11,7 @@ import { } from "#test/mystery-encounter/encounter-test-utils"; import { Moves } from "#enums/moves"; import type BattleScene from "#app/battle-scene"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { Mode } from "#app/ui/ui"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; diff --git a/test/mystery-encounter/encounters/part-timer-encounter.test.ts b/test/mystery-encounter/encounters/part-timer-encounter.test.ts index 308aa9839e9..639a2e140ff 100644 --- a/test/mystery-encounter/encounters/part-timer-encounter.test.ts +++ b/test/mystery-encounter/encounters/part-timer-encounter.test.ts @@ -14,7 +14,7 @@ import { CIVILIZATION_ENCOUNTER_BIOMES } from "#app/data/mystery-encounters/myst import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { PartTimerEncounter } from "#app/data/mystery-encounters/encounters/part-timer-encounter"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { Moves } from "#enums/moves"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; 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 0d0298901d0..a9e6a339d36 100644 --- a/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts @@ -17,7 +17,7 @@ import { TheStrongStuffEncounter } from "#app/data/mystery-encounters/encounters import { Nature } from "#enums/nature"; import { BerryType } from "#enums/berry-type"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { Mode } from "#app/ui/ui"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { BerryModifier, PokemonBaseStatTotalModifier } from "#app/modifier/modifier"; 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 44c8e7a8915..df7bbb9f424 100644 --- a/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts +++ b/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts @@ -12,7 +12,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Biome } from "#app/enums/biome"; import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; import { Species } from "#app/enums/species"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { HealShopCostModifier, HitHealModifier, TurnHealModifier } from "#app/modifier/modifier"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { modifierTypes, type PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; diff --git a/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts b/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts index e4928406a18..452dfcf3784 100644 --- a/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts +++ b/test/mystery-encounter/encounters/uncommon-breed-encounter.test.ts @@ -10,7 +10,7 @@ import { } from "#test/mystery-encounter/encounter-test-utils"; import { Moves } from "#enums/moves"; import type BattleScene from "#app/battle-scene"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index 333f95f2014..543f46b2026 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -1,7 +1,7 @@ import type { BattlerIndex } from "#app/battle"; import { Button } from "#app/enums/buttons"; import type Pokemon from "#app/field/pokemon"; -import { PokemonMove } from "#app/data/moves/pokemon-move"; +import { PokemonMove } from "#app/field/pokemon"; import Overrides from "#app/overrides"; import type { CommandPhase } from "#app/phases/command-phase"; import { LearnMovePhase } from "#app/phases/learn-move-phase"; From 8216a379bf37efaed799261ac757df485af0914f Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Mon, 14 Apr 2025 12:37:26 -0500 Subject: [PATCH 20/52] [Dev][GitHub] Update to node 22 (#5586) * Update node and workflows to use version 22.14 * Update @types/node package * Update engines field in package.json * Hardcode node version in github pages workflow * Update to checkout@v4 in github pages workflow --- .github/workflows/deploy-beta.yml | 2 +- .github/workflows/deploy.yml | 2 +- .github/workflows/github-pages.yml | 10 +++++----- .github/workflows/quality.yml | 1 + .github/workflows/test-shard-template.yml | 5 +++-- .nvmrc | 2 +- README.md | 2 +- package-lock.json | 17 +++++++++-------- package.json | 4 ++-- 9 files changed, 24 insertions(+), 21 deletions(-) diff --git a/.github/workflows/deploy-beta.yml b/.github/workflows/deploy-beta.yml index d8d8126193d..8b0e33a18c4 100644 --- a/.github/workflows/deploy-beta.yml +++ b/.github/workflows/deploy-beta.yml @@ -15,7 +15,7 @@ jobs: submodules: 'recursive' - uses: actions/setup-node@v4 with: - node-version: "20" + node-version-file: '.nvmrc' - name: Install dependencies run: npm ci - name: Build diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e40b18eb69b..00190e477d5 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -18,7 +18,7 @@ jobs: submodules: 'recursive' - uses: actions/setup-node@v4 with: - node-version: "20" + node-version-file: '.nvmrc' - name: Install dependencies run: npm ci - name: Build diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 58067ac81ac..b7d5fb95c1e 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout repository for Typedoc - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: 'recursive' path: pokerogue_docs @@ -34,14 +34,14 @@ jobs: sudo apt update sudo apt install -y git openssh-client - - name: Setup Node 20.13.1 - uses: actions/setup-node@v1 + - name: Setup Node 22.14.1 + uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 22 - name: Checkout repository for Github Pages if: github.event_name == 'push' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: pokerogue_gh ref: gh-pages diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 7e33a77a73a..d9592662998 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -29,6 +29,7 @@ jobs: uses: actions/setup-node@v4 # Use the setup-node action version 4 with: node-version-file: '.nvmrc' + cache: 'npm' - name: Install Node.js dependencies # Step to install Node.js dependencies run: npm ci # Use 'npm ci' to install dependencies diff --git a/.github/workflows/test-shard-template.yml b/.github/workflows/test-shard-template.yml index 9fc41d1b965..cee452f3a59 100644 --- a/.github/workflows/test-shard-template.yml +++ b/.github/workflows/test-shard-template.yml @@ -19,13 +19,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out Git repository - uses: actions/checkout@v4 + uses: actions/checkout@v4.2.2 with: submodules: 'recursive' - name: Set up Node.js uses: actions/setup-node@v4 with: - node-version: 20 + node-version-file: '.nvmrc' + cache: 'npm' - name: Install Node.js dependencies run: npm ci - name: Run tests diff --git a/.nvmrc b/.nvmrc index 9bcccb9439d..517f38666b4 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v20.13.1 +v22.14.0 diff --git a/README.md b/README.md index 5bb3ecfd26f..56392808b3c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ If you have the motivation and experience with Typescript/Javascript (or are wil #### Prerequisites -- node: 20.13.1 +- node: 22.14.0 - npm: [how to install](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) #### Running Locally diff --git a/package-lock.json b/package-lock.json index 6b880370f0b..622eac908de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "@hpcc-js/wasm": "^2.22.4", "@stylistic/eslint-plugin-ts": "^4.1.0", "@types/jsdom": "^21.1.7", - "@types/node": "^20.12.13", + "@types/node": "^22.13.14", "@typescript-eslint/eslint-plugin": "^8.28.0", "@typescript-eslint/parser": "^8.28.0", "@vitest/coverage-istanbul": "^3.0.9", @@ -2582,12 +2582,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.14.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", - "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", + "version": "22.13.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz", + "integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.20.0" } }, "node_modules/@types/statuses": { @@ -7312,9 +7313,9 @@ "dev": true }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index c84e926fc35..ffe4c06bea0 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@hpcc-js/wasm": "^2.22.4", "@stylistic/eslint-plugin-ts": "^4.1.0", "@types/jsdom": "^21.1.7", - "@types/node": "^20.12.13", + "@types/node": "^22.13.14", "@typescript-eslint/eslint-plugin": "^8.28.0", "@typescript-eslint/parser": "^8.28.0", "@vitest/coverage-istanbul": "^3.0.9", @@ -67,6 +67,6 @@ "phaser3-rex-plugins": "^1.80.14" }, "engines": { - "node": ">=20.0.0" + "node": ">=22.0.0" } } From 3ec8f236f92b022e370eedcc6b695c057c6d7ac2 Mon Sep 17 00:00:00 2001 From: AJ Fontaine <36677462+Fontbane@users.noreply.github.com> Date: Mon, 14 Apr 2025 20:13:05 -0400 Subject: [PATCH 21/52] [Refactor] Change how rival event rewards are generated (#5638) * Change how rival event rewards are generated * Simplify to switch case Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/trainers/trainer-config.ts | 13 ------------- src/phases/trainer-victory-phase.ts | 6 ------ src/phases/victory-phase.ts | 21 +++++++++++++++------ 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index 0ab7119dab9..4efe294f7d0 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -116,7 +116,6 @@ export class TrainerConfig { public modifierRewardFuncs: ModifierTypeFunc[] = []; public partyTemplates: TrainerPartyTemplate[]; public partyTemplateFunc: PartyTemplateFunc; - public eventRewardFuncs: ModifierTypeFunc[] = []; public partyMemberFuncs: PartyMemberFuncs = {}; public speciesPools: TrainerTierPools; public speciesFilter: PokemonSpeciesFilter; @@ -517,16 +516,6 @@ export class TrainerConfig { // return ret; // } - /** - * Sets eventRewardFuncs to the active event rewards for the specified wave - * @param wave Associated with {@linkcode getFixedBattleEventRewards} - * @returns this - */ - setEventModifierRewardFuncs(wave: number): TrainerConfig { - this.eventRewardFuncs = timedEventManager.getFixedBattleEventRewards(wave).map(r => modifierTypes[r]); - return this; - } - setModifierRewardFuncs(...modifierTypeFuncs: (() => ModifierTypeFunc)[]): TrainerConfig { this.modifierRewardFuncs = modifierTypeFuncs.map(func => () => { const modifierTypeFunc = func(); @@ -3692,7 +3681,6 @@ export const trainerConfigs: TrainerConfigs = { () => modifierTypes.SUPER_EXP_CHARM, () => modifierTypes.EXP_SHARE, ) - .setEventModifierRewardFuncs(8) .setPartyMemberFunc( 0, getRandomPartyMemberFunc( @@ -3760,7 +3748,6 @@ export const trainerConfigs: TrainerConfigs = { .setMixedBattleBgm("battle_rival") .setPartyTemplates(trainerPartyTemplates.RIVAL_2) .setModifierRewardFuncs(() => modifierTypes.EXP_SHARE) - .setEventModifierRewardFuncs(25) .setPartyMemberFunc( 0, getRandomPartyMemberFunc( diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts index 637ddea8b56..f17071f118e 100644 --- a/src/phases/trainer-victory-phase.ts +++ b/src/phases/trainer-victory-phase.ts @@ -26,12 +26,6 @@ export class TrainerVictoryPhase extends BattlePhase { globalScene.unshiftPhase(new ModifierRewardPhase(modifierRewardFunc)); } - if (timedEventManager.isEventActive()) { - for (const rewardFunc of globalScene.currentBattle.trainer?.config.eventRewardFuncs!) { - globalScene.unshiftPhase(new ModifierRewardPhase(rewardFunc)); - } - } - const trainerType = globalScene.currentBattle.trainer?.config.trainerType!; // TODO: is this bang correct? // Validate Voucher for boss trainers if (vouchers.hasOwnProperty(TrainerType[trainerType])) { diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index 78bf72195e8..9f4412fe270 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -13,6 +13,7 @@ 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"; export class VictoryPhase extends PokemonPhase { /** If true, indicates that the phase is intended for EXP purposes only, and not to continue a battle to next phase */ @@ -53,12 +54,20 @@ export class VictoryPhase extends PokemonPhase { } if (globalScene.gameMode.isEndless || !globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) { globalScene.pushPhase(new EggLapsePhase()); - if ( - globalScene.gameMode.isClassic && - globalScene.currentBattle.waveIndex === 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)); + if (globalScene.gameMode.isClassic) { + switch (globalScene.currentBattle.waveIndex) { + case ClassicFixedBossWaves.RIVAL_1: + case ClassicFixedBossWaves.RIVAL_2: + // Get event modifiers for this wave + timedEventManager + .getFixedBattleEventRewards(globalScene.currentBattle.waveIndex) + .map(r => globalScene.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)); + break; + } } if (globalScene.currentBattle.waveIndex % 10) { globalScene.pushPhase(new SelectModifierPhase(undefined, undefined, this.getFixedBattleCustomModifiers())); From 4740b593a02dfdc0b45e5515d56bf30a0d7cf4f8 Mon Sep 17 00:00:00 2001 From: Madmadness65 <59298170+Madmadness65@users.noreply.github.com> Date: Tue, 15 Apr 2025 00:27:14 -0500 Subject: [PATCH 22/52] =?UTF-8?q?[Balance]=20Fix=20Depot=20Agent=20trainer?= =?UTF-8?q?=20type=20lacking=20Pok=C3=A9mon=20(#5623)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix Depot Agent trainer type lacking Pokémon Also removes a stray duplicate Barboach from the Fisherman. --- src/data/trainers/trainer-config.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index 4efe294f7d0..d9922ecc097 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -1318,7 +1318,16 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.RARE]: [Species.BELLOSSOM, Species.HITMONTOP, Species.MIME_JR, Species.ORICORIO], [TrainerPoolTier.SUPER_RARE]: [Species.QUAXLY, Species.JANGMO_O], }), - [TrainerType.DEPOT_AGENT]: new TrainerConfig(++t).setMoneyMultiplier(1.45).setEncounterBgm(TrainerType.CLERK), + [TrainerType.DEPOT_AGENT]: new TrainerConfig(++t) + .setMoneyMultiplier(1.45) + .setEncounterBgm(TrainerType.CLERK) + .setPartyTemplates( + trainerPartyTemplates.TWO_AVG, + trainerPartyTemplates.THREE_WEAK, + trainerPartyTemplates.THREE_AVG, + trainerPartyTemplates.FOUR_WEAK, + ) + .setSpeciesFilter(s => s.isOfType(PokemonType.GROUND)), [TrainerType.DOCTOR]: new TrainerConfig(++t) .setHasGenders("Nurse", "lass") .setHasDouble("Medical Team") @@ -1369,7 +1378,6 @@ export const trainerConfigs: TrainerConfigs = { Species.CHINCHOU, Species.CORSOLA, Species.WAILMER, - Species.BARBOACH, Species.CLAMPERL, Species.LUVDISC, Species.MANTYKE, From ff44cbfa97b834bfbb9172ceef5abce1d9cec13d Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Tue, 15 Apr 2025 09:08:35 -0500 Subject: [PATCH 23/52] [Refactor] Refactor ability file part 1 (#5589) * Move ability.ts to subfolder * Extract types out of ability.ts * Update imports in ability.ts and friends * Cleanup imports in ability.ts * Re-add imports lost during sort * Update imports forgotten during rebase * Re-import proper type from enums * Update biome.jsonc Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Add commit to force tests to rerun --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- biome.jsonc | 2 +- src/@types/ability-types.ts | 11 + src/battle-scene.ts | 4 +- src/data/abilities/ab-attrs/ab-attr.ts | 54 ++++ src/data/abilities/ability-class.ts | 137 +++++++++ src/data/{ => abilities}/ability.ts | 266 ++++-------------- src/data/arena-tag.ts | 2 +- src/data/battler-tags.ts | 4 +- src/data/berry.ts | 2 +- src/data/data-lists.ts | 3 + src/data/moves/move.ts | 4 +- .../encounters/clowning-around-encounter.ts | 2 +- .../encounters/fiery-fallout-encounter.ts | 2 +- .../the-winstrate-challenge-encounter.ts | 2 +- .../encounters/training-session-encounter.ts | 4 +- .../mystery-encounter-requirements.ts | 2 +- src/data/weather.ts | 2 +- src/field/arena.ts | 2 +- src/field/pokemon.ts | 7 +- src/loading-scene.ts | 2 +- src/modifier/modifier.ts | 2 +- src/phases/attempt-run-phase.ts | 7 +- src/phases/battle-end-phase.ts | 2 +- src/phases/berry-phase.ts | 2 +- src/phases/encounter-phase.ts | 2 +- src/phases/faint-phase.ts | 2 +- src/phases/move-effect-phase.ts | 2 +- src/phases/move-end-phase.ts | 2 +- src/phases/move-phase.ts | 2 +- src/phases/new-biome-encounter-phase.ts | 2 +- src/phases/obtain-status-effect-phase.ts | 2 +- src/phases/post-summon-phase.ts | 2 +- src/phases/post-turn-status-effect-phase.ts | 2 +- src/phases/quiet-form-change-phase.ts | 2 +- src/phases/stat-stage-change-phase.ts | 2 +- src/phases/summon-phase.ts | 2 +- src/phases/switch-summon-phase.ts | 2 +- src/phases/turn-end-phase.ts | 2 +- src/phases/turn-start-phase.ts | 2 +- src/phases/weather-effect-phase.ts | 2 +- src/ui/pokedex-page-ui-handler.ts | 2 +- src/ui/pokedex-scan-ui-handler.ts | 2 +- src/ui/pokedex-ui-handler.ts | 2 +- src/ui/starter-select-ui-handler.ts | 4 +- src/ui/summary-ui-handler.ts | 2 +- test/abilities/arena_trap.test.ts | 2 +- test/abilities/flower_gift.test.ts | 2 +- test/abilities/flower_veil.test.ts | 2 +- test/abilities/forecast.test.ts | 2 +- test/abilities/friend_guard.test.ts | 2 +- test/abilities/good_as_gold.test.ts | 2 +- test/abilities/magic_bounce.test.ts | 2 +- test/abilities/neutralizing_gas.test.ts | 2 +- test/abilities/quick_draw.test.ts | 3 +- test/abilities/sand_veil.test.ts | 3 +- test/abilities/shield_dust.test.ts | 2 +- test/abilities/steely_spirit.test.ts | 2 +- test/abilities/unburden.test.ts | 2 +- test/battle/ability_swap.test.ts | 2 +- test/moves/flame_burst.test.ts | 2 +- test/moves/pledge_moves.test.ts | 2 +- test/moves/safeguard.test.ts | 3 +- test/moves/secret_power.test.ts | 3 +- test/testUtils/testFileInitialization.ts | 2 +- 64 files changed, 337 insertions(+), 276 deletions(-) create mode 100644 src/@types/ability-types.ts create mode 100644 src/data/abilities/ab-attrs/ab-attr.ts create mode 100644 src/data/abilities/ability-class.ts rename src/data/{ => abilities}/ability.ts (97%) create mode 100644 src/data/data-lists.ts diff --git a/biome.jsonc b/biome.jsonc index da80d8ee127..9d0e6a9b5ff 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -31,7 +31,7 @@ "src/overrides.ts", // TODO: these files are too big and complex, ignore them until their respective refactors "src/data/moves/move.ts", - "src/data/ability.ts", + "src/data/abilities/ability.ts", "src/field/pokemon.ts", // this file is just too big: diff --git a/src/@types/ability-types.ts b/src/@types/ability-types.ts new file mode 100644 index 00000000000..5d21aaaa844 --- /dev/null +++ b/src/@types/ability-types.ts @@ -0,0 +1,11 @@ +import type { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr"; +import type Move from "#app/data/moves/move"; +import type Pokemon from "#app/field/pokemon"; +import type { BattleStat } from "#enums/stat"; + +export type AbAttrApplyFunc = (attr: TAttr, passive: boolean) => void; +export type AbAttrSuccessFunc = (attr: TAttr, passive: boolean) => boolean; +export type AbAttrCondition = (pokemon: Pokemon) => boolean; +export type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean; +export type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean; +export type PokemonStatStageChangeCondition = (target: Pokemon, statsChanged: BattleStat[], stages: number) => boolean; diff --git a/src/battle-scene.ts b/src/battle-scene.ts index dd983f2b397..90f53d6a95e 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -67,7 +67,6 @@ import { } from "#app/modifier/modifier-type"; import AbilityBar from "#app/ui/ability-bar"; import { - allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs, @@ -75,7 +74,8 @@ import { DoubleBattleChanceAbAttr, PostBattleInitAbAttr, PostItemLostAbAttr, -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; +import { allAbilities } from "./data/data-lists"; import type { FixedBattleConfig } from "#app/battle"; import Battle, { BattleType } from "#app/battle"; import type { GameMode } from "#app/game-mode"; diff --git a/src/data/abilities/ab-attrs/ab-attr.ts b/src/data/abilities/ab-attrs/ab-attr.ts new file mode 100644 index 00000000000..c8ead691b25 --- /dev/null +++ b/src/data/abilities/ab-attrs/ab-attr.ts @@ -0,0 +1,54 @@ +import type { AbAttrCondition } from "#app/@types/ability-types"; +import type Pokemon from "#app/field/pokemon"; +import type * as Utils from "#app/utils"; + +export abstract class AbAttr { + public showAbility: boolean; + private extraCondition: AbAttrCondition; + + constructor(showAbility = true) { + this.showAbility = showAbility; + } + + /** + * Applies ability effects without checking conditions + * @param _pokemon - The pokemon to apply this ability to + * @param _passive - Whether or not the ability is a passive + * @param _simulated - Whether the call is simulated + * @param _args - Extra args passed to the function. Handled by child classes. + * @see {@linkcode canApply} + */ + apply( + _pokemon: Pokemon, + _passive: boolean, + _simulated: boolean, + _cancelled: Utils.BooleanHolder | null, + _args: any[], + ): void {} + + getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null { + return null; + } + + getCondition(): AbAttrCondition | null { + return this.extraCondition || null; + } + + addCondition(condition: AbAttrCondition): AbAttr { + this.extraCondition = condition; + return this; + } + + /** + * Returns a boolean describing whether the ability can be applied under current conditions + * @param _pokemon - The pokemon to apply this ability to + * @param _passive - Whether or not the ability is a passive + * @param _simulated - Whether the call is simulated + * @param _args - Extra args passed to the function. Handled by child classes. + * @returns `true` if the ability can be applied, `false` otherwise + * @see {@linkcode apply} + */ + canApply(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _args: any[]): boolean { + return true; + } +} diff --git a/src/data/abilities/ability-class.ts b/src/data/abilities/ability-class.ts new file mode 100644 index 00000000000..b4cda2482d4 --- /dev/null +++ b/src/data/abilities/ability-class.ts @@ -0,0 +1,137 @@ +import { Abilities } from "#enums/abilities"; +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 { Constructor } from "#app/utils"; + +export class Ability implements Localizable { + public id: Abilities; + + private nameAppend: string; + public name: string; + public description: string; + public generation: number; + public isBypassFaint: boolean; + public isIgnorable: boolean; + public isSuppressable = true; + public isCopiable = true; + public isReplaceable = true; + public attrs: AbAttr[]; + public conditions: AbAttrCondition[]; + + constructor(id: Abilities, generation: number) { + this.id = id; + + this.nameAppend = ""; + this.generation = generation; + this.attrs = []; + this.conditions = []; + + this.isSuppressable = true; + this.isCopiable = true; + this.isReplaceable = true; + + this.localize(); + } + + public get isSwappable(): boolean { + return this.isCopiable && this.isReplaceable; + } + localize(): void { + const i18nKey = Abilities[this.id] + .split("_") + .filter(f => f) + .map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase())) + .join("") as string; + + this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`) as string}${this.nameAppend}` : ""; + this.description = this.id ? (i18next.t(`ability:${i18nKey}.description`) as string) : ""; + } + + /** + * Get all ability attributes that match `attrType` + * @param attrType any attribute that extends {@linkcode AbAttr} + * @returns Array of attributes that match `attrType`, Empty Array if none match. + */ + getAttrs(attrType: Constructor): T[] { + return this.attrs.filter((a): a is T => a instanceof attrType); + } + + /** + * Check if an ability has an attribute that matches `attrType` + * @param attrType any attribute that extends {@linkcode AbAttr} + * @returns true if the ability has attribute `attrType` + */ + hasAttr(attrType: Constructor): boolean { + return this.attrs.some(attr => attr instanceof attrType); + } + + attr>(AttrType: T, ...args: ConstructorParameters): Ability { + const attr = new AttrType(...args); + this.attrs.push(attr); + + return this; + } + + conditionalAttr>( + condition: AbAttrCondition, + AttrType: T, + ...args: ConstructorParameters + ): Ability { + const attr = new AttrType(...args); + attr.addCondition(condition); + this.attrs.push(attr); + + return this; + } + + bypassFaint(): Ability { + this.isBypassFaint = true; + return this; + } + + ignorable(): Ability { + this.isIgnorable = true; + return this; + } + + unsuppressable(): Ability { + this.isSuppressable = false; + return this; + } + + uncopiable(): Ability { + this.isCopiable = false; + return this; + } + + unreplaceable(): Ability { + this.isReplaceable = false; + return this; + } + + condition(condition: AbAttrCondition): Ability { + this.conditions.push(condition); + + return this; + } + + partial(): this { + this.nameAppend += " (P)"; + return this; + } + + unimplemented(): this { + this.nameAppend += " (N)"; + return this; + } + + /** + * Internal flag used for developers to document edge cases. When using this, please be sure to document the edge case. + * @returns the ability + */ + edgeCase(): this { + return this; + } +} diff --git a/src/data/ability.ts b/src/data/abilities/ability.ts similarity index 97% rename from src/data/ability.ts rename to src/data/abilities/ability.ts index 3e32a624f9f..17a8eddf47f 100644 --- a/src/data/ability.ts +++ b/src/data/abilities/ability.ts @@ -1,228 +1,75 @@ -import type { EnemyPokemon, PokemonMove } from "../field/pokemon"; -import type Pokemon from "../field/pokemon"; -import { HitResult, MoveResult, PlayerPokemon } from "../field/pokemon"; -import { PokemonType } from "#enums/pokemon-type"; +import { HitResult, MoveResult, PlayerPokemon } from "#app/field/pokemon"; import { BooleanHolder, NumberHolder, toDmgValue, isNullOrUndefined, randSeedItem, randSeedInt, type Constructor } from "#app/utils"; -import { getPokemonNameWithAffix } from "../messages"; -import type { Weather } from "#app/data/weather"; -import type { BattlerTag } from "./battler-tags"; -import { BattlerTagLapseType, GroundedTag } from "./battler-tags"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { BattlerTagLapseType, GroundedTag } from "#app/data/battler-tags"; import { getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "#app/data/status-effect"; -import { Gender } from "./gender"; -import type Move from "./moves/move"; -import { AttackMove, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./moves/move"; -import { MoveFlags } from "#enums/MoveFlags"; -import { MoveTarget } from "#enums/MoveTarget"; -import { MoveCategory } from "#enums/MoveCategory"; -import type { ArenaTrapTag, SuppressAbilitiesTag } from "./arena-tag"; -import { ArenaTagSide } from "./arena-tag"; -import { BerryModifier, HitHealModifier, PokemonHeldItemModifier } from "../modifier/modifier"; -import { TerrainType } from "./terrain"; -import { SpeciesFormChangeAbilityTrigger, SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "./pokemon-forms"; +import { Gender } from "#app/data/gender"; +import { + AttackMove, + FlinchAttr, + OneHitKOAttr, + HitHealAttr, + allMoves, + StatusMove, + SelfStatusMove, + VariablePowerAttr, + applyMoveAttrs, + VariableMoveTypeAttr, + RandomMovesetMoveAttr, + RandomMoveAttr, + NaturePowerAttr, + CopyMoveAttr, + NeutralDamageAgainstFlyingTypeMultiplierAttr, + FixedDamageAttr, +} from "#app/data/moves/move"; +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 i18next from "i18next"; -import type { Localizable } from "#app/interfaces/locales"; -import { Command } from "../ui/command-ui-handler"; +import { Command } from "#app/ui/command-ui-handler"; import { BerryModifierType } from "#app/modifier/modifier-type"; -import { getPokeballName } from "./pokeball"; -import type { BattlerIndex } from "#app/battle"; +import { getPokeballName } from "#app/data/pokeball"; import { BattleType } from "#app/battle"; -import { Abilities } from "#enums/abilities"; -import { ArenaTagType } from "#enums/arena-tag-type"; -import { BattlerTagType } from "#enums/battler-tag-type"; -import { Moves } from "#enums/moves"; -import { Species } from "#enums/species"; -import { Stat, type BattleStat, type EffectiveStat, BATTLE_STATS, EFFECTIVE_STATS, getStatKey } from "#app/enums/stat"; 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 { globalScene } from "#app/global-scene"; -import { SwitchType } from "#app/enums/switch-type"; 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"; + +// Enum imports +import { Stat, type BattleStat , BATTLE_STATS, EFFECTIVE_STATS, getStatKey, type EffectiveStat } from "#enums/stat"; +import { PokemonType } from "#enums/pokemon-type"; import { PokemonAnimType } from "#enums/pokemon-anim-type"; import { StatusEffect } from "#enums/status-effect"; import { WeatherType } from "#enums/weather-type"; -import { PokemonTransformPhase } from "#app/phases/pokemon-transform-phase"; +import { Abilities } from "#enums/abilities"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { SwitchType } from "#enums/switch-type"; +import { MoveFlags } from "#enums/MoveFlags"; +import { MoveTarget } from "#enums/MoveTarget"; +import { MoveCategory } from "#enums/MoveCategory"; -export class Ability implements Localizable { - public id: Abilities; - - private nameAppend: string; - public name: string; - public description: string; - public generation: number; - public isBypassFaint: boolean; - public isIgnorable: boolean; - public isSuppressable = true; - public isCopiable = true; - public isReplaceable = true; - public attrs: AbAttr[]; - public conditions: AbAttrCondition[]; - - constructor(id: Abilities, generation: number) { - this.id = id; - - this.nameAppend = ""; - this.generation = generation; - this.attrs = []; - this.conditions = []; - - this.isSuppressable = true; - this.isCopiable = true; - this.isReplaceable = true; - - this.localize(); - } - - public get isSwappable(): boolean { - return this.isCopiable && this.isReplaceable; - } - localize(): void { - const i18nKey = Abilities[this.id].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("") as string; - - this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`) as string}${this.nameAppend}` : ""; - this.description = this.id ? i18next.t(`ability:${i18nKey}.description`) as string : ""; - } - - /** - * Get all ability attributes that match `attrType` - * @param attrType any attribute that extends {@linkcode AbAttr} - * @returns Array of attributes that match `attrType`, Empty Array if none match. - */ - getAttrs(attrType: Constructor ): T[] { - return this.attrs.filter((a): a is T => a instanceof attrType); - } - - /** - * Check if an ability has an attribute that matches `attrType` - * @param attrType any attribute that extends {@linkcode AbAttr} - * @returns true if the ability has attribute `attrType` - */ - hasAttr(attrType: Constructor): boolean { - return this.attrs.some((attr) => attr instanceof attrType); - } - - attr>(AttrType: T, ...args: ConstructorParameters): Ability { - const attr = new AttrType(...args); - this.attrs.push(attr); - - return this; - } - - conditionalAttr>(condition: AbAttrCondition, AttrType: T, ...args: ConstructorParameters): Ability { - const attr = new AttrType(...args); - attr.addCondition(condition); - this.attrs.push(attr); - - return this; - } - - bypassFaint(): Ability { - this.isBypassFaint = true; - return this; - } - - ignorable(): Ability { - this.isIgnorable = true; - return this; - } - - unsuppressable(): Ability { - this.isSuppressable = false; - return this; - } - - uncopiable(): Ability { - this.isCopiable = false; - return this; - } - - unreplaceable(): Ability { - this.isReplaceable = false; - return this; - } - - condition(condition: AbAttrCondition): Ability { - this.conditions.push(condition); - - return this; - } - - partial(): this { - this.nameAppend += " (P)"; - return this; - } - - unimplemented(): this { - this.nameAppend += " (N)"; - return this; - } - - /** - * Internal flag used for developers to document edge cases. When using this, please be sure to document the edge case. - * @returns the ability - */ - edgeCase(): this { - return this; - } -} - -type AbAttrApplyFunc = (attr: TAttr, passive: boolean) => void; -type AbAttrSuccessFunc = (attr: TAttr, passive: boolean) => boolean; -type AbAttrCondition = (pokemon: Pokemon) => boolean; - -// TODO: Can this be improved? -type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean; -type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean; -type PokemonStatStageChangeCondition = (target: Pokemon, statsChanged: BattleStat[], stages: number) => boolean; - -export abstract class AbAttr { - public showAbility: boolean; - private extraCondition: AbAttrCondition; - - constructor(showAbility = true) { - this.showAbility = showAbility; - } - - /** - * Applies ability effects without checking conditions - * @param pokemon - The pokemon to apply this ability to - * @param passive - Whether or not the ability is a passive - * @param simulated - Whether the call is simulated - * @param args - Extra args passed to the function. Handled by child classes. - * @see {@linkcode canApply} - */ - apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: BooleanHolder | null, args: any[]): void {} - - getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null { - return null; - } - - getCondition(): AbAttrCondition | null { - return this.extraCondition || null; - } - - addCondition(condition: AbAttrCondition): AbAttr { - this.extraCondition = condition; - return this; - } - - /** - * Returns a boolean describing whether the ability can be applied under current conditions - * @param pokemon - The pokemon to apply this ability to - * @param passive - Whether or not the ability is a passive - * @param simulated - Whether the call is simulated - * @param args - Extra args passed to the function. Handled by child classes. - * @returns `true` if the ability can be applied, `false` otherwise - * @see {@linkcode apply} - */ - canApply(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - return true; - } -} +// 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 { BattlerIndex } from "#app/battle"; +import type Move from "#app/data/moves/move"; +import type { ArenaTrapTag, SuppressAbilitiesTag } from "#app/data/arena-tag"; export class BlockRecoilDamageAttr extends AbAttr { constructor() { @@ -233,7 +80,7 @@ export class BlockRecoilDamageAttr extends AbAttr { cancelled.value = true; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]) { + getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]) { return i18next.t("abilityTriggers:blockRecoilDamage", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName }); } } @@ -6453,10 +6300,9 @@ function getPokemonWithWeatherBasedForms() { ); } -export const allAbilities = [ new Ability(Abilities.NONE, 3) ]; - export function initAbilities() { allAbilities.push( + new Ability(Abilities.NONE, 3), new Ability(Abilities.STENCH, 3) .attr(PostAttackApplyBattlerTagAbAttr, false, (user, target, move) => !move.hasAttr(FlinchAttr) && !move.hitsSubstitute(user, target) ? 10 : 0, BattlerTagType.FLINCHED), new Ability(Abilities.DRIZZLE, 3) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 871f622f70a..1fe1eca4bba 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -18,7 +18,7 @@ import { applyAbAttrs, applyOnGainAbAttrs, applyOnLoseAbAttrs, -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; import { Stat } from "#enums/stat"; import { CommonAnim, CommonBattleAnim } from "#app/data/battle-anims"; import i18next from "i18next"; diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 76e91485460..401fd9903d1 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1,13 +1,13 @@ import { globalScene } from "#app/global-scene"; import { - allAbilities, applyAbAttrs, BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ProtectStatAbAttr, ConditionalUserFieldProtectStatAbAttr, ReverseDrainAbAttr, -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; +import { allAbilities } from "./data-lists"; import { ChargeAnim, CommonAnim, CommonBattleAnim, MoveChargeAnim } from "#app/data/battle-anims"; import type Move from "#app/data/moves/move"; import { diff --git a/src/data/berry.ts b/src/data/berry.ts index 8a58d337aa4..e118b45711c 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -9,7 +9,7 @@ import { ReduceBerryUseThresholdAbAttr, applyAbAttrs, applyPostItemLostAbAttrs, -} from "./ability"; +} from "./abilities/ability"; import i18next from "i18next"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; diff --git a/src/data/data-lists.ts b/src/data/data-lists.ts new file mode 100644 index 00000000000..d3c31abc851 --- /dev/null +++ b/src/data/data-lists.ts @@ -0,0 +1,3 @@ +import type { Ability } from "./abilities/ability-class"; + +export const allAbilities: Ability[] = []; diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 962a13bb840..b68dd0d3e1d 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -34,7 +34,6 @@ import { WeatherType } from "#enums/weather-type"; import type { ArenaTrapTag } from "../arena-tag"; import { ArenaTagSide, WeakenMoveTypeTag } from "../arena-tag"; import { - allAbilities, AllyMoveCategoryPowerBoostAbAttr, applyAbAttrs, applyPostAttackAbAttrs, @@ -65,7 +64,8 @@ import { UserFieldMoveTypePowerBoostAbAttr, VariableMovePowerAbAttr, WonderSkinAbAttr, -} from "../ability"; +} from "../abilities/ability"; +import { allAbilities } from "../data-lists"; import { AttackTypeBoosterModifier, BerryModifier, diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index eca99fc0c13..5edc2e6bbc5 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -38,7 +38,7 @@ import i18next from "i18next"; import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler"; import type { PlayerPokemon } from "#app/field/pokemon"; import { PokemonMove } from "#app/field/pokemon"; -import { Ability } from "#app/data/ability"; +import { Ability } from "#app/data/abilities/ability-class"; import { BerryModifier } from "#app/modifier/modifier"; import { BerryType } from "#enums/berry-type"; import { BattlerIndex } from "#app/battle"; diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 6118fe3d0de..f0b7a05a21c 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -46,7 +46,7 @@ import { Abilities } from "#enums/abilities"; 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/ability"; +import { Ability } from "#app/data/abilities/ability-class"; import { FIRE_RESISTANT_ABILITIES } from "#app/data/mystery-encounters/requirements/requirement-groups"; /** the i18n namespace for the encounter */ 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 aca04ad50ed..41bf87351f4 100644 --- a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts @@ -24,7 +24,7 @@ import { PokemonType } from "#enums/pokemon-type"; import { BerryType } from "#enums/berry-type"; import { Stat } from "#enums/stat"; import { SpeciesFormChangeAbilityTrigger } from "#app/data/pokemon-forms"; -import { applyPostBattleInitAbAttrs, PostBattleInitAbAttr } from "#app/data/ability"; +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"; diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index cc56f3efa42..e8711be172d 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -1,5 +1,5 @@ -import type { Ability } from "#app/data/ability"; -import { allAbilities } from "#app/data/ability"; +import type { Ability } from "#app/data/abilities/ability-class"; +import { allAbilities } from "#app/data/data-lists"; import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { initBattleWithEnemyConfig, diff --git a/src/data/mystery-encounters/mystery-encounter-requirements.ts b/src/data/mystery-encounters/mystery-encounter-requirements.ts index f9aedf2c1a7..948e3e96ef0 100644 --- a/src/data/mystery-encounters/mystery-encounter-requirements.ts +++ b/src/data/mystery-encounters/mystery-encounter-requirements.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "../data-lists"; import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { Nature } from "#enums/nature"; import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms"; diff --git a/src/data/weather.ts b/src/data/weather.ts index a8dd0a66492..31b460bbddb 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -6,7 +6,7 @@ import { PokemonType } from "#enums/pokemon-type"; import type Move from "./moves/move"; import { AttackMove } from "./moves/move"; import { randSeedInt } from "#app/utils"; -import { SuppressWeatherEffectAbAttr } from "./ability"; +import { SuppressWeatherEffectAbAttr } from "./abilities/ability"; import { TerrainType, getTerrainName } from "./terrain"; import i18next from "i18next"; import { globalScene } from "#app/global-scene"; diff --git a/src/field/arena.ts b/src/field/arena.ts index adc3123ce81..1bc465c7dbb 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -26,7 +26,7 @@ import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, TerrainEventTypeChangeAbAttr, -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; import type Pokemon from "#app/field/pokemon"; import Overrides from "#app/overrides"; import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index b59b7ba01fe..22ede4260c3 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -136,7 +136,8 @@ import { WeakenMoveScreenTag, } from "#app/data/arena-tag"; import type { SuppressAbilitiesTag } from "#app/data/arena-tag"; -import type { Ability, AbAttr } from "#app/data/ability"; +import type { Ability } from "#app/data/abilities/ability-class"; +import type { AbAttr } from "#app/data/abilities/ab-attrs/ab-attr"; import { StatMultiplierAbAttr, BlockCritAbAttr, @@ -151,7 +152,6 @@ import { StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, - allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, @@ -188,7 +188,8 @@ import { applyAllyStatMultiplierAbAttrs, AllyStatMultiplierAbAttr, MoveAbilityBypassAbAttr -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; +import { allAbilities } from "#app/data/data-lists"; import type PokemonData from "#app/system/pokemon-data"; import { BattlerIndex } from "#app/battle"; import { Mode } from "#app/ui/ui"; diff --git a/src/loading-scene.ts b/src/loading-scene.ts index b45cf64ff56..4ec2fdf1bb2 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -11,7 +11,7 @@ import { initEggMoves } from "#app/data/balance/egg-moves"; import { initPokemonForms } from "#app/data/pokemon-forms"; import { initSpecies } from "#app/data/pokemon-species"; import { initMoves } from "#app/data/moves/move"; -import { initAbilities } from "#app/data/ability"; +import { initAbilities } from "#app/data/abilities/ability"; import { initAchievements } from "#app/system/achv"; import { initTrainerTypeDialogue } from "#app/data/dialogue"; import { initChallenges } from "#app/data/challenge"; diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 80f14ba22ce..851fa33cedc 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -47,7 +47,7 @@ import { } from "./modifier-type"; import { Color, ShadowColor } from "#enums/color"; import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#app/data/balance/starters"; -import { applyAbAttrs, CommanderAbAttr } from "#app/data/ability"; +import { applyAbAttrs, CommanderAbAttr } from "#app/data/abilities/ability"; import { globalScene } from "#app/global-scene"; export type ModifierPredicate = (modifier: Modifier) => boolean; diff --git a/src/phases/attempt-run-phase.ts b/src/phases/attempt-run-phase.ts index e5691f5fb8e..5c51e5c589d 100644 --- a/src/phases/attempt-run-phase.ts +++ b/src/phases/attempt-run-phase.ts @@ -1,4 +1,9 @@ -import { applyAbAttrs, applyPreLeaveFieldAbAttrs, PreLeaveFieldAbAttr, RunSuccessAbAttr } from "#app/data/ability"; +import { + applyAbAttrs, + applyPreLeaveFieldAbAttrs, + PreLeaveFieldAbAttr, + RunSuccessAbAttr, +} from "#app/data/abilities/ability"; import { Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; import type { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon"; diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index 0d831c65b52..275a9017dfa 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { applyPostBattleAbAttrs, PostBattleAbAttr } from "#app/data/ability"; +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"; diff --git a/src/phases/berry-phase.ts b/src/phases/berry-phase.ts index e5614739903..ae593f66f34 100644 --- a/src/phases/berry-phase.ts +++ b/src/phases/berry-phase.ts @@ -1,4 +1,4 @@ -import { applyAbAttrs, PreventBerryUseAbAttr, HealFromBerryUseAbAttr } from "#app/data/ability"; +import { applyAbAttrs, PreventBerryUseAbAttr, HealFromBerryUseAbAttr } from "#app/data/abilities/ability"; import { CommonAnim } from "#app/data/battle-anims"; import { BerryUsedEvent } from "#app/events/battle-scene"; import { getPokemonNameWithAffix } from "#app/messages"; diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index 15f3d102e41..67236c1c041 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -1,7 +1,7 @@ import { BattlerIndex, BattleType } from "#app/battle"; import { globalScene } from "#app/global-scene"; import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; -import { applyAbAttrs, SyncEncounterNatureAbAttr, applyPreSummonAbAttrs, PreSummonAbAttr } from "#app/data/ability"; +import { applyAbAttrs, SyncEncounterNatureAbAttr, applyPreSummonAbAttrs, PreSummonAbAttr } from "#app/data/abilities/ability"; import { initEncounterAnims, loadEncounterAnimAssets } from "#app/data/battle-anims"; import { getCharVariantFromDialogue } from "#app/data/dialogue"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 7e1ae4ec07b..01a556115a6 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -8,7 +8,7 @@ import { PostFaintAbAttr, PostKnockOutAbAttr, PostVictoryAbAttr, -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; import type { DestinyBondTag, GrudgeTag } from "#app/data/battler-tags"; import { BattlerTagLapseType } from "#app/data/battler-tags"; import { battleSpecDialogue } from "#app/data/dialogue"; diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index acc7ac0f63a..af9f685eebe 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -14,7 +14,7 @@ import { PostDefendAbAttr, ReflectStatusMoveAbAttr, TypeImmunityAbAttr, -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag"; import { MoveAnim } from "#app/data/battle-anims"; import { diff --git a/src/phases/move-end-phase.ts b/src/phases/move-end-phase.ts index 176abee5e98..037596dca59 100644 --- a/src/phases/move-end-phase.ts +++ b/src/phases/move-end-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import { BattlerTagLapseType } from "#app/data/battler-tags"; import { PokemonPhase } from "./pokemon-phase"; import type { BattlerIndex } from "#app/battle"; -import { applyPostSummonAbAttrs, PostSummonRemoveEffectAbAttr } from "#app/data/ability"; +import { applyPostSummonAbAttrs, PostSummonRemoveEffectAbAttr } from "#app/data/abilities/ability"; import type Pokemon from "#app/field/pokemon"; export class MoveEndPhase extends PokemonPhase { diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 478229dcae8..dc394b8a134 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -10,7 +10,7 @@ import { PostMoveUsedAbAttr, RedirectMoveAbAttr, ReduceStatusEffectDurationAbAttr, -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; import type { DelayedAttackTag } from "#app/data/arena-tag"; import { CommonAnim } from "#app/data/battle-anims"; import { BattlerTagLapseType, CenterOfAttentionTag } from "#app/data/battler-tags"; diff --git a/src/phases/new-biome-encounter-phase.ts b/src/phases/new-biome-encounter-phase.ts index 3449a562c4a..6a7afcb8da8 100644 --- a/src/phases/new-biome-encounter-phase.ts +++ b/src/phases/new-biome-encounter-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { applyAbAttrs, PostBiomeChangeAbAttr } from "#app/data/ability"; +import { applyAbAttrs, PostBiomeChangeAbAttr } from "#app/data/abilities/ability"; import { getRandomWeatherType } from "#app/data/weather"; import { NextEncounterPhase } from "./next-encounter-phase"; diff --git a/src/phases/obtain-status-effect-phase.ts b/src/phases/obtain-status-effect-phase.ts index cba9399b996..10ae195b02f 100644 --- a/src/phases/obtain-status-effect-phase.ts +++ b/src/phases/obtain-status-effect-phase.ts @@ -7,7 +7,7 @@ import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { PokemonPhase } from "./pokemon-phase"; import { SpeciesFormChangeStatusEffectTrigger } from "#app/data/pokemon-forms"; -import { applyPostSetStatusAbAttrs, PostSetStatusAbAttr } from "#app/data/ability"; +import { applyPostSetStatusAbAttrs, PostSetStatusAbAttr } from "#app/data/abilities/ability"; import { isNullOrUndefined } from "#app/utils"; export class ObtainStatusEffectPhase extends PokemonPhase { diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts index 45b0a0f65ce..446d45bb2fa 100644 --- a/src/phases/post-summon-phase.ts +++ b/src/phases/post-summon-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { applyAbAttrs, applyPostSummonAbAttrs, CommanderAbAttr, PostSummonAbAttr } from "#app/data/ability"; +import { applyAbAttrs, applyPostSummonAbAttrs, CommanderAbAttr, PostSummonAbAttr } from "#app/data/abilities/ability"; import { ArenaTrapTag } from "#app/data/arena-tag"; import { StatusEffect } from "#app/enums/status-effect"; import { PokemonPhase } from "./pokemon-phase"; diff --git a/src/phases/post-turn-status-effect-phase.ts b/src/phases/post-turn-status-effect-phase.ts index 619ef22d01e..af9a9ac1c29 100644 --- a/src/phases/post-turn-status-effect-phase.ts +++ b/src/phases/post-turn-status-effect-phase.ts @@ -7,7 +7,7 @@ import { BlockStatusDamageAbAttr, PostDamageAbAttr, ReduceBurnDamageAbAttr, -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims"; import { getStatusEffectActivationText } from "#app/data/status-effect"; import { BattleSpec } from "#app/enums/battle-spec"; diff --git a/src/phases/quiet-form-change-phase.ts b/src/phases/quiet-form-change-phase.ts index 1512609abf9..f476919a628 100644 --- a/src/phases/quiet-form-change-phase.ts +++ b/src/phases/quiet-form-change-phase.ts @@ -16,7 +16,7 @@ import { ClearTerrainAbAttr, ClearWeatherAbAttr, PostTeraFormChangeStatChangeAbAttr, -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; export class QuietFormChangePhase extends BattlePhase { protected pokemon: Pokemon; diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index 4c82661a3bb..f52e4fb06a0 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -10,7 +10,7 @@ import { ReflectStatStageChangeAbAttr, StatStageChangeCopyAbAttr, StatStageChangeMultiplierAbAttr, -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; import { ArenaTagSide, MistTag } from "#app/data/arena-tag"; import type { ArenaTag } from "#app/data/arena-tag"; import type Pokemon from "#app/field/pokemon"; diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index 7379d509e55..60d45f19c0c 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -13,7 +13,7 @@ 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/ability"; +import { applyPreSummonAbAttrs, PreSummonAbAttr } from "#app/data/abilities/ability"; import { globalScene } from "#app/global-scene"; export class SummonPhase extends PartyMemberPokemonPhase { diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index d63cdb90f25..f8728f3f9b9 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -5,7 +5,7 @@ import { PostDamageForceSwitchAbAttr, PreSummonAbAttr, PreSwitchOutAbAttr, -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; import { allMoves, ForceSwitchOutAttr } from "#app/data/moves/move"; import { getPokeballTintColor } from "#app/data/pokeball"; import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms"; diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index 9b84ea05e58..fe16a4a864e 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -1,4 +1,4 @@ -import { applyPostTurnAbAttrs, PostTurnAbAttr } from "#app/data/ability"; +import { applyPostTurnAbAttrs, PostTurnAbAttr } from "#app/data/abilities/ability"; import { BattlerTagLapseType } from "#app/data/battler-tags"; import { TerrainType } from "#app/data/terrain"; import { WeatherType } from "#app/enums/weather-type"; diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index d5b4160fe1b..ba6ace2d188 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -1,4 +1,4 @@ -import { applyAbAttrs, BypassSpeedChanceAbAttr, PreventBypassSpeedChanceAbAttr } from "#app/data/ability"; +import { applyAbAttrs, BypassSpeedChanceAbAttr, PreventBypassSpeedChanceAbAttr } from "#app/data/abilities/ability"; import { allMoves, MoveHeaderAttr } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { Stat } from "#app/enums/stat"; diff --git a/src/phases/weather-effect-phase.ts b/src/phases/weather-effect-phase.ts index 5284c9fba85..b83eab43b65 100644 --- a/src/phases/weather-effect-phase.ts +++ b/src/phases/weather-effect-phase.ts @@ -7,7 +7,7 @@ import { BlockNonDirectDamageAbAttr, applyPostWeatherLapseAbAttrs, PostWeatherLapseAbAttr, -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; import { CommonAnim } from "#app/data/battle-anims"; import type { Weather } from "#app/data/weather"; import { getWeatherDamageMessage, getWeatherLapseMessage } from "#app/data/weather"; diff --git a/src/ui/pokedex-page-ui-handler.ts b/src/ui/pokedex-page-ui-handler.ts index 407ebfcd843..3f8959c6219 100644 --- a/src/ui/pokedex-page-ui-handler.ts +++ b/src/ui/pokedex-page-ui-handler.ts @@ -5,7 +5,7 @@ import { getVariantTint, getVariantIcon } from "#app/sprites/variant"; import { argbFromRgba } from "@material/material-color-utilities"; import i18next from "i18next"; import { starterColors } from "#app/battle-scene"; -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { GrowthRate, getGrowthRateColor } from "#app/data/exp"; import { Gender, getGenderColor, getGenderSymbol } from "#app/data/gender"; diff --git a/src/ui/pokedex-scan-ui-handler.ts b/src/ui/pokedex-scan-ui-handler.ts index b34246b97d1..171040f6f12 100644 --- a/src/ui/pokedex-scan-ui-handler.ts +++ b/src/ui/pokedex-scan-ui-handler.ts @@ -6,7 +6,7 @@ import type { OptionSelectItem } from "./abstact-option-select-ui-handler"; import { isNullOrUndefined } from "#app/utils"; import { Mode } from "./ui"; import { FilterTextRow } from "./filter-text"; -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; import { allMoves } from "#app/data/moves/move"; import { allSpecies } from "#app/data/pokemon-species"; import i18next from "i18next"; diff --git a/src/ui/pokedex-ui-handler.ts b/src/ui/pokedex-ui-handler.ts index 59b06d476a2..5fd3ca3e379 100644 --- a/src/ui/pokedex-ui-handler.ts +++ b/src/ui/pokedex-ui-handler.ts @@ -36,7 +36,7 @@ import type { Nature } from "#enums/nature"; import { addWindow } from "./ui-theme"; import type { OptionSelectConfig } from "./abstact-option-select-ui-handler"; import { FilterText, FilterTextRow } from "./filter-text"; -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; import { starterPassiveAbilities } from "#app/data/balance/passives"; import { allMoves } from "#app/data/moves/move"; import { speciesTmMoves } from "#app/data/balance/tms"; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 3e2940f45b9..9b0009d666e 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -8,8 +8,8 @@ import i18next from "i18next"; import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { starterColors } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; -import type { Ability } from "#app/data/ability"; -import { allAbilities } from "#app/data/ability"; +import type { Ability } from "#app/data/abilities/ability-class"; +import { allAbilities } from "#app/data/data-lists"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { GrowthRate, getGrowthRateColor } from "#app/data/exp"; import { Gender, getGenderColor, getGenderSymbol } from "#app/data/gender"; diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 04bcf71d7ae..5ff4a02793d 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -31,7 +31,7 @@ import { loggedInUser } from "#app/account"; import type { Variant } from "#app/sprites/variant"; import { getVariantTint } from "#app/sprites/variant"; import { Button } from "#enums/buttons"; -import type { Ability } from "#app/data/ability"; +import type { Ability } from "#app/data/abilities/ability-class"; import i18next from "i18next"; import { modifierSortFunc } from "#app/modifier/modifier"; import { PlayerGender } from "#enums/player-gender"; diff --git a/test/abilities/arena_trap.test.ts b/test/abilities/arena_trap.test.ts index e0d093a91aa..3a5bad9c34b 100644 --- a/test/abilities/arena_trap.test.ts +++ b/test/abilities/arena_trap.test.ts @@ -1,4 +1,4 @@ -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/abilities/flower_gift.test.ts b/test/abilities/flower_gift.test.ts index 5da796539e5..8c7b32e7e33 100644 --- a/test/abilities/flower_gift.test.ts +++ b/test/abilities/flower_gift.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; import { Abilities } from "#app/enums/abilities"; import { Stat } from "#app/enums/stat"; import { WeatherType } from "#app/enums/weather-type"; diff --git a/test/abilities/flower_veil.test.ts b/test/abilities/flower_veil.test.ts index c26a952acff..68242be3886 100644 --- a/test/abilities/flower_veil.test.ts +++ b/test/abilities/flower_veil.test.ts @@ -9,7 +9,7 @@ import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { allMoves } from "#app/data/moves/move"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; describe("Abilities - Flower Veil", () => { let phaserGame: Phaser.Game; diff --git a/test/abilities/forecast.test.ts b/test/abilities/forecast.test.ts index a25af32537d..675b9a8b59c 100644 --- a/test/abilities/forecast.test.ts +++ b/test/abilities/forecast.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; import { Abilities } from "#app/enums/abilities"; import { WeatherType } from "#app/enums/weather-type"; import { DamageAnimPhase } from "#app/phases/damage-anim-phase"; diff --git a/test/abilities/friend_guard.test.ts b/test/abilities/friend_guard.test.ts index 30175fe37e0..474c89adaf1 100644 --- a/test/abilities/friend_guard.test.ts +++ b/test/abilities/friend_guard.test.ts @@ -5,7 +5,7 @@ import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { BattlerIndex } from "#app/battle"; -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; import { allMoves } from "#app/data/moves/move"; import { MoveCategory } from "#enums/MoveCategory"; diff --git a/test/abilities/good_as_gold.test.ts b/test/abilities/good_as_gold.test.ts index 7cc543c4a0d..4c4741a331f 100644 --- a/test/abilities/good_as_gold.test.ts +++ b/test/abilities/good_as_gold.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; import { ArenaTagSide } from "#app/data/arena-tag"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import { BattlerTagType } from "#app/enums/battler-tag-type"; diff --git a/test/abilities/magic_bounce.test.ts b/test/abilities/magic_bounce.test.ts index f9a076776aa..7886ac5fd5c 100644 --- a/test/abilities/magic_bounce.test.ts +++ b/test/abilities/magic_bounce.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; import { ArenaTagSide } from "#app/data/arena-tag"; import { allMoves } from "#app/data/moves/move"; import { ArenaTagType } from "#app/enums/arena-tag-type"; diff --git a/test/abilities/neutralizing_gas.test.ts b/test/abilities/neutralizing_gas.test.ts index a10a246d855..56a663db403 100644 --- a/test/abilities/neutralizing_gas.test.ts +++ b/test/abilities/neutralizing_gas.test.ts @@ -1,7 +1,7 @@ import { BattlerIndex } from "#app/battle"; import type { CommandPhase } from "#app/phases/command-phase"; import { Command } from "#app/ui/command-ui-handler"; -import { PostSummonWeatherChangeAbAttr } from "#app/data/ability"; +import { PostSummonWeatherChangeAbAttr } from "#app/data/abilities/ability"; import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; import { Moves } from "#enums/moves"; diff --git a/test/abilities/quick_draw.test.ts b/test/abilities/quick_draw.test.ts index 9969dc2aa75..1277fd5d3cb 100644 --- a/test/abilities/quick_draw.test.ts +++ b/test/abilities/quick_draw.test.ts @@ -1,4 +1,5 @@ -import { allAbilities, BypassSpeedChanceAbAttr } from "#app/data/ability"; +import { BypassSpeedChanceAbAttr } from "#app/data/abilities/ability"; +import { allAbilities } from "#app/data/data-lists"; import { FaintPhase } from "#app/phases/faint-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; diff --git a/test/abilities/sand_veil.test.ts b/test/abilities/sand_veil.test.ts index 5e0a3f567dd..c7b12a11c0e 100644 --- a/test/abilities/sand_veil.test.ts +++ b/test/abilities/sand_veil.test.ts @@ -1,4 +1,5 @@ -import { StatMultiplierAbAttr, allAbilities } from "#app/data/ability"; +import { StatMultiplierAbAttr } from "#app/data/abilities/ability"; +import { allAbilities } from "#app/data/data-lists"; import { CommandPhase } from "#app/phases/command-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; diff --git a/test/abilities/shield_dust.test.ts b/test/abilities/shield_dust.test.ts index 8e02b5a7713..257ebe885df 100644 --- a/test/abilities/shield_dust.test.ts +++ b/test/abilities/shield_dust.test.ts @@ -4,7 +4,7 @@ import { applyPreDefendAbAttrs, IgnoreMoveEffectsAbAttr, MoveEffectChanceMultiplierAbAttr, -} from "#app/data/ability"; +} from "#app/data/abilities/ability"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { NumberHolder } from "#app/utils"; import { Abilities } from "#enums/abilities"; diff --git a/test/abilities/steely_spirit.test.ts b/test/abilities/steely_spirit.test.ts index b180ff8919e..eb5e7aac601 100644 --- a/test/abilities/steely_spirit.test.ts +++ b/test/abilities/steely_spirit.test.ts @@ -1,4 +1,4 @@ -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; import { allMoves } from "#app/data/moves/move"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#enums/moves"; diff --git a/test/abilities/unburden.test.ts b/test/abilities/unburden.test.ts index 8f18604011c..769e078faf8 100644 --- a/test/abilities/unburden.test.ts +++ b/test/abilities/unburden.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { PostItemLostAbAttr } from "#app/data/ability"; +import { PostItemLostAbAttr } from "#app/data/abilities/ability"; import { allMoves, StealHeldItemChanceAttr } from "#app/data/moves/move"; import type Pokemon from "#app/field/pokemon"; import type { ContactHeldItemTransferChanceModifier } from "#app/modifier/modifier"; diff --git a/test/battle/ability_swap.test.ts b/test/battle/ability_swap.test.ts index 72991dba6b0..215321f26c2 100644 --- a/test/battle/ability_swap.test.ts +++ b/test/battle/ability_swap.test.ts @@ -1,4 +1,4 @@ -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; import { Stat } from "#app/enums/stat"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; diff --git a/test/moves/flame_burst.test.ts b/test/moves/flame_burst.test.ts index b6a425e7bb5..a39c27d37b3 100644 --- a/test/moves/flame_burst.test.ts +++ b/test/moves/flame_burst.test.ts @@ -1,4 +1,4 @@ -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; import { Abilities } from "#app/enums/abilities"; import type Pokemon from "#app/field/pokemon"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; diff --git a/test/moves/pledge_moves.test.ts b/test/moves/pledge_moves.test.ts index c866d15357c..ee9e0b8b154 100644 --- a/test/moves/pledge_moves.test.ts +++ b/test/moves/pledge_moves.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { allAbilities } from "#app/data/ability"; +import { allAbilities } from "#app/data/data-lists"; import { ArenaTagSide } from "#app/data/arena-tag"; import { allMoves, FlinchAttr } from "#app/data/moves/move"; import { PokemonType } from "#enums/pokemon-type"; diff --git a/test/moves/safeguard.test.ts b/test/moves/safeguard.test.ts index 2235b59e1af..675c74f28d0 100644 --- a/test/moves/safeguard.test.ts +++ b/test/moves/safeguard.test.ts @@ -1,5 +1,6 @@ import { BattlerIndex } from "#app/battle"; -import { allAbilities, PostDefendContactApplyStatusEffectAbAttr } from "#app/data/ability"; +import { PostDefendContactApplyStatusEffectAbAttr } from "#app/data/abilities/ability"; +import { allAbilities } from "#app/data/data-lists"; import { Abilities } from "#app/enums/abilities"; import { StatusEffect } from "#app/enums/status-effect"; import GameManager from "#test/testUtils/gameManager"; diff --git a/test/moves/secret_power.test.ts b/test/moves/secret_power.test.ts index 37f1664251b..d769b112b70 100644 --- a/test/moves/secret_power.test.ts +++ b/test/moves/secret_power.test.ts @@ -11,7 +11,8 @@ import { StatusEffect } from "#enums/status-effect"; import { BattlerIndex } from "#app/battle"; import { ArenaTagType } from "#enums/arena-tag-type"; import { ArenaTagSide } from "#app/data/arena-tag"; -import { allAbilities, MoveEffectChanceMultiplierAbAttr } from "#app/data/ability"; +import { MoveEffectChanceMultiplierAbAttr } from "#app/data/abilities/ability"; +import { allAbilities } from "#app/data/data-lists"; describe("Moves - Secret Power", () => { let phaserGame: Phaser.Game; diff --git a/test/testUtils/testFileInitialization.ts b/test/testUtils/testFileInitialization.ts index 2b41f3aa29a..cb2cd57044d 100644 --- a/test/testUtils/testFileInitialization.ts +++ b/test/testUtils/testFileInitialization.ts @@ -1,6 +1,6 @@ import { SESSION_ID_COOKIE_NAME } from "#app/constants"; import { initLoggedInUser } from "#app/account"; -import { initAbilities } from "#app/data/ability"; +import { initAbilities } from "#app/data/abilities/ability"; import { initBiomes } from "#app/data/balance/biomes"; import { initEggMoves } from "#app/data/balance/egg-moves"; import { initPokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; From 18c4dddcf06d51ce9119c1189aef1846ce6c06ee Mon Sep 17 00:00:00 2001 From: Stephen Kelman <64545785+stephenrzkelman@users.noreply.github.com> Date: Tue, 15 Apr 2025 07:19:19 -0700 Subject: [PATCH 24/52] [Bug] Fixing Tera Starstorm for first turn of terastallization (#5658) * Updating tera starstorm targeting condition so that it is a spread move on the turn that terastallization happens * added new unit tests to verify behavior of tera starstorm under non-tera conditions as well as on terastallization turns --- src/data/moves/move.ts | 2 +- test/moves/tera_starstorm.test.ts | 34 ++++++++++++++++++++++++++++ test/testUtils/helpers/moveHelper.ts | 27 ++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index b68dd0d3e1d..9546a6a40e5 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -11205,7 +11205,7 @@ export function initMoves() { new AttackMove(Moves.TERA_STARSTORM, PokemonType.NORMAL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9) .attr(TeraMoveCategoryAttr) .attr(TeraStarstormTypeAttr) - .attr(VariableTargetAttr, (user, target, move) => user.hasSpecies(Species.TERAPAGOS) && user.isTerastallized ? MoveTarget.ALL_NEAR_ENEMIES : MoveTarget.NEAR_OTHER) + .attr(VariableTargetAttr, (user, target, move) => user.hasSpecies(Species.TERAPAGOS) && (user.isTerastallized || globalScene.currentBattle.preTurnCommands[user.getFieldIndex()]?.command === Command.TERA) ? MoveTarget.ALL_NEAR_ENEMIES : MoveTarget.NEAR_OTHER) .partial(), /** Does not ignore abilities that affect stats, relevant in determining the move's category {@see TeraMoveCategoryAttr} */ new AttackMove(Moves.FICKLE_BEAM, PokemonType.DRAGON, MoveCategory.SPECIAL, 80, 100, 5, 30, 0, 9) .attr(PreMoveMessageAttr, doublePowerChanceMessageFunc) diff --git a/test/moves/tera_starstorm.test.ts b/test/moves/tera_starstorm.test.ts index 19fe58f4057..9f97b2a51aa 100644 --- a/test/moves/tera_starstorm.test.ts +++ b/test/moves/tera_starstorm.test.ts @@ -69,6 +69,40 @@ describe("Moves - Tera Starstorm", () => { expect(enemyField.every(pokemon => pokemon.isFullHp())).toBe(false); }); + it("targets both opponents in a double battle when used by Terapagos immediately after terastallizing", async () => { + await game.classicMode.startBattle([Species.TERAPAGOS]); + + const terapagos = game.scene.getPlayerParty()[0]; + terapagos.isTerastallized = false; + + game.move.selectWithTera(Moves.TERA_STARSTORM, 0); + + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); + + const enemyField = game.scene.getEnemyField(); + + // Terapagos in Stellar Form should hit both targets + await game.phaseInterceptor.to("MoveEndPhase"); + expect(enemyField.some(pokemon => pokemon.isFullHp())).toBe(false); + }); + + it("targets only one opponent in a double battle when used by Terapagos without terastallizing", async () => { + await game.classicMode.startBattle([Species.TERAPAGOS]); + + const terapagos = game.scene.getPlayerParty()[0]; + terapagos.isTerastallized = false; + + game.move.select(Moves.TERA_STARSTORM, 0, BattlerIndex.ENEMY); + + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); + + const enemyField = game.scene.getEnemyField(); + + // Terapagos in Stellar Form should hit both targets + await game.phaseInterceptor.to("MoveEndPhase"); + expect(enemyField.some(pokemon => pokemon.isFullHp())).toBe(true); + }); + it("applies the effects when Terapagos in Stellar Form is fused with another Pokemon", async () => { await game.classicMode.startBattle([Species.TERAPAGOS, Species.CHARMANDER, Species.MAGIKARP]); diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index 543f46b2026..a54028ebca0 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -65,6 +65,33 @@ export class MoveHelper extends GameManagerHelper { } } + /** + * Select the move to be used by the given Pokemon(-index), **which will also terastallize on this turn**. + * Triggers during the next {@linkcode CommandPhase} + * @param move - the move to use + * @param pkmIndex - the pokemon index. Relevant for double-battles only (defaults to 0) + * @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves, or `null` if a manual call to `selectTarget()` is required + */ + public selectWithTera(move: Moves, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null) { + const movePosition = getMovePosition(this.game.scene, pkmIndex, move); + this.game.scene.getPlayerParty()[pkmIndex].isTerastallized = false; + + this.game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + this.game.scene.ui.setMode( + Mode.FIGHT, + (this.game.scene.getCurrentPhase() as CommandPhase).getFieldIndex(), + Command.TERA, + ); + }); + this.game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { + (this.game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.TERA, movePosition, false); + }); + + if (targetIndex !== null) { + this.game.selectTarget(movePosition, targetIndex); + } + } + /** * Forces the Paralysis or Freeze status to activate on the next move by temporarily mocking {@linkcode Overrides.STATUS_ACTIVATION_OVERRIDE}, * advancing to the next `MovePhase`, and then resetting the override to `null` From efad0d1324368ca31cb0c799f97dc475bf375330 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Tue, 15 Apr 2025 20:55:29 -0700 Subject: [PATCH 25/52] [GitHub] Use `.nvmrc` for pages workflow (#5666) --- .github/workflows/github-pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index b7d5fb95c1e..ce7c17e2db9 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -37,7 +37,7 @@ jobs: - name: Setup Node 22.14.1 uses: actions/setup-node@v4 with: - node-version: 22 + node-version-file: "pokerogue_docs/.nvmrc" - name: Checkout repository for Github Pages if: github.event_name == 'push' From ae588ebff907f17e38d2850ddc5bd07d8bce656a Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Wed, 16 Apr 2025 16:05:40 -0500 Subject: [PATCH 26/52] [Bug][Move] Struggle no longer gets STAB (#5643) * Struggle no longer gets STAB * Apply kev's suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/field/pokemon.ts | 133 ++++++++++++++++++++---------------- test/moves/struggle.test.ts | 65 ++++++++++++++++++ 2 files changed, 139 insertions(+), 59 deletions(-) create mode 100644 test/moves/struggle.test.ts diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 22ede4260c3..cdd48f7d940 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -4151,6 +4151,62 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return baseDamage; } + + /** Determine the STAB multiplier for a move used against this pokemon. + * + * @param source - The attacking {@linkcode Pokemon} + * @param move - The {@linkcode Move} used in the attack + * @param ignoreSourceAbility - If `true`, ignores the attacking Pokemon's ability effects + * @param simulated - If `true`, suppresses changes to game state during the calculation + * + * @returns The STAB multiplier for the move used against this Pokemon + */ + calculateStabMultiplier(source: Pokemon, move: Move, ignoreSourceAbility: boolean, simulated: boolean): number { + // If the move has the Typeless attribute, it doesn't get STAB (e.g. struggle) + if (move.hasAttr(TypelessAttr)) { + return 1; + } + const sourceTypes = source.getTypes(); + const sourceTeraType = source.getTeraType(); + const moveType = source.getMoveType(move); + const matchesSourceType = sourceTypes.includes(source.getMoveType(move)); + const stabMultiplier = new NumberHolder(1); + if (matchesSourceType && moveType !== PokemonType.STELLAR) { + stabMultiplier.value += 0.5; + } + + applyMoveAttrs( + CombinedPledgeStabBoostAttr, + source, + this, + move, + stabMultiplier, + ); + + if (!ignoreSourceAbility) { + applyAbAttrs(StabBoostAbAttr, source, null, simulated, stabMultiplier); + } + + if ( + source.isTerastallized && + sourceTeraType === moveType && + moveType !== PokemonType.STELLAR + ) { + stabMultiplier.value += 0.5; + } + + if ( + source.isTerastallized && + source.getTeraType() === PokemonType.STELLAR && + (!source.stellarTypesBoosted.includes(moveType) || + source.hasSpecies(Species.TERAPAGOS)) + ) { + stabMultiplier.value += matchesSourceType ? 0.5 : 0.2; + } + + return Math.min(stabMultiplier.value, 2.25); + } + /** * Calculates the damage of an attack made by another Pokemon against this Pokemon * @param source {@linkcode Pokemon} the attacking Pokemon @@ -4333,70 +4389,29 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ? 1 : this.randSeedIntRange(85, 100) / 100; - const sourceTypes = source.getTypes(); - const sourceTeraType = source.getTeraType(); - const matchesSourceType = sourceTypes.includes(moveType); + /** A damage multiplier for when the attack is of the attacker's type and/or Tera type. */ - const stabMultiplier = new NumberHolder(1); - if (matchesSourceType && moveType !== PokemonType.STELLAR) { - stabMultiplier.value += 0.5; - } - - if (!ignoreSourceAbility) { - applyAbAttrs(StabBoostAbAttr, source, null, simulated, stabMultiplier); - } - - applyMoveAttrs( - CombinedPledgeStabBoostAttr, - source, - this, - move, - stabMultiplier, - ); - - if ( - source.isTerastallized && - sourceTeraType === moveType && - moveType !== PokemonType.STELLAR - ) { - stabMultiplier.value += 0.5; - } - - if ( - source.isTerastallized && - source.getTeraType() === PokemonType.STELLAR && - (!source.stellarTypesBoosted.includes(moveType) || - source.hasSpecies(Species.TERAPAGOS)) - ) { - if (matchesSourceType) { - stabMultiplier.value += 0.5; - } else { - stabMultiplier.value += 0.2; - } - } - - stabMultiplier.value = Math.min(stabMultiplier.value, 2.25); + const stabMultiplier = this.calculateStabMultiplier(source, move, ignoreSourceAbility, simulated); /** Halves damage if the attacker is using a physical attack while burned */ - const burnMultiplier = new NumberHolder(1); + let burnMultiplier = 1; if ( isPhysical && source.status && - source.status.effect === StatusEffect.BURN + source.status.effect === StatusEffect.BURN && + !move.hasAttr(BypassBurnDamageReductionAttr) ) { - if (!move.hasAttr(BypassBurnDamageReductionAttr)) { - const burnDamageReductionCancelled = new BooleanHolder(false); - if (!ignoreSourceAbility) { - applyAbAttrs( - BypassBurnDamageReductionAbAttr, - source, - burnDamageReductionCancelled, - simulated, - ); - } - if (!burnDamageReductionCancelled.value) { - burnMultiplier.value = 0.5; - } + const burnDamageReductionCancelled = new BooleanHolder(false); + if (!ignoreSourceAbility) { + applyAbAttrs( + BypassBurnDamageReductionAbAttr, + source, + burnDamageReductionCancelled, + simulated, + ); + } + if (!burnDamageReductionCancelled.value) { + burnMultiplier = 0.5; } } @@ -4447,9 +4462,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { glaiveRushMultiplier.value * criticalMultiplier.value * randomMultiplier * - stabMultiplier.value * + stabMultiplier * typeMultiplier * - burnMultiplier.value * + burnMultiplier * screenMultiplier.value * hitsTagMultiplier.value * mistyTerrainMultiplier, diff --git a/test/moves/struggle.test.ts b/test/moves/struggle.test.ts new file mode 100644 index 00000000000..6b566df9d54 --- /dev/null +++ b/test/moves/struggle.test.ts @@ -0,0 +1,65 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Moves - Struggle", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([Moves.SPLASH]) + .ability(Abilities.BALL_FETCH) + .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should not have its power boosted by adaptability or stab", async () => { + game.override.moveset([Moves.STRUGGLE]).ability(Abilities.ADAPTABILITY); + await game.classicMode.startBattle([Species.RATTATA]); + + const enemy = game.scene.getEnemyPokemon()!; + game.move.select(Moves.STRUGGLE); + + const stabSpy = vi.spyOn(enemy, "calculateStabMultiplier"); + + await game.phaseInterceptor.to("BerryPhase"); + + expect(stabSpy).toHaveReturnedWith(1); + + stabSpy.mockRestore(); + }); + + it("should ignore type effectiveness", async () => { + game.override.moveset([Moves.STRUGGLE]); + await game.classicMode.startBattle([Species.GASTLY]); + + const enemy = game.scene.getEnemyPokemon()!; + game.move.select(Moves.STRUGGLE); + + const moveEffectivenessSpy = vi.spyOn(enemy, "getMoveEffectiveness"); + + await game.phaseInterceptor.to("BerryPhase"); + + expect(moveEffectivenessSpy).toHaveReturnedWith(1); + + moveEffectivenessSpy.mockRestore(); + }); +}); From 8d311e65cf5dc65d807827a874f140c0f7b33ce4 Mon Sep 17 00:00:00 2001 From: damocleas Date: Wed, 16 Apr 2025 22:31:53 -0400 Subject: [PATCH 27/52] [Bug] [Ability] Fixed wrong Sheer Force interactions and multiplier from ~1.33 -> 1.3 (#5515) * sheer force #, sheer force and burning jealousy test fix, and move chance fixes * removed order up sheer force interaction mention and test - updated comments * remove electro shot from changes --- src/data/abilities/ability.ts | 4 ++-- src/data/moves/move.ts | 24 ++++++++++++------------ test/abilities/sheer_force.test.ts | 2 +- test/moves/burning_jealousy.test.ts | 2 +- test/moves/order_up.test.ts | 19 ------------------- 5 files changed, 16 insertions(+), 35 deletions(-) diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 17a8eddf47f..43a6cd5901b 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -6702,8 +6702,8 @@ export function initAbilities() { .attr(PostDefendStealHeldItemAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT)) .condition(getSheerForceHitDisableAbCondition()), new Ability(Abilities.SHEER_FORCE, 5) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.chance >= 1, 5461 / 4096) - .attr(MoveEffectChanceMultiplierAbAttr, 0), // Should disable life orb, eject button, red card, kee/maranga berry if they get implemented + .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(Abilities.CONTRARY, 5) .attr(StatStageChangeMultiplierAbAttr, -1) .ignorable(), diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 9546a6a40e5..c2dd0ec31ca 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -3466,8 +3466,7 @@ export class CutHpStatStageBoostAttr extends StatStageChangeAttr { /** * Attribute implementing the stat boosting effect of {@link https://bulbapedia.bulbagarden.net/wiki/Order_Up_(move) | Order Up}. * If the user has a Pokemon with {@link https://bulbapedia.bulbagarden.net/wiki/Commander_(Ability) | Commander} in their mouth, - * one of the user's stats are increased by 1 stage, depending on the "commanding" Pokemon's form. This effect does not respect - * effect chance, but Order Up itself may be boosted by Sheer Force. + * one of the user's stats are increased by 1 stage, depending on the "commanding" Pokemon's form. */ export class OrderUpStatBoostAttr extends MoveEffectAttr { constructor() { @@ -9726,7 +9725,7 @@ export function initMoves() { .ignoresProtect() .target(MoveTarget.BOTH_SIDES) .unimplemented(), - new AttackMove(Moves.SMACK_DOWN, PokemonType.ROCK, MoveCategory.PHYSICAL, 50, 100, 15, 100, 0, 5) + new AttackMove(Moves.SMACK_DOWN, PokemonType.ROCK, MoveCategory.PHYSICAL, 50, 100, 15, -1, 0, 5) .attr(FallDownAttr) .attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED) .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.FLOATING, BattlerTagType.TELEKINESIS ]) @@ -9893,7 +9892,7 @@ export function initMoves() { .attr(MovePowerMultiplierAttr, (user, target, move) => globalScene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1) .makesContact(false) .target(MoveTarget.ALL_NEAR_OTHERS), - new AttackMove(Moves.FROST_BREATH, PokemonType.ICE, MoveCategory.SPECIAL, 60, 90, 10, 100, 0, 5) + new AttackMove(Moves.FROST_BREATH, PokemonType.ICE, MoveCategory.SPECIAL, 60, 90, 10, -1, 0, 5) .attr(CritOnlyAttr), new AttackMove(Moves.DRAGON_TAIL, PokemonType.DRAGON, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5) .attr(ForceSwitchOutAttr, false, SwitchType.FORCE_SWITCH) @@ -10535,7 +10534,7 @@ export function initMoves() { .attr(AddArenaTagAttr, ArenaTagType.LIGHT_SCREEN, 5, false, true), new AttackMove(Moves.BADDY_BAD, PokemonType.DARK, MoveCategory.SPECIAL, 80, 95, 15, -1, 0, 7) .attr(AddArenaTagAttr, ArenaTagType.REFLECT, 5, false, true), - new AttackMove(Moves.SAPPY_SEED, PokemonType.GRASS, MoveCategory.PHYSICAL, 100, 90, 10, 100, 0, 7) + new AttackMove(Moves.SAPPY_SEED, PokemonType.GRASS, MoveCategory.PHYSICAL, 100, 90, 10, -1, 0, 7) .attr(LeechSeedAttr) .makesContact(false), new AttackMove(Moves.FREEZY_FROST, PokemonType.ICE, MoveCategory.SPECIAL, 100, 90, 10, -1, 0, 7) @@ -10863,7 +10862,7 @@ export function initMoves() { .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true), new AttackMove(Moves.BITTER_MALICE, PokemonType.GHOST, MoveCategory.SPECIAL, 75, 100, 10, 100, 0, 8) .attr(StatStageChangeAttr, [ Stat.ATK ], -1), - new SelfStatusMove(Moves.SHELTER, PokemonType.STEEL, -1, 10, 100, 0, 8) + new SelfStatusMove(Moves.SHELTER, PokemonType.STEEL, -1, 10, -1, 0, 8) .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true), new AttackMove(Moves.TRIPLE_ARROWS, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 10, 30, 0, 8) .makesContact(false) @@ -11018,7 +11017,7 @@ export function initMoves() { .makesContact(false), new AttackMove(Moves.LUMINA_CRASH, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9) .attr(StatStageChangeAttr, [ Stat.SPDEF ], -2), - new AttackMove(Moves.ORDER_UP, PokemonType.DRAGON, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 9) + new AttackMove(Moves.ORDER_UP, PokemonType.DRAGON, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 9) .attr(OrderUpStatBoostAttr) .makesContact(false), new AttackMove(Moves.JET_PUNCH, PokemonType.WATER, MoveCategory.PHYSICAL, 60, 100, 15, -1, 1, 9) @@ -11072,7 +11071,7 @@ export function initMoves() { .attr(CutHpStatStageBoostAttr, [ Stat.ATK, Stat.SPATK, Stat.SPD ], 2, 2), new AttackMove(Moves.KOWTOW_CLEAVE, PokemonType.DARK, MoveCategory.PHYSICAL, 85, -1, 10, -1, 0, 9) .slicingMove(), - new AttackMove(Moves.FLOWER_TRICK, PokemonType.GRASS, MoveCategory.PHYSICAL, 70, -1, 10, 100, 0, 9) + new AttackMove(Moves.FLOWER_TRICK, PokemonType.GRASS, MoveCategory.PHYSICAL, 70, -1, 10, -1, 0, 9) .attr(CritOnlyAttr) .makesContact(false), new AttackMove(Moves.TORCH_SONG, PokemonType.FIRE, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9) @@ -11191,7 +11190,7 @@ export function initMoves() { .attr(StatusEffectAttr, StatusEffect.BURN) .target(MoveTarget.ALL_NEAR_ENEMIES) .triageMove(), - new AttackMove(Moves.SYRUP_BOMB, PokemonType.GRASS, MoveCategory.SPECIAL, 60, 85, 10, -1, 0, 9) + new AttackMove(Moves.SYRUP_BOMB, PokemonType.GRASS, MoveCategory.SPECIAL, 60, 85, 10, 100, 0, 9) .attr(AddBattlerTagAttr, BattlerTagType.SYRUP_BOMB, false, false, 3) .ballBombMove(), new AttackMove(Moves.IVY_CUDGEL, PokemonType.GRASS, MoveCategory.PHYSICAL, 100, 100, 10, -1, 0, 9) @@ -11209,7 +11208,8 @@ export function initMoves() { .partial(), /** Does not ignore abilities that affect stats, relevant in determining the move's category {@see TeraMoveCategoryAttr} */ new AttackMove(Moves.FICKLE_BEAM, PokemonType.DRAGON, MoveCategory.SPECIAL, 80, 100, 5, 30, 0, 9) .attr(PreMoveMessageAttr, doublePowerChanceMessageFunc) - .attr(DoublePowerChanceAttr), + .attr(DoublePowerChanceAttr) + .edgeCase(), // Should not interact with Sheer Force new SelfStatusMove(Moves.BURNING_BULWARK, PokemonType.FIRE, -1, 10, -1, 4, 9) .attr(ProtectAttr, BattlerTagType.BURNING_BULWARK) .condition(failIfLastCondition), @@ -11232,7 +11232,7 @@ export function initMoves() { new StatusMove(Moves.DRAGON_CHEER, PokemonType.DRAGON, -1, 15, -1, 0, 9) .attr(AddBattlerTagAttr, BattlerTagType.DRAGON_CHEER, false, true) .target(MoveTarget.NEAR_ALLY), - new AttackMove(Moves.ALLURING_VOICE, PokemonType.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9) + new AttackMove(Moves.ALLURING_VOICE, PokemonType.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9) .attr(AddBattlerTagIfBoostedAttr, BattlerTagType.CONFUSED) .soundBased(), new AttackMove(Moves.TEMPER_FLARE, PokemonType.FIRE, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 9) @@ -11241,7 +11241,7 @@ export function initMoves() { .attr(MissEffectAttr, crashDamageFunc) .attr(NoEffectAttr, crashDamageFunc) .recklessMove(), - new AttackMove(Moves.PSYCHIC_NOISE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 75, 100, 10, -1, 0, 9) + new AttackMove(Moves.PSYCHIC_NOISE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 75, 100, 10, 100, 0, 9) .soundBased() .attr(AddBattlerTagAttr, BattlerTagType.HEAL_BLOCK, false, false, 2), new AttackMove(Moves.UPPER_HAND, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, 100, 3, 9) diff --git a/test/abilities/sheer_force.test.ts b/test/abilities/sheer_force.test.ts index 4a1c20cde5c..fae089958a5 100644 --- a/test/abilities/sheer_force.test.ts +++ b/test/abilities/sheer_force.test.ts @@ -34,7 +34,7 @@ describe("Abilities - Sheer Force", () => { .disableCrits(); }); - const SHEER_FORCE_MULT = 5461 / 4096; + const SHEER_FORCE_MULT = 1.3; it("Sheer Force should boost the power of the move but disable secondary effects", async () => { game.override.moveset([Moves.AIR_SLASH]); diff --git a/test/moves/burning_jealousy.test.ts b/test/moves/burning_jealousy.test.ts index 60387df4226..04966b24206 100644 --- a/test/moves/burning_jealousy.test.ts +++ b/test/moves/burning_jealousy.test.ts @@ -89,7 +89,7 @@ describe("Moves - Burning Jealousy", () => { await game.phaseInterceptor.to("BerryPhase"); expect(allMoves[Moves.BURNING_JEALOUSY].calculateBattlePower).toHaveReturnedWith( - (allMoves[Moves.BURNING_JEALOUSY].power * 5461) / 4096, + allMoves[Moves.BURNING_JEALOUSY].power * 1.3, ); }); }); diff --git a/test/moves/order_up.test.ts b/test/moves/order_up.test.ts index 516f7f625a3..f25114c12de 100644 --- a/test/moves/order_up.test.ts +++ b/test/moves/order_up.test.ts @@ -65,23 +65,4 @@ describe("Moves - Order Up", () => { affectedStats.forEach(st => expect(dondozo.getStatStage(st)).toBe(st === stat ? 3 : 2)); }, ); - - it("should be boosted by Sheer Force while still applying a stat boost", async () => { - game.override.passiveAbility(Abilities.SHEER_FORCE).starterForms({ [Species.TATSUGIRI]: 0 }); - - await game.classicMode.startBattle([Species.TATSUGIRI, Species.DONDOZO]); - - const [tatsugiri, dondozo] = game.scene.getPlayerField(); - - expect(game.scene.triggerPokemonBattleAnim).toHaveBeenLastCalledWith(tatsugiri, PokemonAnimType.COMMANDER_APPLY); - expect(dondozo.getTag(BattlerTagType.COMMANDED)).toBeDefined(); - - game.move.select(Moves.ORDER_UP, 1, BattlerIndex.ENEMY); - expect(game.scene.currentBattle.turnCommands[0]?.skip).toBeTruthy(); - - await game.phaseInterceptor.to("BerryPhase", false); - - expect(dondozo.battleData.abilitiesApplied.includes(Abilities.SHEER_FORCE)).toBeTruthy(); - expect(dondozo.getStatStage(Stat.ATK)).toBe(3); - }); }); From b2bab46e1cd7b12363c9220835dcfc9b5f839b98 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Wed, 16 Apr 2025 23:47:49 -0500 Subject: [PATCH 28/52] [Bug][Ability] Fix healer queueing its message when its ally is fainted (#5642) * Add check against faint status effect * Add tests for healer * Remove redundant portions of the tests * Fix broken test --- src/data/abilities/ability.ts | 4 +- test/abilities/healer.test.ts | 97 +++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 test/abilities/healer.test.ts diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 43a6cd5901b..ab07d406868 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -4033,7 +4033,9 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr { } else { this.target = pokemon; } - return !isNullOrUndefined(this.target?.status); + + const effect = this.target?.status?.effect; + return !!effect && effect !== StatusEffect.FAINT; } override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void { diff --git a/test/abilities/healer.test.ts b/test/abilities/healer.test.ts new file mode 100644 index 00000000000..35aa74209b4 --- /dev/null +++ b/test/abilities/healer.test.ts @@ -0,0 +1,97 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { StatusEffect } from "#enums/status-effect"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi, type MockInstance } from "vitest"; +import { isNullOrUndefined } from "#app/utils"; +import { PostTurnResetStatusAbAttr } from "#app/data/abilities/ability"; +import { allAbilities } from "#app/data/data-lists"; +import type Pokemon from "#app/field/pokemon"; + +describe("Abilities - Healer", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + let healerAttrSpy: MockInstance; + let healerAttr: PostTurnResetStatusAbAttr; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + healerAttrSpy.mockRestore(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([Moves.SPLASH]) + .ability(Abilities.BALL_FETCH) + .battleType("double") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + + healerAttr = allAbilities[Abilities.HEALER].getAttrs(PostTurnResetStatusAbAttr)[0]; + healerAttrSpy = vi + .spyOn(healerAttr, "getCondition") + .mockReturnValue((pokemon: Pokemon) => !isNullOrUndefined(pokemon.getAlly())); + }); + + it("should not queue a message phase for healing if the ally has fainted", async () => { + game.override.moveset([Moves.SPLASH, Moves.LUNAR_DANCE]); + await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP]); + const user = game.scene.getPlayerPokemon()!; + // Only want one magikarp to have the ability. + vi.spyOn(user, "getAbility").mockReturnValue(allAbilities[Abilities.HEALER]); + game.move.select(Moves.SPLASH); + // faint the ally + game.move.select(Moves.LUNAR_DANCE, 1); + const abSpy = vi.spyOn(healerAttr, "canApplyPostTurn"); + await game.phaseInterceptor.to("TurnEndPhase"); + + // It's not enough to just test that the ally still has its status. + // We need to ensure that the ability failed to meet its condition + expect(abSpy).toHaveReturnedWith(false); + + // Explicitly restore the mock to ensure pollution doesn't happen + abSpy.mockRestore(); + }); + + it("should heal the status of an ally if the ally has a status", async () => { + await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP]); + const [user, ally] = game.scene.getPlayerField(); + // Only want one magikarp to have the ability. + vi.spyOn(user, "getAbility").mockReturnValue(allAbilities[Abilities.HEALER]); + expect(ally.trySetStatus(StatusEffect.BURN)).toBe(true); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SPLASH, 1); + + await game.phaseInterceptor.to("TurnEndPhase"); + await game.toNextTurn(); + + expect(ally.status?.effect, "status effect was not healed").toBeFalsy(); + }); + + // TODO: Healer is currently checked before the + it.todo("should heal a burn before its end of turn damage", async () => { + await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP]); + const [user, ally] = game.scene.getPlayerField(); + // Only want one magikarp to have the ability. + vi.spyOn(user, "getAbility").mockReturnValue(allAbilities[Abilities.HEALER]); + expect(ally.trySetStatus(StatusEffect.BURN)).toBe(true); + game.move.select(Moves.SPLASH); + game.move.select(Moves.SPLASH, 1); + await game.phaseInterceptor.to("TurnEndPhase"); + await game.toNextTurn(); + + expect(ally.status?.effect, "status effect was not healed").toBeFalsy(); + expect(ally.hp).toBe(ally.getMaxHp()); + }); +}); From 45a2f426024e8221f4756f524f6bda93b5cc6a5f Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Thu, 17 Apr 2025 10:44:50 -0500 Subject: [PATCH 29/52] [Bug] Prevent game from hanging when loading in a new battle (#5676) --- src/field/pokemon.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index cdd48f7d940..ce36a40697b 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -840,12 +840,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { await Promise.allSettled(loadPromises); - // Wait for the assets we queued to load to finish loading, then... + // This must be initiated before we queue loading, otherwise the load could have finished before + // we reach the line of code that adds the listener, causing a deadlock. + const waitOnLoadPromise = new Promise(resolve => globalScene.load.once(Phaser.Loader.Events.COMPLETE, resolve)); + if (!globalScene.load.isLoading()) { globalScene.load.start(); } + + // Wait for the assets we queued to load to finish loading, then... // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#creating_a_promise_around_an_old_callback_api - await new Promise(resolve => globalScene.load.once(Phaser.Loader.Events.COMPLETE, resolve)); + await waitOnLoadPromise; // With the sprites loaded, generate the animation frame information if (this.isPlayer()) { From eef8367caf028e84213924fa0673a9e58927991f Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Thu, 17 Apr 2025 11:57:30 -0500 Subject: [PATCH 30/52] [Bug] Fix experimental sprites not loading in starter select (#5664) [Bug][Sprite] Fix experimental variant sprites not being loaded in starter select screen --- src/data/pokemon-species.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index a27c00121dc..75ea07edd40 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -27,11 +27,12 @@ import { } from "#app/data/balance/pokemon-level-moves"; import type { Stat } from "#enums/stat"; import type { Variant, VariantSet } from "#app/sprites/variant"; -import { variantData } from "#app/sprites/variant"; +import { populateVariantColorCache, variantData } from "#app/sprites/variant"; import { speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters"; import { SpeciesFormKey } from "#enums/species-form-key"; import { starterPassiveAbilities } from "#app/data/balance/passives"; import { loadPokemonVariantAssets } from "#app/sprites/pokemon-sprite"; +import { hasExpSprite } from "#app/sprites/sprite-utils"; export enum Region { NORMAL, @@ -388,8 +389,7 @@ export abstract class PokemonSpeciesForm { return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; } - /** Compute the sprite ID of the pokemon form. */ - getSpriteId(female: boolean, formIndex?: number, shiny?: boolean, variant = 0, back?: boolean): string { + getBaseSpriteKey(female: boolean, formIndex?: number): string { if (formIndex === undefined || this instanceof PokemonForm) { formIndex = this.formIndex; } @@ -400,7 +400,12 @@ export abstract class PokemonSpeciesForm { female && ![SpeciesFormKey.MEGA, SpeciesFormKey.GIGANTAMAX].includes(formSpriteKey as SpeciesFormKey); - const baseSpriteKey = `${showGenderDiffs ? "female__" : ""}${this.speciesId}${formSpriteKey ? `-${formSpriteKey}` : ""}`; + return `${showGenderDiffs ? "female__" : ""}${this.speciesId}${formSpriteKey ? `-${formSpriteKey}` : ""}`; + } + + /** Compute the sprite ID of the pokemon form. */ + getSpriteId(female: boolean, formIndex?: number, shiny?: boolean, variant = 0, back?: boolean): string { + const baseSpriteKey = this.getBaseSpriteKey(female, formIndex); let config = variantData; `${back ? "back__" : ""}${baseSpriteKey}`.split("__").map(p => (config ? (config = config[p]) : null)); @@ -597,10 +602,19 @@ export abstract class PokemonSpeciesForm { startLoad = false, back = false, ): Promise { + // We need to populate the color cache for this species' variant const spriteKey = this.getSpriteKey(female, formIndex, shiny, variant, back); globalScene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant, back)); globalScene.load.audio(this.getCryKey(formIndex), `audio/${this.getCryKey(formIndex)}.m4a`); + const baseSpriteKey = this.getBaseSpriteKey(female, formIndex); + + // Force the variant color cache to be loaded for the form + await populateVariantColorCache( + "pkmn__" + baseSpriteKey, + globalScene.experimentalSprites && hasExpSprite(spriteKey), + baseSpriteKey, + ); return new Promise(resolve => { globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => { const originalWarn = console.warn; From 3a46aae687142201aa3c42264c13b4865c5d561f Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Thu, 17 Apr 2025 15:25:38 -0500 Subject: [PATCH 31/52] [Bug] Fix beak blast: not applying if user faints and not respecting long reach (#5639) * Add test for beak blast applying after user faints * Rewrite tags for contact protected and check moveFlags.doesFlagEffectApply * Add test to beak blast ensuring a long reach user does not get burned * Re-add DamageProtectedTag to relevant inheritance chains * Move resetSummonData to faintPhase instead of pokemon.apply * Remove passing of grudge and destiny bond tags to faint phase --- src/data/abilities/ability.ts | 6 +- src/data/battler-tags.ts | 169 +++++++++++++++++--------------- src/field/pokemon.ts | 8 +- src/phases/faint-phase.ts | 31 ++---- src/phases/move-effect-phase.ts | 10 +- test/moves/beak_blast.test.ts | 31 +++++- 6 files changed, 136 insertions(+), 119 deletions(-) diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index ab07d406868..6cbb579d4e0 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -6701,7 +6701,7 @@ export function initAbilities() { new Ability(Abilities.BAD_DREAMS, 4) .attr(PostTurnHurtIfSleepingAbAttr), new Ability(Abilities.PICKPOCKET, 5) - .attr(PostDefendStealHeldItemAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT)) + .attr(PostDefendStealHeldItemAbAttr, (target, user, move) => move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user, target})) .condition(getSheerForceHitDisableAbCondition()), new Ability(Abilities.SHEER_FORCE, 5) .attr(MovePowerBoostAbAttr, (user, target, move) => move.chance >= 1, 1.3) @@ -7051,7 +7051,7 @@ export function initAbilities() { new Ability(Abilities.BATTERY, 7) .attr(AllyMoveCategoryPowerBoostAbAttr, [ MoveCategory.SPECIAL ], 1.3), new Ability(Abilities.FLUFFY, 7) - .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), 0.5) + .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) .ignorable(), new Ability(Abilities.DAZZLING, 7) @@ -7060,7 +7060,7 @@ export function initAbilities() { new Ability(Abilities.SOUL_HEART, 7) .attr(PostKnockOutStatStageChangeAbAttr, Stat.SPATK, 1), new Ability(Abilities.TANGLING_HAIR, 7) - .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), Stat.SPD, -1, false), + .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user, target}), Stat.SPD, -1, false), new Ability(Abilities.RECEIVER, 7) .attr(CopyFaintedAllyAbilityAbAttr) .uncopiable(), diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 401fd9903d1..9b72f3083fd 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -52,6 +52,7 @@ export enum BattlerTagLapseType { MOVE_EFFECT, TURN_END, HIT, + /** Tag lapses AFTER_HIT, applying its effects even if the user faints */ AFTER_HIT, CUSTOM, } @@ -498,7 +499,13 @@ export class BeakBlastChargingTag extends BattlerTag { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { if (lapseType === BattlerTagLapseType.AFTER_HIT) { const phaseData = getMoveEffectPhaseData(pokemon); - if (phaseData?.move.hasFlag(MoveFlags.MAKES_CONTACT)) { + if ( + phaseData?.move.doesFlagEffectApply({ + flag: MoveFlags.MAKES_CONTACT, + user: phaseData.attacker, + target: pokemon, + }) + ) { phaseData.attacker.trySetStatus(StatusEffect.BURN, true, pokemon); } return true; @@ -1611,19 +1618,50 @@ export class ProtectedTag extends BattlerTag { } } -/** Base class for `BattlerTag`s that block damaging moves but not status moves */ -export class DamageProtectedTag extends ProtectedTag {} +/** Class for `BattlerTag`s that apply some effect when hit by a contact move */ +export class ContactProtectedTag extends ProtectedTag { + /** + * Function to call when a contact move hits the pokemon with this tag. + * @param _attacker - The pokemon using the contact move + * @param _user - The pokemon that is being attacked and has the tag + * @param _move - The move used by the attacker + */ + onContact(_attacker: Pokemon, _user: Pokemon) {} + + /** + * Lapse the tag and apply `onContact` if the move makes contact and + * `lapseType` is custom, respecting the move's flags and the pokemon's + * abilities, and whether the lapseType is custom. + * + * @param pokemon - The pokemon with the tag + * @param lapseType - The type of lapse to apply. If this is not {@linkcode BattlerTagLapseType.CUSTOM CUSTOM}, no effect will be applied. + * @returns Whether the tag continues to exist after the lapse. + */ + lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { + const ret = super.lapse(pokemon, lapseType); + + const moveData = getMoveEffectPhaseData(pokemon); + if ( + lapseType === BattlerTagLapseType.CUSTOM && + moveData && + moveData.move.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user: moveData.attacker, target: pokemon }) + ) { + this.onContact(moveData.attacker, pokemon); + } + + return ret; + } +} /** * `BattlerTag` class for moves that block damaging moves damage the enemy if the enemy's move makes contact * Used by {@linkcode Moves.SPIKY_SHIELD} */ -export class ContactDamageProtectedTag extends ProtectedTag { +export class ContactDamageProtectedTag extends ContactProtectedTag { private damageRatio: number; constructor(sourceMove: Moves, damageRatio: number) { super(sourceMove, BattlerTagType.SPIKY_SHIELD); - this.damageRatio = damageRatio; } @@ -1636,22 +1674,46 @@ export class ContactDamageProtectedTag extends ProtectedTag { this.damageRatio = source.damageRatio; } - lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { - const ret = super.lapse(pokemon, lapseType); - - if (lapseType === BattlerTagLapseType.CUSTOM) { - const effectPhase = globalScene.getCurrentPhase(); - if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) { - const attacker = effectPhase.getPokemon(); - if (!attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { - attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), { - result: HitResult.INDIRECT, - }); - } - } + /** + * Damage the attacker by `this.damageRatio` of the target's max HP + * @param attacker - The pokemon using the contact move + * @param user - The pokemon that is being attacked and has the tag + */ + override onContact(attacker: Pokemon, user: Pokemon): void { + const cancelled = new BooleanHolder(false); + applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled); + if (!cancelled.value) { + attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), { + result: HitResult.INDIRECT, + }); } + } +} - return ret; +/** Base class for `BattlerTag`s that block damaging moves but not status moves */ +export class DamageProtectedTag extends ContactProtectedTag {} + +export class ContactSetStatusProtectedTag extends DamageProtectedTag { + /** + * @param sourceMove The move that caused the tag to be applied + * @param tagType The type of the tag + * @param statusEffect The status effect to apply to the attacker + */ + constructor( + sourceMove: Moves, + tagType: BattlerTagType, + private statusEffect: StatusEffect, + ) { + super(sourceMove, tagType); + } + + /** + * Set the status effect on the attacker + * @param attacker - The pokemon using the contact move + * @param user - The pokemon that is being attacked and has the tag + */ + override onContact(attacker: Pokemon, user: Pokemon): void { + attacker.trySetStatus(this.statusEffect, true, user); } } @@ -1674,68 +1736,19 @@ export class ContactStatStageChangeProtectedTag extends DamageProtectedTag { * When given a battler tag or json representing one, load the data for it. * @param {BattlerTag | any} source A battler tag */ - loadTag(source: BattlerTag | any): void { + override loadTag(source: BattlerTag | any): void { super.loadTag(source); this.stat = source.stat; this.levels = source.levels; } - lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { - const ret = super.lapse(pokemon, lapseType); - - if (lapseType === BattlerTagLapseType.CUSTOM) { - const effectPhase = globalScene.getCurrentPhase(); - if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) { - const attacker = effectPhase.getPokemon(); - globalScene.unshiftPhase(new StatStageChangePhase(attacker.getBattlerIndex(), false, [this.stat], this.levels)); - } - } - - return ret; - } -} - -export class ContactPoisonProtectedTag extends ProtectedTag { - constructor(sourceMove: Moves) { - super(sourceMove, BattlerTagType.BANEFUL_BUNKER); - } - - lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { - const ret = super.lapse(pokemon, lapseType); - - if (lapseType === BattlerTagLapseType.CUSTOM) { - const effectPhase = globalScene.getCurrentPhase(); - if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) { - const attacker = effectPhase.getPokemon(); - attacker.trySetStatus(StatusEffect.POISON, true, pokemon); - } - } - - return ret; - } -} - -/** - * `BattlerTag` class for moves that block damaging moves and burn the enemy if the enemy's move makes contact - * Used by {@linkcode Moves.BURNING_BULWARK} - */ -export class ContactBurnProtectedTag extends DamageProtectedTag { - constructor(sourceMove: Moves) { - super(sourceMove, BattlerTagType.BURNING_BULWARK); - } - - lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { - const ret = super.lapse(pokemon, lapseType); - - if (lapseType === BattlerTagLapseType.CUSTOM) { - const effectPhase = globalScene.getCurrentPhase(); - if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) { - const attacker = effectPhase.getPokemon(); - attacker.trySetStatus(StatusEffect.BURN, true); - } - } - - return ret; + /** + * Initiate the stat stage change on the attacker + * @param attacker - The pokemon using the contact move + * @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)); } } @@ -3518,9 +3531,9 @@ export function getBattlerTag( case BattlerTagType.SILK_TRAP: return new ContactStatStageChangeProtectedTag(sourceMove, tagType, Stat.SPD, -1); case BattlerTagType.BANEFUL_BUNKER: - return new ContactPoisonProtectedTag(sourceMove); + return new ContactSetStatusProtectedTag(sourceMove, tagType, StatusEffect.POISON); case BattlerTagType.BURNING_BULWARK: - return new ContactBurnProtectedTag(sourceMove); + return new ContactSetStatusProtectedTag(sourceMove, tagType, StatusEffect.BURN); case BattlerTagType.ENDURING: return new EnduringTag(tagType, BattlerTagLapseType.TURN_END, sourceMove); case BattlerTagType.ENDURE_TOKEN: diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index ce36a40697b..5ae7d227b3c 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -128,6 +128,7 @@ import { TarShotTag, AutotomizedTag, PowerTrickTag, + type GrudgeTag, } from "../data/battler-tags"; import { WeatherType } from "#enums/weather-type"; import { @@ -4754,15 +4755,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { new FaintPhase( this.getBattlerIndex(), false, - destinyTag, - grudgeTag, source, ), ); this.destroySubstitute(); this.lapseTag(BattlerTagType.COMMANDED); - this.resetSummonData(); } return result; @@ -4824,7 +4822,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ); this.destroySubstitute(); this.lapseTag(BattlerTagType.COMMANDED); - this.resetSummonData(); } return damage; } @@ -4992,6 +4989,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } + /**@overload */ + getTag(tagType: BattlerTagType.GRUDGE): GrudgeTag | nil; + /** @overload */ getTag(tagType: BattlerTagType): BattlerTag | nil; diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 01a556115a6..d1856c9331c 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -9,7 +9,6 @@ import { PostKnockOutAbAttr, PostVictoryAbAttr, } from "#app/data/abilities/ability"; -import type { DestinyBondTag, GrudgeTag } from "#app/data/battler-tags"; import { BattlerTagLapseType } from "#app/data/battler-tags"; import { battleSpecDialogue } from "#app/data/dialogue"; import { allMoves, PostVictoryStatStageChangeAttr } from "#app/data/moves/move"; @@ -32,6 +31,7 @@ import { ToggleDoublePositionPhase } from "./toggle-double-position-phase"; import { VictoryPhase } from "./victory-phase"; import { isNullOrUndefined } from "#app/utils"; import { FRIENDSHIP_LOSS_FROM_FAINT } from "#app/data/balance/starters"; +import { BattlerTagType } from "#enums/battler-tag-type"; export class FaintPhase extends PokemonPhase { /** @@ -39,33 +39,15 @@ export class FaintPhase extends PokemonPhase { */ private preventEndure: boolean; - /** - * Destiny Bond tag belonging to the currently fainting Pokemon, if applicable - */ - private destinyTag?: DestinyBondTag | null; - - /** - * Grudge tag belonging to the currently fainting Pokemon, if applicable - */ - private grudgeTag?: GrudgeTag | null; - /** * The source Pokemon that dealt fatal damage */ private source?: Pokemon; - constructor( - battlerIndex: BattlerIndex, - preventEndure = false, - destinyTag?: DestinyBondTag | null, - grudgeTag?: GrudgeTag | null, - source?: Pokemon, - ) { + constructor(battlerIndex: BattlerIndex, preventEndure = false, source?: Pokemon) { super(battlerIndex); this.preventEndure = preventEndure; - this.destinyTag = destinyTag; - this.grudgeTag = grudgeTag; this.source = source; } @@ -74,13 +56,12 @@ export class FaintPhase extends PokemonPhase { const faintPokemon = this.getPokemon(); - if (!isNullOrUndefined(this.destinyTag) && !isNullOrUndefined(this.source)) { - this.destinyTag.lapse(this.source, BattlerTagLapseType.CUSTOM); + if (this.source) { + faintPokemon.getTag(BattlerTagType.DESTINY_BOND)?.lapse(this.source, BattlerTagLapseType.CUSTOM); + faintPokemon.getTag(BattlerTagType.GRUDGE)?.lapse(faintPokemon, BattlerTagLapseType.CUSTOM, this.source); } - if (!isNullOrUndefined(this.grudgeTag) && !isNullOrUndefined(this.source)) { - this.grudgeTag.lapse(faintPokemon, BattlerTagLapseType.CUSTOM, this.source); - } + faintPokemon.resetSummonData(); if (!this.preventEndure) { const instantReviveModifier = globalScene.applyModifier( diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index af9f685eebe..3a4e5f32ede 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -627,18 +627,20 @@ export class MoveEffectPhase extends PokemonPhase { * @param hitResult - The {@linkcode HitResult} of the attempted move * @returns a `Promise` intended to be passed into a `then()` call. */ - protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult): void { + protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult) { + const hitsSubstitute = this.move.getMove().hitsSubstitute(user, target); if (!target.isFainted() || target.canApplyAbility()) { applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult); - if (!this.move.getMove().hitsSubstitute(user, target)) { + if (!hitsSubstitute) { if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) { globalScene.applyShuffledModifiers(EnemyAttackStatusEffectChanceModifier, false, target); } - - target.lapseTags(BattlerTagLapseType.AFTER_HIT); } } + if (!hitsSubstitute) { + target.lapseTags(BattlerTagLapseType.AFTER_HIT); + } } /** diff --git a/test/moves/beak_blast.test.ts b/test/moves/beak_blast.test.ts index 9f8b1e3d5c3..252b28448fd 100644 --- a/test/moves/beak_blast.test.ts +++ b/test/moves/beak_blast.test.ts @@ -38,7 +38,7 @@ describe("Moves - Beak Blast", () => { }); it("should add a charge effect that burns attackers on contact", async () => { - await game.startBattle([Species.BLASTOISE]); + await game.classicMode.startBattle([Species.BLASTOISE]); const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -55,7 +55,7 @@ describe("Moves - Beak Blast", () => { it("should still charge and burn opponents if the user is sleeping", async () => { game.override.statusEffect(StatusEffect.SLEEP); - await game.startBattle([Species.BLASTOISE]); + await game.classicMode.startBattle([Species.BLASTOISE]); const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -72,7 +72,7 @@ describe("Moves - Beak Blast", () => { it("should not burn attackers that don't make contact", async () => { game.override.enemyMoveset([Moves.WATER_GUN]); - await game.startBattle([Species.BLASTOISE]); + await game.classicMode.startBattle([Species.BLASTOISE]); const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -89,7 +89,7 @@ describe("Moves - Beak Blast", () => { it("should only hit twice with Multi-Lens", async () => { game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); - await game.startBattle([Species.BLASTOISE]); + await game.classicMode.startBattle([Species.BLASTOISE]); const leadPokemon = game.scene.getPlayerPokemon()!; @@ -102,7 +102,7 @@ describe("Moves - Beak Blast", () => { it("should be blocked by Protect", async () => { game.override.enemyMoveset([Moves.PROTECT]); - await game.startBattle([Species.BLASTOISE]); + await game.classicMode.startBattle([Species.BLASTOISE]); const leadPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -116,4 +116,25 @@ describe("Moves - Beak Blast", () => { expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); expect(leadPokemon.getTag(BattlerTagType.BEAK_BLAST_CHARGING)).toBeUndefined(); }); + + it("should still burn the enemy if the user is knocked out", async () => { + game.override.ability(Abilities.BALL_FETCH); + await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP]); + const enemyPokemon = game.scene.getEnemyPokemon()!; + const user = game.scene.getPlayerPokemon()!; + user.hp = 1; + game.move.select(Moves.BEAK_BLAST); + await game.phaseInterceptor.to("BerryPhase", false); + expect(enemyPokemon.status?.effect).toBe(StatusEffect.BURN); + }); + + it("should not burn a long reach enemy that hits the user with a contact move", async () => { + game.override.enemyAbility(Abilities.LONG_REACH); + game.override.enemyMoveset([Moves.FALSE_SWIPE]).enemyLevel(100); + await game.classicMode.startBattle([Species.MAGIKARP]); + game.move.select(Moves.BEAK_BLAST); + await game.phaseInterceptor.to("BerryPhase", false); + const enemyPokemon = game.scene.getEnemyPokemon()!; + expect(enemyPokemon.status?.effect).not.toBe(StatusEffect.BURN); + }); }); From b8b101119c66cfc67f16c842dbec11e1cc5ae3d4 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Thu, 17 Apr 2025 15:31:57 -0500 Subject: [PATCH 32/52] [Bug][Sprite] Use floats for variant shader recolor comparison (#5668) * Use float values for comparison * Remove unused colorInt --- src/pipelines/glsl/spriteFragShader.frag | 36 ++++++++---------------- src/pipelines/glsl/spriteShader.vert | 1 - src/pipelines/sprite.ts | 8 +++--- src/utils.ts | 5 +++- 4 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/pipelines/glsl/spriteFragShader.frag b/src/pipelines/glsl/spriteFragShader.frag index 3765e595b70..03f8c8c27bc 100644 --- a/src/pipelines/glsl/spriteFragShader.frag +++ b/src/pipelines/glsl/spriteFragShader.frag @@ -31,9 +31,9 @@ uniform vec2 texSize; uniform float yOffset; uniform float yShadowOffset; uniform vec4 tone; -uniform ivec4 baseVariantColors[32]; +uniform vec4 baseVariantColors[32]; uniform vec4 variantColors[32]; -uniform ivec4 spriteColors[32]; +uniform vec4 spriteColors[32]; uniform ivec4 fusionSpriteColors[32]; const vec3 lumaF = vec3(.299, .587, .114); @@ -69,7 +69,6 @@ float hue2rgb(float f1, float f2, float hue) { vec3 rgb2hsl(vec3 color) { vec3 hsl; - float fmin = min(min(color.r, color.g), color.b); float fmax = max(max(color.r, color.g), color.b); float delta = fmax - fmin; @@ -152,34 +151,23 @@ vec3 hsv2rgb(vec3 c) { void main() { vec4 texture = texture2D(uMainSampler[0], outTexCoord); - ivec4 colorInt = ivec4(texture*255.0); - for (int i = 0; i < 32; i++) { - if (baseVariantColors[i][3] == 0) + if (baseVariantColors[i].a == 0.0) break; - // abs value is broken in this version of gles with highp - ivec3 diffs = ivec3( - (colorInt.r > baseVariantColors[i].r) ? colorInt.r - baseVariantColors[i].r : baseVariantColors[i].r - colorInt.r, - (colorInt.g > baseVariantColors[i].g) ? colorInt.g - baseVariantColors[i].g : baseVariantColors[i].g - colorInt.g, - (colorInt.b > baseVariantColors[i].b) ? colorInt.b - baseVariantColors[i].b : baseVariantColors[i].b - colorInt.b - ); - // Set color threshold to be within 3 points for each channel - bvec3 threshold = lessThan(diffs, ivec3(3)); - - if (texture.a > 0.0 && all(threshold)) { + if (texture.a > 0.0 && all(lessThan(abs(texture.rgb - baseVariantColors[i].rgb), vec3(1.0/255.0)))) { texture.rgb = variantColors[i].rgb; break; } } for (int i = 0; i < 32; i++) { - if (spriteColors[i][3] == 0) + if (spriteColors[i][3] == 0.0) break; - if (texture.a > 0.0 && colorInt.r == spriteColors[i].r && colorInt.g == spriteColors[i].g && colorInt.b == spriteColors[i].b) { - vec3 fusionColor = vec3(float(fusionSpriteColors[i].r) / 255.0, float(fusionSpriteColors[i].g) / 255.0, float(fusionSpriteColors[i].b) / 255.0); - vec3 bg = vec3(spriteColors[i].rgb) / 255.0; + if (texture.a > 0.0 && all(lessThan(abs(texture.rgb - spriteColors[i].rgb), vec3(1.0/255.0)))) { + vec3 fusionColor = vec3(fusionSpriteColors[i].rgb) / 255.0; + vec3 bg = spriteColors[i].rgb; float gray = (bg.r + bg.g + bg.b) / 3.0; - bg = vec3(gray, gray, gray); + bg = vec3(gray); vec3 fg = fusionColor; texture.rgb = mix(1.0 - 2.0 * (1.0 - bg) * (1.0 - fg), 2.0 * bg * fg, step(bg, vec3(0.5))); break; @@ -192,7 +180,7 @@ void main() { vec4 color = texture * texel; if (color.a > 0.0 && teraColor.r > 0.0 && teraColor.g > 0.0 && teraColor.b > 0.0) { - vec2 relUv = vec2((outTexCoord.x - texFrameUv.x) / (size.x / texSize.x), (outTexCoord.y - texFrameUv.y) / (size.y / texSize.y)); + vec2 relUv = (outTexCoord.xy - texFrameUv.xy) / (size.xy / texSize.xy); vec2 teraTexCoord = vec2(relUv.x * (size.x / 200.0), relUv.y * (size.y / 120.0)); vec4 teraCol = texture2D(uMainSampler[1], teraTexCoord); float floorValue = 86.0 / 255.0; @@ -265,8 +253,8 @@ void main() { if ((spriteY >= 0.9 && (color.a == 0.0 || yOverflow))) { float shadowSpriteY = (spriteY - 0.9) * (1.0 / 0.15); - if (distance(vec2(spriteX, shadowSpriteY), vec2(0.5, 0.5)) < 0.5) { - color = vec4(vec3(0.0, 0.0, 0.0), 0.5); + if (distance(vec2(spriteX, shadowSpriteY), vec2(0.5)) < 0.5) { + color = vec4(vec3(0.0), 0.5); } else if (yOverflow) { discard; } diff --git a/src/pipelines/glsl/spriteShader.vert b/src/pipelines/glsl/spriteShader.vert index 33743384b47..84e73834f49 100644 --- a/src/pipelines/glsl/spriteShader.vert +++ b/src/pipelines/glsl/spriteShader.vert @@ -11,7 +11,6 @@ attribute float inTintEffect; attribute vec4 inTint; varying vec2 outTexCoord; -varying vec2 outtexFrameUv; varying float outTexId; varying vec2 outPosition; varying float outTintEffect; diff --git a/src/pipelines/sprite.ts b/src/pipelines/sprite.ts index d97cae1662b..0aa9409617a 100644 --- a/src/pipelines/sprite.ts +++ b/src/pipelines/sprite.ts @@ -101,7 +101,7 @@ export default class SpritePipeline extends FieldSpritePipeline { flatSpriteColors.splice( flatSpriteColors.length, 0, - ...(c < spriteColors.length ? spriteColors[c] : emptyColors), + ...(c < spriteColors.length ? spriteColors[c].map(x => x / 255.0) : emptyColors), ); flatFusionSpriteColors.splice( flatFusionSpriteColors.length, @@ -110,7 +110,7 @@ export default class SpritePipeline extends FieldSpritePipeline { ); } - this.set4iv("spriteColors", flatSpriteColors.flat()); + this.set4fv("spriteColors", flatSpriteColors.flat()); this.set4iv("fusionSpriteColors", flatFusionSpriteColors.flat()); } } @@ -146,7 +146,7 @@ export default class SpritePipeline extends FieldSpritePipeline { if (c < baseColors.length) { const baseColor = Array.from(Object.values(rgbHexToRgba(baseColors[c]))); const variantColor = Array.from(Object.values(rgbHexToRgba(variantColors[variant][baseColors[c]]))); - flatBaseColors.splice(flatBaseColors.length, 0, ...baseColor); + flatBaseColors.splice(flatBaseColors.length, 0, ...baseColor.map(c => c / 255.0)); flatVariantColors.splice(flatVariantColors.length, 0, ...variantColor.map(c => c / 255.0)); } else { flatBaseColors.splice(flatBaseColors.length, 0, ...emptyColors); @@ -160,7 +160,7 @@ export default class SpritePipeline extends FieldSpritePipeline { } } - this.set4iv("baseVariantColors", flatBaseColors.flat()); + this.set4fv("baseVariantColors", flatBaseColors.flat()); this.set4fv("variantColors", flatVariantColors.flat()); } diff --git a/src/utils.ts b/src/utils.ts index 2f05e2724ff..ce9966c0d7f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -405,8 +405,11 @@ export function deltaRgb(rgb1: number[], rgb2: number[]): number { return Math.ceil(Math.sqrt(2 * drp2 + 4 * dgp2 + 3 * dbp2 + (t * (drp2 - dbp2)) / 256)); } +// Extract out the rgb values from a hex string +const hexRegex = /^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i; + export function rgbHexToRgba(hex: string) { - const color = hex.match(/^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i) ?? ["000000", "00", "00", "00"]; + const color = hex.match(hexRegex) ?? ["000000", "00", "00", "00"]; return { r: Number.parseInt(color[1], 16), g: Number.parseInt(color[2], 16), From 82cd492117cf9a5c92d7b65322fcf34deac98a5a Mon Sep 17 00:00:00 2001 From: Lylian BALL <131535108+PyGaVS@users.noreply.github.com> Date: Fri, 18 Apr 2025 11:33:28 +0200 Subject: [PATCH 33/52] [Bug] Pokemon with illusion imitate the cry of the illusion (#5675) --- src/field/pokemon.ts | 9 ++++----- test/abilities/illusion.test.ts | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 5ae7d227b3c..27c4edea297 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1107,7 +1107,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ getSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { const species: PokemonSpecies = useIllusion && !!this.summonData?.illusion ? getPokemonSpecies(this.summonData?.illusion.species) : this.species; - const formIndex: integer = useIllusion && !!this.summonData?.illusion ? this.summonData?.illusion.formIndex : this.formIndex; if (!ignoreOverride && this.summonData?.speciesForm) { @@ -5282,13 +5281,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { sceneOverride?: BattleScene, ): AnySound { const scene = sceneOverride ?? globalScene; // TODO: is `sceneOverride` needed? - const cry = this.getSpeciesForm().cry(soundConfig); + const cry = this.getSpeciesForm(undefined, true).cry(soundConfig); let duration = cry.totalDuration * 1000; if ( this.fusionSpecies && - this.getSpeciesForm() !== this.getFusionSpeciesForm() + this.getSpeciesForm(undefined, true) !== this.getFusionSpeciesForm(undefined, true) ) { - let fusionCry = this.getFusionSpeciesForm().cry(soundConfig, true); + let fusionCry = this.getFusionSpeciesForm(undefined, true).cry(soundConfig, true); duration = Math.min(duration, fusionCry.totalDuration * 1000); fusionCry.destroy(); scene.time.delayedCall(fixedInt(Math.ceil(duration * 0.4)), () => { @@ -5298,7 +5297,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { cry, fixedInt(Math.ceil(duration * 0.2)), ); - fusionCry = this.getFusionSpeciesForm().cry( + fusionCry = this.getFusionSpeciesForm(undefined, true).cry( Object.assign( { seek: Math.max(fusionCry.totalDuration * 0.4, 0) }, soundConfig, diff --git a/test/abilities/illusion.test.ts b/test/abilities/illusion.test.ts index aa77aa701b2..bdb235f458b 100644 --- a/test/abilities/illusion.test.ts +++ b/test/abilities/illusion.test.ts @@ -7,6 +7,7 @@ import { Moves } from "#enums/moves"; import { Abilities } from "#enums/abilities"; import { PokeballType } from "#app/enums/pokeball"; import { Gender } from "#app/data/gender"; +import { BerryPhase } from "#app/phases/berry-phase"; describe("Abilities - Illusion", () => { let phaserGame: Phaser.Game; @@ -66,7 +67,7 @@ describe("Abilities - Illusion", () => { expect(!!zorua.summonData?.illusion).equals(false); }); - it("break if the ability is suppressed", async () => { + it("break with neutralizing gas", async () => { game.override.enemyAbility(Abilities.NEUTRALIZING_GAS); await game.classicMode.startBattle([Species.KOFFING]); From 54ce58411b18781acd32c41b7631c66c14000bfc Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Fri, 18 Apr 2025 04:35:46 -0500 Subject: [PATCH 34/52] [Bug] Fix forced switch bugs in enemy partner trainer battles (#5644) * Add isPartner method to trainer class * Ensure force switches cannot pull pokemon from the wrong trainer * Add override for battle type * Fixup tests and broken assumptions * Make move fail override semi-invuln check Bandaid fix because move effect phase does not allow for the move to fail if all of its conditions fail * Restore overrides * Apply kev's suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Fix illusion test battle type invocation * Update struggle and healer tests to use battleStyle --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/battle-scene.ts | 33 ++-- src/battle.ts | 8 +- src/data/abilities/ability.ts | 8 +- src/data/challenge.ts | 3 +- src/data/moves/move.ts | 45 +++--- .../utils/encounter-phase-utils.ts | 3 +- src/enums/battle-type.ts | 6 + src/field/trainer.ts | 7 + src/overrides.ts | 28 +++- src/phases/command-phase.ts | 2 +- src/phases/encounter-phase.ts | 3 +- src/phases/faint-phase.ts | 2 +- src/phases/game-over-phase.ts | 2 +- src/phases/summon-phase.ts | 2 +- src/phases/title-phase.ts | 2 +- src/phases/victory-phase.ts | 3 +- src/system/game-data.ts | 2 +- src/system/pokemon-data.ts | 2 +- src/ui/fight-ui-handler.ts | 2 +- src/ui/run-history-ui-handler.ts | 2 +- src/ui/run-info-ui-handler.ts | 2 +- test/abilities/ability_duplication.test.ts | 2 +- test/abilities/ability_timing.test.ts | 2 +- test/abilities/analytic.test.ts | 4 +- test/abilities/arena_trap.test.ts | 4 +- test/abilities/aroma_veil.test.ts | 2 +- test/abilities/aura_break.test.ts | 2 +- test/abilities/battery.test.ts | 2 +- test/abilities/battle_bond.test.ts | 2 +- test/abilities/beast_boost.test.ts | 2 +- test/abilities/commander.test.ts | 2 +- test/abilities/competitive.test.ts | 2 +- test/abilities/contrary.test.ts | 2 +- test/abilities/corrosion.test.ts | 2 +- test/abilities/costar.test.ts | 2 +- test/abilities/dancer.test.ts | 2 +- test/abilities/defiant.test.ts | 2 +- test/abilities/desolate-land.test.ts | 8 +- test/abilities/disguise.test.ts | 2 +- test/abilities/dry_skin.test.ts | 2 +- test/abilities/early_bird.test.ts | 2 +- test/abilities/flash_fire.test.ts | 2 +- test/abilities/flower_gift.test.ts | 4 +- test/abilities/flower_veil.test.ts | 14 +- test/abilities/forecast.test.ts | 2 +- test/abilities/friend_guard.test.ts | 2 +- test/abilities/galvanize.test.ts | 2 +- test/abilities/good_as_gold.test.ts | 12 +- test/abilities/gorilla_tactics.test.ts | 2 +- test/abilities/gulp_missile.test.ts | 2 +- test/abilities/healer.test.ts | 2 +- test/abilities/heatproof.test.ts | 2 +- test/abilities/honey_gather.test.ts | 2 +- test/abilities/hustle.test.ts | 2 +- test/abilities/hyper_cutter.test.ts | 2 +- test/abilities/ice_face.test.ts | 2 +- test/abilities/illuminate.test.ts | 2 +- test/abilities/illusion.test.ts | 2 +- test/abilities/immunity.test.ts | 12 +- test/abilities/imposter.test.ts | 2 +- test/abilities/infiltrator.test.ts | 2 +- test/abilities/insomnia.test.ts | 12 +- test/abilities/intimidate.test.ts | 4 +- test/abilities/intrepid_sword.test.ts | 2 +- test/abilities/libero.test.ts | 2 +- test/abilities/lightningrod.test.ts | 2 +- test/abilities/limber.test.ts | 12 +- test/abilities/magic_bounce.test.ts | 12 +- test/abilities/magma_armor.test.ts | 12 +- test/abilities/mimicry.test.ts | 2 +- test/abilities/mirror_armor.test.ts | 10 +- test/abilities/mold_breaker.test.ts | 13 +- test/abilities/moody.test.ts | 2 +- test/abilities/moxie.test.ts | 4 +- test/abilities/mummy.test.ts | 2 +- test/abilities/mycelium_might.test.ts | 2 +- test/abilities/neutralizing_gas.test.ts | 8 +- test/abilities/no_guard.test.ts | 2 +- test/abilities/oblivious.test.ts | 20 +-- test/abilities/own_tempo.test.ts | 12 +- test/abilities/parental_bond.test.ts | 4 +- test/abilities/pastel_veil.test.ts | 2 +- test/abilities/perish_body.test.ts | 2 +- test/abilities/power_construct.test.ts | 2 +- test/abilities/power_spot.test.ts | 2 +- test/abilities/protean.test.ts | 2 +- test/abilities/protosynthesis.test.ts | 2 +- test/abilities/quick_draw.test.ts | 2 +- test/abilities/sand_spit.test.ts | 2 +- test/abilities/sand_veil.test.ts | 2 +- test/abilities/sap_sipper.test.ts | 2 +- test/abilities/schooling.test.ts | 2 +- test/abilities/screen_cleaner.test.ts | 2 +- test/abilities/seed_sower.test.ts | 2 +- test/abilities/serene_grace.test.ts | 2 +- test/abilities/sheer_force.test.ts | 2 +- test/abilities/shield_dust.test.ts | 2 +- test/abilities/shields_down.test.ts | 2 +- test/abilities/simple.test.ts | 2 +- test/abilities/speed_boost.test.ts | 2 +- test/abilities/stakeout.test.ts | 2 +- test/abilities/stall.test.ts | 2 +- test/abilities/steely_spirit.test.ts | 2 +- test/abilities/storm_drain.test.ts | 2 +- test/abilities/sturdy.test.ts | 2 +- test/abilities/super_luck.test.ts | 2 +- test/abilities/supreme_overlord.test.ts | 2 +- test/abilities/sweet_veil.test.ts | 2 +- test/abilities/synchronize.test.ts | 2 +- test/abilities/tera_shell.test.ts | 2 +- test/abilities/thermal_exchange.test.ts | 12 +- test/abilities/trace.test.ts | 2 +- test/abilities/unburden.test.ts | 6 +- test/abilities/unseen_fist.test.ts | 2 +- test/abilities/victory_star.test.ts | 2 +- test/abilities/vital_spirit.test.ts | 12 +- test/abilities/volt_absorb.test.ts | 2 +- test/abilities/wandering_spirit.test.ts | 2 +- test/abilities/water_bubble.test.ts | 12 +- test/abilities/water_veil.test.ts | 12 +- test/abilities/wimp_out.test.ts | 15 +- test/abilities/wind_power.test.ts | 2 +- test/abilities/wind_rider.test.ts | 2 +- test/abilities/wonder_skin.test.ts | 2 +- test/abilities/zen_mode.test.ts | 2 +- test/abilities/zero_to_hero.test.ts | 2 +- test/arena/arena_gravity.test.ts | 2 +- test/arena/grassy_terrain.test.ts | 2 +- test/arena/weather_fog.test.ts | 2 +- test/arena/weather_hail.test.ts | 2 +- test/arena/weather_sandstorm.test.ts | 4 +- test/arena/weather_strong_winds.test.ts | 2 +- test/battle/ability_swap.test.ts | 2 +- test/battle/battle-order.test.ts | 8 +- test/battle/battle.test.ts | 20 +-- test/battle/damage_calculation.test.ts | 2 +- test/battle/double_battle.test.ts | 2 +- test/battle/inverse_battle.test.ts | 2 +- test/battle/special_battle.test.ts | 18 +-- test/boss-pokemon.test.ts | 8 +- test/daily_mode.test.ts | 2 +- test/data/status_effect.test.ts | 4 +- test/escape-calculations.test.ts | 6 +- test/evolution.test.ts | 2 +- test/items/dire_hit.test.ts | 2 +- test/items/eviolite.test.ts | 2 +- test/items/exp_booster.test.ts | 2 +- test/items/grip_claw.test.ts | 4 +- test/items/leek.test.ts | 2 +- test/items/leftovers.test.ts | 2 +- test/items/light_ball.test.ts | 2 +- test/items/lock_capsule.test.ts | 2 +- test/items/metal_powder.test.ts | 2 +- test/items/multi_lens.test.ts | 6 +- test/items/mystical_rock.test.ts | 2 +- test/items/quick_powder.test.ts | 2 +- test/items/reviver_seed.test.ts | 2 +- test/items/scope_lens.test.ts | 2 +- test/items/temp_stat_stage_booster.test.ts | 2 +- test/items/thick_club.test.ts | 2 +- test/items/toxic_orb.test.ts | 2 +- test/moves/after_you.test.ts | 2 +- test/moves/alluring_voice.test.ts | 2 +- test/moves/aromatherapy.test.ts | 2 +- test/moves/assist.test.ts | 2 +- test/moves/astonish.test.ts | 2 +- test/moves/aurora_veil.test.ts | 6 +- test/moves/autotomize.test.ts | 2 +- test/moves/baddy_bad.test.ts | 2 +- test/moves/baneful_bunker.test.ts | 2 +- test/moves/baton_pass.test.ts | 2 +- test/moves/beak_blast.test.ts | 2 +- test/moves/beat_up.test.ts | 2 +- test/moves/burning_jealousy.test.ts | 4 +- test/moves/camouflage.test.ts | 2 +- test/moves/ceaseless_edge.test.ts | 2 +- test/moves/chilly_reception.test.ts | 6 +- test/moves/chloroblast.test.ts | 2 +- test/moves/copycat.test.ts | 2 +- test/moves/crafty_shield.test.ts | 2 +- test/moves/defog.test.ts | 2 +- test/moves/destiny_bond.test.ts | 6 +- test/moves/diamond_storm.test.ts | 4 +- test/moves/dig.test.ts | 2 +- test/moves/disable.test.ts | 2 +- test/moves/dive.test.ts | 2 +- test/moves/doodle.test.ts | 6 +- test/moves/double_team.test.ts | 2 +- test/moves/dragon_cheer.test.ts | 2 +- test/moves/dragon_rage.test.ts | 2 +- test/moves/dragon_tail.test.ts | 6 +- test/moves/dynamax_cannon.test.ts | 2 +- test/moves/electrify.test.ts | 2 +- test/moves/electro_shot.test.ts | 2 +- test/moves/encore.test.ts | 2 +- test/moves/endure.test.ts | 2 +- test/moves/entrainment.test.ts | 2 +- test/moves/fairy_lock.test.ts | 2 +- test/moves/fake_out.test.ts | 2 +- test/moves/false_swipe.test.ts | 2 +- test/moves/fell_stinger.test.ts | 8 +- test/moves/fissure.test.ts | 2 +- test/moves/flame_burst.test.ts | 2 +- test/moves/flower_shield.test.ts | 4 +- test/moves/fly.test.ts | 2 +- test/moves/focus_punch.test.ts | 2 +- test/moves/follow_me.test.ts | 2 +- test/moves/forests_curse.test.ts | 2 +- test/moves/freeze_dry.test.ts | 2 +- test/moves/freezy_frost.test.ts | 4 +- test/moves/fusion_bolt.test.ts | 2 +- test/moves/fusion_flare.test.ts | 2 +- test/moves/fusion_flare_bolt.test.ts | 2 +- test/moves/future_sight.test.ts | 2 +- test/moves/gastro_acid.test.ts | 4 +- test/moves/geomancy.test.ts | 2 +- test/moves/gigaton_hammer.test.ts | 2 +- test/moves/glaive_rush.test.ts | 2 +- test/moves/growth.test.ts | 2 +- test/moves/grudge.test.ts | 2 +- test/moves/guard_split.test.ts | 2 +- test/moves/guard_swap.test.ts | 2 +- test/moves/hard_press.test.ts | 2 +- test/moves/haze.test.ts | 2 +- test/moves/heal_bell.test.ts | 2 +- test/moves/heart_swap.test.ts | 2 +- test/moves/hyper_beam.test.ts | 2 +- test/moves/imprison.test.ts | 2 +- test/moves/instruct.test.ts | 22 +-- test/moves/jaw_lock.test.ts | 4 +- test/moves/lash_out.test.ts | 2 +- test/moves/last_respects.test.ts | 2 +- test/moves/light_screen.test.ts | 4 +- test/moves/lucky_chant.test.ts | 4 +- test/moves/lunar_blessing.test.ts | 2 +- test/moves/lunar_dance.test.ts | 2 +- test/moves/magic_coat.test.ts | 12 +- test/moves/magnet_rise.test.ts | 2 +- test/moves/make_it_rain.test.ts | 4 +- test/moves/mat_block.test.ts | 2 +- test/moves/metal_burst.test.ts | 2 +- test/moves/metronome.test.ts | 4 +- test/moves/mirror_move.test.ts | 4 +- test/moves/mist.test.ts | 2 +- test/moves/moongeist_beam.test.ts | 2 +- test/moves/multi_target.test.ts | 2 +- test/moves/nightmare.test.ts | 2 +- test/moves/obstruct.test.ts | 2 +- test/moves/octolock.test.ts | 2 +- test/moves/order_up.test.ts | 2 +- test/moves/parting_shot.test.ts | 2 +- test/moves/plasma_fists.test.ts | 6 +- test/moves/pledge_moves.test.ts | 4 +- test/moves/pollen_puff.test.ts | 4 +- test/moves/powder.test.ts | 10 +- test/moves/power_shift.test.ts | 2 +- test/moves/power_split.test.ts | 2 +- test/moves/power_swap.test.ts | 2 +- test/moves/power_trick.test.ts | 2 +- test/moves/protect.test.ts | 2 +- test/moves/psycho_shift.test.ts | 2 +- test/moves/purify.test.ts | 2 +- test/moves/quash.test.ts | 2 +- test/moves/quick_guard.test.ts | 4 +- test/moves/rage_fist.test.ts | 2 +- test/moves/rage_powder.test.ts | 2 +- test/moves/reflect.test.ts | 4 +- test/moves/reflect_type.test.ts | 2 +- test/moves/relic_song.test.ts | 2 +- test/moves/retaliate.test.ts | 2 +- test/moves/revival_blessing.test.ts | 6 +- test/moves/role_play.test.ts | 2 +- test/moves/rollout.test.ts | 2 +- test/moves/roost.test.ts | 2 +- test/moves/round.test.ts | 2 +- test/moves/safeguard.test.ts | 4 +- test/moves/scale_shot.test.ts | 2 +- test/moves/secret_power.test.ts | 4 +- test/moves/shed_tail.test.ts | 2 +- test/moves/shell_side_arm.test.ts | 2 +- test/moves/shell_trap.test.ts | 4 +- test/moves/simple_beam.test.ts | 2 +- test/moves/sketch.test.ts | 2 +- test/moves/skill_swap.test.ts | 2 +- test/moves/sleep_talk.test.ts | 2 +- test/moves/solar_beam.test.ts | 2 +- test/moves/sparkly_swirl.test.ts | 4 +- test/moves/speed_swap.test.ts | 2 +- test/moves/spikes.test.ts | 4 +- test/moves/spit_up.test.ts | 2 +- test/moves/spotlight.test.ts | 2 +- test/moves/steamroller.test.ts | 2 +- test/moves/stockpile.test.ts | 2 +- test/moves/struggle.test.ts | 2 +- test/moves/substitute.test.ts | 2 +- test/moves/swallow.test.ts | 2 +- test/moves/syrup_bomb.test.ts | 2 +- test/moves/tackle.test.ts | 2 +- test/moves/tail_whip.test.ts | 2 +- test/moves/tailwind.test.ts | 6 +- test/moves/tar_shot.test.ts | 2 +- test/moves/taunt.test.ts | 2 +- test/moves/telekinesis.test.ts | 2 +- test/moves/tera_blast.test.ts | 2 +- test/moves/tera_starstorm.test.ts | 4 +- test/moves/thousand_arrows.test.ts | 2 +- test/moves/throat_chop.test.ts | 2 +- test/moves/thunder_wave.test.ts | 2 +- test/moves/tidy_up.test.ts | 2 +- test/moves/torment.test.ts | 2 +- test/moves/toxic.test.ts | 2 +- test/moves/toxic_spikes.test.ts | 2 +- test/moves/transform.test.ts | 2 +- test/moves/trick_or_treat.test.ts | 2 +- test/moves/triple_arrows.test.ts | 2 +- test/moves/u_turn.test.ts | 2 +- test/moves/upper_hand.test.ts | 2 +- test/moves/whirlwind.test.ts | 66 +++++++- test/moves/wide_guard.test.ts | 2 +- test/moves/will_o_wisp.test.ts | 2 +- test/phases/form-change-phase.test.ts | 2 +- test/phases/frenzy-move-reset.test.ts | 2 +- test/phases/game-over-phase.test.ts | 2 +- test/reload.test.ts | 12 +- test/system/game_data.test.ts | 2 +- test/testUtils/gameManagerUtils.ts | 3 +- test/testUtils/helpers/overridesHelper.ts | 147 +++++++++++------- test/ui/battle_info.test.ts | 2 +- test/ui/transfer-item.test.ts | 2 +- test/ui/type-hints.test.ts | 4 +- 330 files changed, 757 insertions(+), 628 deletions(-) create mode 100644 src/enums/battle-type.ts diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 90f53d6a95e..0fe4c7f7e4f 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -77,7 +77,8 @@ import { } from "#app/data/abilities/ability"; import { allAbilities } from "./data/data-lists"; import type { FixedBattleConfig } from "#app/battle"; -import Battle, { BattleType } from "#app/battle"; +import Battle from "#app/battle"; +import { BattleType } from "#enums/battle-type"; import type { GameMode } from "#app/game-mode"; import { GameModes, getGameMode } from "#app/game-mode"; import FieldSpritePipeline from "#app/pipelines/field-sprite"; @@ -1338,22 +1339,27 @@ export default class BattleScene extends SceneBase { } else { if ( !this.gameMode.hasTrainers || + Overrides.BATTLE_TYPE_OVERRIDE === BattleType.WILD || (Overrides.DISABLE_STANDARD_TRAINERS_OVERRIDE && isNullOrUndefined(trainerData)) ) { newBattleType = BattleType.WILD; - } else if (battleType === undefined) { - newBattleType = this.gameMode.isWaveTrainer(newWaveIndex, this.arena) ? BattleType.TRAINER : BattleType.WILD; } else { - newBattleType = battleType; + newBattleType = + Overrides.BATTLE_TYPE_OVERRIDE ?? + battleType ?? + (this.gameMode.isWaveTrainer(newWaveIndex, this.arena) ? BattleType.TRAINER : BattleType.WILD); } if (newBattleType === BattleType.TRAINER) { - const trainerType = this.arena.randomTrainerType(newWaveIndex); + const trainerType = + Overrides.RANDOM_TRAINER_OVERRIDE?.trainerType ?? this.arena.randomTrainerType(newWaveIndex); let doubleTrainer = false; if (trainerConfigs[trainerType].doubleOnly) { doubleTrainer = true; } else if (trainerConfigs[trainerType].hasDouble) { - doubleTrainer = !randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField)); + doubleTrainer = + Overrides.RANDOM_TRAINER_OVERRIDE?.alwaysDouble || + !randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField)); // Add a check that special trainers can't be double except for tate and liza - they should use the normal double chance if ( trainerConfigs[trainerType].trainerTypeDouble && @@ -1373,7 +1379,10 @@ export default class BattleScene extends SceneBase { // Check for mystery encounter // Can only occur in place of a standard (non-boss) wild battle, waves 10-180 - if (this.isWaveMysteryEncounter(newBattleType, newWaveIndex) || newBattleType === BattleType.MYSTERY_ENCOUNTER) { + if ( + !Overrides.BATTLE_TYPE_OVERRIDE && + (this.isWaveMysteryEncounter(newBattleType, newWaveIndex) || newBattleType === BattleType.MYSTERY_ENCOUNTER) + ) { newBattleType = BattleType.MYSTERY_ENCOUNTER; // Reset to base spawn weight this.mysteryEncounterSaveData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT; @@ -1383,9 +1392,9 @@ export default class BattleScene extends SceneBase { if (double === undefined && newWaveIndex > 1) { if (newBattleType === BattleType.WILD && !this.gameMode.isWaveFinal(newWaveIndex)) { newDouble = !randSeedInt(this.getDoubleBattleChance(newWaveIndex, playerField)); - } else if (newBattleType === BattleType.TRAINER) { - newDouble = newTrainer?.variant === TrainerVariant.DOUBLE; } + } else if (double === undefined && newBattleType === BattleType.TRAINER) { + newDouble = newTrainer?.variant === TrainerVariant.DOUBLE; } else if (!battleConfig) { newDouble = !!double; } @@ -1395,10 +1404,10 @@ export default class BattleScene extends SceneBase { newDouble = false; } - if (!isNullOrUndefined(Overrides.BATTLE_TYPE_OVERRIDE)) { + if (!isNullOrUndefined(Overrides.BATTLE_STYLE_OVERRIDE)) { let doubleOverrideForWave: "single" | "double" | null = null; - switch (Overrides.BATTLE_TYPE_OVERRIDE) { + switch (Overrides.BATTLE_STYLE_OVERRIDE) { case "double": doubleOverrideForWave = "double"; break; @@ -1418,7 +1427,7 @@ export default class BattleScene extends SceneBase { } /** * Override battles into single only if not fighting with trainers. - * @see {@link https://github.com/pagefaultgames/pokerogue/issues/1948 | GitHub Issue #1948} + * @see {@link https://github.com/pagefaultgames/pokerogue/issues/1948 GitHub Issue #1948} */ if (newBattleType !== BattleType.TRAINER && doubleOverrideForWave === "single") { newDouble = false; diff --git a/src/battle.ts b/src/battle.ts index fb5af223b8f..3e2f293065a 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -30,6 +30,7 @@ import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; import type { CustomModifierSettings } from "#app/modifier/modifier-type"; import { ModifierTier } from "#app/modifier/modifier-tier"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { BattleType } from "#enums/battle-type"; export enum ClassicFixedBossWaves { TOWN_YOUNGSTER = 5, @@ -54,13 +55,6 @@ export enum ClassicFixedBossWaves { RIVAL_6 = 195, } -export enum BattleType { - WILD, - TRAINER, - CLEAR, - MYSTERY_ENCOUNTER, -} - export enum BattlerIndex { ATTACKER = -1, PLAYER, diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 6cbb579d4e0..a3bd9b728f5 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -30,7 +30,7 @@ import i18next from "i18next"; import { Command } from "#app/ui/command-ui-handler"; import { BerryModifierType } from "#app/modifier/modifier-type"; import { getPokeballName } from "#app/data/pokeball"; -import { BattleType } from "#app/battle"; +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"; @@ -44,6 +44,7 @@ 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"; +import { TrainerVariant } from "#app/field/trainer"; // Enum imports import { Stat, type BattleStat , BATTLE_STATS, EFFECTIVE_STATS, getStatKey, type EffectiveStat } from "#enums/stat"; @@ -61,6 +62,7 @@ import { MoveFlags } from "#enums/MoveFlags"; import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; + // Type imports import type { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; @@ -5526,8 +5528,8 @@ class ForceSwitchOutHelper { const party = player ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); return (!player && globalScene.currentBattle.battleType === BattleType.WILD) - || party.filter(p => p.isAllowedInBattle() - && (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > globalScene.currentBattle.getBattlerCount(); + || party.filter(p => p.isAllowedInBattle() && !p.isOnField() + && (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > 0; } /** diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 51616c3f00f..cc5783ad1fb 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -8,7 +8,8 @@ import { speciesStarterCosts } from "#app/data/balance/starters"; import type Pokemon from "#app/field/pokemon"; import { PokemonMove } from "#app/field/pokemon"; import type { FixedBattleConfig } from "#app/battle"; -import { ClassicFixedBossWaves, BattleType, getRandomTrainerFunc } from "#app/battle"; +import { ClassicFixedBossWaves, getRandomTrainerFunc } from "#app/battle"; +import { BattleType } from "#enums/battle-type"; import Trainer, { TrainerVariant } from "#app/field/trainer"; import { PokemonType } from "#enums/pokemon-type"; import { Challenges } from "#enums/challenges"; diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index c2dd0ec31ca..6e5e09839c1 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -75,7 +75,7 @@ import { PreserveBerryModifier, } from "../../modifier/modifier"; import type { BattlerIndex } from "../../battle"; -import { BattleType } from "../../battle"; +import { BattleType } from "#enums/battle-type"; import { TerrainType } from "../terrain"; import { ModifierPoolType } from "#app/modifier/modifier-type"; import { Command } from "../../ui/command-ui-handler"; @@ -121,6 +121,7 @@ import { MoveFlags } from "#enums/MoveFlags"; import { MoveEffectTrigger } from "#enums/MoveEffectTrigger"; import { MultiHitType } from "#enums/MultiHitType"; import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves } from "./invalid-moves"; +import { TrainerVariant } from "#app/field/trainer"; type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean; type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean; @@ -6295,9 +6296,10 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { return false; } else if (globalScene.currentBattle.battleType !== BattleType.WILD) { // Switch out logic for enemy trainers // Find indices of off-field Pokemon that are eligible to be switched into + const isPartnerTrainer = globalScene.currentBattle.trainer?.isPartner(); const eligibleNewIndices: number[] = []; globalScene.getEnemyParty().forEach((pokemon, index) => { - if (pokemon.isAllowedInBattle() && !pokemon.isOnField()) { + if (pokemon.isAllowedInBattle() && !pokemon.isOnField() && (!isPartnerTrainer || pokemon.trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)) { eligibleNewIndices.push(index); } }); @@ -6347,15 +6349,6 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { } } - if (globalScene.currentBattle.waveIndex % 10 === 0) { - return false; - } - - // Don't allow wild mons to flee with U-turn et al. - if (this.selfSwitch && !user.isPlayer() && move.category !== MoveCategory.STATUS) { - return false; - } - const allyPokemon = switchOutTarget.getAlly(); if (switchOutTarget.hp > 0) { @@ -6368,13 +6361,12 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { } } - if (!allyPokemon?.isActive(true)) { - globalScene.clearEnemyHeldItemModifiers(); + // clear out enemy held item modifiers of the switch out target + globalScene.clearEnemyHeldItemModifiers(switchOutTarget); - if (switchOutTarget.hp) { + if (!allyPokemon?.isActive(true) && switchOutTarget.hp) { globalScene.pushPhase(new BattleEndPhase(false)); globalScene.pushPhase(new NewBattlePhase()); - } } } @@ -6393,6 +6385,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { } } + getSwitchOutCondition(): MoveConditionFunc { return (user, target, move) => { const switchOutTarget = (this.selfSwitch ? user : target); @@ -6416,23 +6409,23 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { const blockedByAbility = new BooleanHolder(false); applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility); - return !blockedByAbility.value; + if (blockedByAbility.value) { + return false; + } } + if (!player && globalScene.currentBattle.battleType === BattleType.WILD) { - if (this.isBatonPass()) { - return false; - } - // Don't allow wild opponents to flee on the boss stage since it can ruin a run early on - if (globalScene.currentBattle.waveIndex % 10 === 0) { - return false; - } + // wild pokemon cannot switch out with baton pass. + return !this.isBatonPass() + && globalScene.currentBattle.waveIndex % 10 !== 0 + // Don't allow wild mons to flee with U-turn et al. + && !(this.selfSwitch && MoveCategory.STATUS !== move.category); } const party = player ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); - return (!player && !globalScene.currentBattle.battleType) - || party.filter(p => p.isAllowedInBattle() - && (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > globalScene.currentBattle.getBattlerCount(); + return party.filter(p => p.isAllowedInBattle() && !p.isOnField() + && (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > 0; }; } diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index a9f6b787878..69b0d81520a 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -1,5 +1,6 @@ import type Battle from "#app/battle"; -import { BattlerIndex, BattleType } from "#app/battle"; +import { BattlerIndex } from "#app/battle"; +import { BattleType } from "#enums/battle-type"; import { biomeLinks, BiomePoolTier } from "#app/data/balance/biomes"; import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; import { diff --git a/src/enums/battle-type.ts b/src/enums/battle-type.ts new file mode 100644 index 00000000000..81cf89ef488 --- /dev/null +++ b/src/enums/battle-type.ts @@ -0,0 +1,6 @@ +export enum BattleType { + WILD, + TRAINER, + CLEAR, + MYSTERY_ENCOUNTER +} diff --git a/src/field/trainer.ts b/src/field/trainer.ts index 30cf43b54a1..1679e6f12e0 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -223,6 +223,13 @@ export default class Trainer extends Phaser.GameObjects.Container { return this.config.doubleOnly || this.variant === TrainerVariant.DOUBLE; } + /** + * Return whether the trainer is a duo, like Tate & Liza + */ + isPartner(): boolean { + return this.variant === TrainerVariant.DOUBLE; + } + getMixedBattleBgm(): string { return this.config.mixedBattleBgm; } diff --git a/src/overrides.ts b/src/overrides.ts index 21c72cd7b98..d36cfbfac98 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -15,11 +15,13 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PokeballType } from "#enums/pokeball"; import { PokemonType } from "#enums/pokemon-type"; import { Species } from "#enums/species"; -import { Stat } from "#enums/stat"; +import { BATTLE_STATS, Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; import { TimeOfDay } from "#enums/time-of-day"; import { VariantTier } from "#enums/variant-tier"; import { WeatherType } from "#enums/weather-type"; +import { TrainerType } from "#enums/trainer-type"; +import { BattleType } from "#enums/battle-type"; /** * This comment block exists to prevent IDEs from automatically removing unused imports @@ -41,7 +43,7 @@ import { WeatherType } from "#enums/weather-type"; * } * ``` */ -const overrides = {} satisfies Partial>; +const overrides = {} satisfies Partial>; /** * If you need to add Overrides values for local testing do that inside {@linkcode overrides} @@ -69,7 +71,7 @@ class DefaultOverrides { * * If `"odd-doubles"`, follow the `"double"` rule on odd wave numbers, and follow the `"single"` rule on even wave numbers. */ - readonly BATTLE_TYPE_OVERRIDE: BattleStyle | null = null; + readonly BATTLE_STYLE_OVERRIDE: BattleStyle | null = null; readonly STARTING_WAVE_OVERRIDE: number = 0; readonly STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN; readonly ARENA_TINT_OVERRIDE: TimeOfDay | null = null; @@ -259,6 +261,16 @@ class DefaultOverrides { * If `true`, disable all non-scripted opponent trainer encounters. */ readonly DISABLE_STANDARD_TRAINERS_OVERRIDE: boolean = false; + + /** + * Set all non-scripted waves to use the selected battle type. + * + * Ignored if set to {@linkcode BattleType.TRAINER} and `DISABLE_STANDARD_TRAINERS_OVERRIDE` is `true`. + */ + readonly BATTLE_TYPE_OVERRIDE: Exclude | null = null; + + /** Force all random trainer types to be the provided type. */ + readonly RANDOM_TRAINER_OVERRIDE: RandomTrainerOverride | null = null; } export const defaultOverrides = new DefaultOverrides(); @@ -269,3 +281,13 @@ export default { } satisfies InstanceType; export type BattleStyle = "double" | "single" | "even-doubles" | "odd-doubles"; + +export type RandomTrainerOverride = { + /** The Type of trainer to force */ + trainerType: Exclude, + /* If the selected trainer type has a double version, it will always use its double version. */ + alwaysDouble?: boolean +} + +/** The type of the {@linkcode DefaultOverrides} class */ +export type OverridesType = typeof DefaultOverrides; \ No newline at end of file diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 8691ac453ca..30343f92aa3 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import type { TurnCommand } from "#app/battle"; -import { BattleType } from "#app/battle"; +import { BattleType } from "#enums/battle-type"; import type { EncoreTag } from "#app/data/battler-tags"; import { TrappedTag } from "#app/data/battler-tags"; import type { MoveTargetSet } from "#app/data/moves/move"; diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index 67236c1c041..c196608f91e 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -1,4 +1,5 @@ -import { BattlerIndex, BattleType } from "#app/battle"; +import { BattlerIndex } from "#app/battle"; +import { BattleType } from "#enums/battle-type"; import { globalScene } from "#app/global-scene"; import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; import { applyAbAttrs, SyncEncounterNatureAbAttr, applyPreSummonAbAttrs, PreSummonAbAttr } from "#app/data/abilities/ability"; diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index d1856c9331c..2719206a6cc 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -1,5 +1,5 @@ import type { BattlerIndex } from "#app/battle"; -import { BattleType } from "#app/battle"; +import { BattleType } from "#enums/battle-type"; import { globalScene } from "#app/global-scene"; import { applyPostFaintAbAttrs, diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index 1ccdc9c7106..9e79eafe88b 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -1,5 +1,5 @@ import { clientSessionId } from "#app/account"; -import { BattleType } from "#app/battle"; +import { BattleType } from "#enums/battle-type"; import { globalScene } from "#app/global-scene"; import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import { getCharVariantFromDialogue } from "#app/data/dialogue"; diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index 60d45f19c0c..ee27fc28247 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -1,4 +1,4 @@ -import { BattleType } from "#app/battle"; +import { BattleType } from "#enums/battle-type"; import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball"; import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms"; import { TrainerSlot } from "#enums/trainer-slot"; diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index 108366d4774..bc1b157e98e 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -1,5 +1,5 @@ import { loggedInUser } from "#app/account"; -import { BattleType } from "#app/battle"; +import { BattleType } from "#enums/battle-type"; import { fetchDailyRunSeed, getDailyRunStarters } from "#app/data/daily-run"; import { Gender } from "#app/data/gender"; import { getBiomeKey } from "#app/field/arena"; diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index 9f4412fe270..17b29f654e2 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -1,5 +1,6 @@ import type { BattlerIndex } from "#app/battle"; -import { BattleType, ClassicFixedBossWaves } from "#app/battle"; +import { ClassicFixedBossWaves } from "#app/battle"; +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"; diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 53146301666..698299845a3 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -15,7 +15,7 @@ import PersistentModifierData from "#app/system/modifier-data"; import ArenaData from "#app/system/arena-data"; import { Unlockables } from "#app/system/unlockables"; import { GameModes, getGameMode } from "#app/game-mode"; -import { BattleType } from "#app/battle"; +import { BattleType } from "#enums/battle-type"; import TrainerData from "#app/system/trainer-data"; import { trainerConfigs } from "#app/data/trainers/trainer-config"; import { resetSettings, setSetting, SettingKeys } from "#app/system/settings/settings"; diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 97ce494a43a..00baad8cf12 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -1,4 +1,4 @@ -import { BattleType } from "../battle"; +import { BattleType } from "#enums/battle-type"; import { globalScene } from "#app/global-scene"; import type { Gender } from "../data/gender"; import type { Nature } from "#enums/nature"; diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index 27985629e3d..285a1dd36cc 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -14,7 +14,7 @@ import type { PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import type { CommandPhase } from "#app/phases/command-phase"; import MoveInfoOverlay from "./move-info-overlay"; -import { BattleType } from "#app/battle"; +import { BattleType } from "#enums/battle-type"; export default class FightUiHandler extends UiHandler implements InfoToggle { public static readonly MOVES_CONTAINER_NAME = "moves"; diff --git a/src/ui/run-history-ui-handler.ts b/src/ui/run-history-ui-handler.ts index ffc9d378d18..16aad7b8634 100644 --- a/src/ui/run-history-ui-handler.ts +++ b/src/ui/run-history-ui-handler.ts @@ -8,7 +8,7 @@ import type PokemonData from "../system/pokemon-data"; import MessageUiHandler from "./message-ui-handler"; import i18next from "i18next"; import { Button } from "../enums/buttons"; -import { BattleType } from "../battle"; +import { BattleType } from "#enums/battle-type"; import type { RunEntry } from "../system/game-data"; import { PlayerGender } from "#enums/player-gender"; import { TrainerVariant } from "../field/trainer"; diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index 47de6a1a64d..60667035147 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -9,7 +9,7 @@ import { formatLargeNumber, getPlayTimeString, formatMoney, formatFancyLargeNumb import type PokemonData from "../system/pokemon-data"; import i18next from "i18next"; import { Button } from "../enums/buttons"; -import { BattleType } from "../battle"; +import { BattleType } from "#enums/battle-type"; import { TrainerVariant } from "../field/trainer"; import { Challenges } from "#enums/challenges"; import { getLuckString, getLuckTextTint } from "../modifier/modifier-type"; diff --git a/test/abilities/ability_duplication.test.ts b/test/abilities/ability_duplication.test.ts index 08b74f682f2..de429045bb8 100644 --- a/test/abilities/ability_duplication.test.ts +++ b/test/abilities/ability_duplication.test.ts @@ -24,7 +24,7 @@ describe("Ability Duplication", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.SPLASH]) - .battleType("single") + .battleStyle("single") .ability(Abilities.HUGE_POWER) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH); diff --git a/test/abilities/ability_timing.test.ts b/test/abilities/ability_timing.test.ts index d59c4f7c38d..9df4fe0d1c9 100644 --- a/test/abilities/ability_timing.test.ts +++ b/test/abilities/ability_timing.test.ts @@ -27,7 +27,7 @@ describe("Ability Timing", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.INTIMIDATE) .ability(Abilities.BALL_FETCH); diff --git a/test/abilities/analytic.test.ts b/test/abilities/analytic.test.ts index e488b467ce0..1aadf2c0746 100644 --- a/test/abilities/analytic.test.ts +++ b/test/abilities/analytic.test.ts @@ -26,7 +26,7 @@ describe("Abilities - Analytic", () => { game.override .moveset([Moves.SPLASH, Moves.TACKLE]) .ability(Abilities.ANALYTIC) - .battleType("single") + .battleStyle("single") .disableCrits() .startingLevel(200) .enemyLevel(200) @@ -53,7 +53,7 @@ describe("Abilities - Analytic", () => { }); it("should increase damage only if the user moves last in doubles", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.classicMode.startBattle([Species.GENGAR, Species.SHUCKLE]); const [enemy] = game.scene.getEnemyField(); diff --git a/test/abilities/arena_trap.test.ts b/test/abilities/arena_trap.test.ts index 3a5bad9c34b..f37b8a2859f 100644 --- a/test/abilities/arena_trap.test.ts +++ b/test/abilities/arena_trap.test.ts @@ -32,7 +32,7 @@ describe("Abilities - Arena Trap", () => { // TODO: Enable test when Issue #935 is addressed it.todo("should not allow grounded Pokémon to flee", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); await game.classicMode.startBattle(); @@ -61,7 +61,7 @@ describe("Abilities - Arena Trap", () => { */ it("should lift if pokemon with this ability leaves the field", async () => { game.override - .battleType("double") + .battleStyle("double") .enemyMoveset(Moves.SPLASH) .moveset([Moves.ROAR, Moves.SPLASH]) .ability(Abilities.BALL_FETCH); diff --git a/test/abilities/aroma_veil.test.ts b/test/abilities/aroma_veil.test.ts index af8a0233a60..38683bcb1e3 100644 --- a/test/abilities/aroma_veil.test.ts +++ b/test/abilities/aroma_veil.test.ts @@ -25,7 +25,7 @@ describe("Moves - Aroma Veil", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("double") + .battleStyle("double") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset([Moves.HEAL_BLOCK, Moves.IMPRISON, Moves.SPLASH]) .enemySpecies(Species.SHUCKLE) diff --git a/test/abilities/aura_break.test.ts b/test/abilities/aura_break.test.ts index 86b6c69ec8b..523a2773c99 100644 --- a/test/abilities/aura_break.test.ts +++ b/test/abilities/aura_break.test.ts @@ -24,7 +24,7 @@ describe("Abilities - Aura Break", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.moveset([Moves.MOONBLAST, Moves.DARK_PULSE, Moves.MOONBLAST, Moves.DARK_PULSE]); game.override.enemyMoveset(Moves.SPLASH); game.override.enemyAbility(Abilities.AURA_BREAK); diff --git a/test/abilities/battery.test.ts b/test/abilities/battery.test.ts index cc7570c3d31..6a1f77f4b27 100644 --- a/test/abilities/battery.test.ts +++ b/test/abilities/battery.test.ts @@ -26,7 +26,7 @@ describe("Abilities - Battery", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.enemySpecies(Species.SHUCKLE); game.override.enemyAbility(Abilities.BALL_FETCH); game.override.moveset([Moves.TACKLE, Moves.BREAKING_SWIPE, Moves.SPLASH, Moves.DAZZLING_GLEAM]); diff --git a/test/abilities/battle_bond.test.ts b/test/abilities/battle_bond.test.ts index 6305d7dedc5..d599b3212f9 100644 --- a/test/abilities/battle_bond.test.ts +++ b/test/abilities/battle_bond.test.ts @@ -28,7 +28,7 @@ describe("Abilities - BATTLE BOND", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .startingWave(4) // Leads to arena reset on Wave 5 trainer battle .ability(Abilities.BATTLE_BOND) .starterForms({ [Species.GRENINJA]: ashForm }) diff --git a/test/abilities/beast_boost.test.ts b/test/abilities/beast_boost.test.ts index b307a9eeeba..a6b6ec0aacf 100644 --- a/test/abilities/beast_boost.test.ts +++ b/test/abilities/beast_boost.test.ts @@ -24,7 +24,7 @@ describe("Abilities - Beast Boost", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.BULBASAUR) .enemyAbility(Abilities.BEAST_BOOST) .ability(Abilities.BEAST_BOOST) diff --git a/test/abilities/commander.test.ts b/test/abilities/commander.test.ts index 9d16d474dd4..0e6cb1b9208 100644 --- a/test/abilities/commander.test.ts +++ b/test/abilities/commander.test.ts @@ -34,7 +34,7 @@ describe("Abilities - Commander", () => { .enemyLevel(100) .moveset([Moves.LIQUIDATION, Moves.MEMENTO, Moves.SPLASH, Moves.FLIP_TURN]) .ability(Abilities.COMMANDER) - .battleType("double") + .battleStyle("double") .disableCrits() .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/competitive.test.ts b/test/abilities/competitive.test.ts index cad35be18f7..1e0b5fcf40e 100644 --- a/test/abilities/competitive.test.ts +++ b/test/abilities/competitive.test.ts @@ -25,7 +25,7 @@ describe("Abilities - Competitive", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.BEEDRILL) .enemyMoveset(Moves.TICKLE) .startingLevel(1) diff --git a/test/abilities/contrary.test.ts b/test/abilities/contrary.test.ts index 19041eb2801..929d620c232 100644 --- a/test/abilities/contrary.test.ts +++ b/test/abilities/contrary.test.ts @@ -23,7 +23,7 @@ describe("Abilities - Contrary", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.BULBASAUR) .enemyAbility(Abilities.CONTRARY) .ability(Abilities.INTIMIDATE) diff --git a/test/abilities/corrosion.test.ts b/test/abilities/corrosion.test.ts index b7f316fbe2d..c72aef9f0a3 100644 --- a/test/abilities/corrosion.test.ts +++ b/test/abilities/corrosion.test.ts @@ -23,7 +23,7 @@ describe("Abilities - Corrosion", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.SPLASH]) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.GRIMER) .enemyAbility(Abilities.CORROSION) diff --git a/test/abilities/costar.test.ts b/test/abilities/costar.test.ts index c6a44bffe54..7b1e362689d 100644 --- a/test/abilities/costar.test.ts +++ b/test/abilities/costar.test.ts @@ -24,7 +24,7 @@ describe("Abilities - COSTAR", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.ability(Abilities.COSTAR); game.override.moveset([Moves.SPLASH, Moves.NASTY_PLOT]); game.override.enemyMoveset(Moves.SPLASH); diff --git a/test/abilities/dancer.test.ts b/test/abilities/dancer.test.ts index c296329473d..cdd1e3221e9 100644 --- a/test/abilities/dancer.test.ts +++ b/test/abilities/dancer.test.ts @@ -23,7 +23,7 @@ describe("Abilities - Dancer", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); }); // Reference Link: https://bulbapedia.bulbagarden.net/wiki/Dancer_(Ability) diff --git a/test/abilities/defiant.test.ts b/test/abilities/defiant.test.ts index a73002d999c..d06aef4d785 100644 --- a/test/abilities/defiant.test.ts +++ b/test/abilities/defiant.test.ts @@ -25,7 +25,7 @@ describe("Abilities - Defiant", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.BEEDRILL) .enemyMoveset(Moves.TICKLE) .startingLevel(1) diff --git a/test/abilities/desolate-land.test.ts b/test/abilities/desolate-land.test.ts index bb0b152418d..d6f01f7aa5e 100644 --- a/test/abilities/desolate-land.test.ts +++ b/test/abilities/desolate-land.test.ts @@ -38,7 +38,7 @@ describe("Abilities - Desolate Land", () => { * is forcefully moved out of the field from moves such as Roar {@linkcode Moves.ROAR} */ it("should lift only when all pokemon with this ability leave the field", async () => { - game.override.battleType("double").enemyMoveset([Moves.SPLASH, Moves.ROAR]); + game.override.battleStyle("double").enemyMoveset([Moves.SPLASH, Moves.ROAR]); await game.classicMode.startBattle([Species.MAGCARGO, Species.MAGCARGO, Species.MAGIKARP, Species.MAGIKARP]); expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN); @@ -76,7 +76,7 @@ describe("Abilities - Desolate Land", () => { it("should lift when enemy faints", async () => { game.override - .battleType("single") + .battleStyle("single") .moveset([Moves.SHEER_COLD]) .ability(Abilities.NO_GUARD) .startingLevel(100) @@ -96,7 +96,7 @@ describe("Abilities - Desolate Land", () => { }); it("should lift when pokemon returns upon switching from double to single battle", async () => { - game.override.battleType("even-doubles").enemyMoveset([Moves.SPLASH, Moves.MEMENTO]).startingWave(12); + game.override.battleStyle("even-doubles").enemyMoveset([Moves.SPLASH, Moves.MEMENTO]).startingWave(12); await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGCARGO]); expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN); @@ -117,7 +117,7 @@ describe("Abilities - Desolate Land", () => { it("should lift when enemy is captured", async () => { game.override - .battleType("single") + .battleStyle("single") .enemyMoveset([Moves.SPLASH]) .enemySpecies(Species.MAGCARGO) .enemyHasPassiveAbility(true); diff --git a/test/abilities/disguise.test.ts b/test/abilities/disguise.test.ts index a971f5c2733..fd8289312db 100644 --- a/test/abilities/disguise.test.ts +++ b/test/abilities/disguise.test.ts @@ -27,7 +27,7 @@ describe("Abilities - Disguise", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MIMIKYU) .enemyMoveset(Moves.SPLASH) .starterSpecies(Species.REGIELEKI) diff --git a/test/abilities/dry_skin.test.ts b/test/abilities/dry_skin.test.ts index 9d8a29c431a..398d09393ab 100644 --- a/test/abilities/dry_skin.test.ts +++ b/test/abilities/dry_skin.test.ts @@ -22,7 +22,7 @@ describe("Abilities - Dry Skin", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .disableCrits() .enemyAbility(Abilities.DRY_SKIN) .enemyMoveset(Moves.SPLASH) diff --git a/test/abilities/early_bird.test.ts b/test/abilities/early_bird.test.ts index cc486672c95..0f298ba479d 100644 --- a/test/abilities/early_bird.test.ts +++ b/test/abilities/early_bird.test.ts @@ -27,7 +27,7 @@ describe("Abilities - Early Bird", () => { game.override .moveset([Moves.REST, Moves.BELLY_DRUM, Moves.SPLASH]) .ability(Abilities.EARLY_BIRD) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/flash_fire.test.ts b/test/abilities/flash_fire.test.ts index 3cec9cd9cb7..8d94d21adf8 100644 --- a/test/abilities/flash_fire.test.ts +++ b/test/abilities/flash_fire.test.ts @@ -27,7 +27,7 @@ describe("Abilities - Flash Fire", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .ability(Abilities.FLASH_FIRE) .enemyAbility(Abilities.BALL_FETCH) .startingLevel(20) diff --git a/test/abilities/flower_gift.test.ts b/test/abilities/flower_gift.test.ts index 8c7b32e7e33..f2b32dc4c80 100644 --- a/test/abilities/flower_gift.test.ts +++ b/test/abilities/flower_gift.test.ts @@ -47,7 +47,7 @@ describe("Abilities - Flower Gift", () => { allyAbility = Abilities.BALL_FETCH, enemyAbility = Abilities.BALL_FETCH, ): Promise<[number, number]> => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.SPLASH, Moves.SUNNY_DAY, move, Moves.HEAL_PULSE]); game.override.enemyMoveset([Moves.SPLASH, Moves.HEAL_PULSE]); const target_index = allyAttacker ? BattlerIndex.ENEMY : BattlerIndex.PLAYER_2; @@ -110,7 +110,7 @@ describe("Abilities - Flower Gift", () => { }); it("increases the ATK and SPDEF stat stages of the Pokémon with this Ability and its allies by 1.5× during Harsh Sunlight", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.classicMode.startBattle([Species.CHERRIM, Species.MAGIKARP]); const [cherrim, magikarp] = game.scene.getPlayerField(); diff --git a/test/abilities/flower_veil.test.ts b/test/abilities/flower_veil.test.ts index 68242be3886..1fd7dbb3ed7 100644 --- a/test/abilities/flower_veil.test.ts +++ b/test/abilities/flower_veil.test.ts @@ -31,7 +31,7 @@ describe("Abilities - Flower Veil", () => { .moveset([Moves.SPLASH]) .enemySpecies(Species.BULBASAUR) .ability(Abilities.FLOWER_VEIL) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -63,7 +63,7 @@ describe("Abilities - Flower Veil", () => { }); it("should prevent drowsiness from yawn for a grass user and its grass allies", async () => { - game.override.enemyMoveset([Moves.YAWN]).moveset([Moves.SPLASH]).battleType("double"); + game.override.enemyMoveset([Moves.YAWN]).moveset([Moves.SPLASH]).battleStyle("double"); await game.classicMode.startBattle([Species.BULBASAUR, Species.BULBASAUR]); // Clear the ability of the ally to isolate the test @@ -81,7 +81,7 @@ describe("Abilities - Flower Veil", () => { }); it("should prevent status conditions from moves like Thunder Wave for a grass user and its grass allies", async () => { - game.override.enemyMoveset([Moves.THUNDER_WAVE]).moveset([Moves.SPLASH]).battleType("double"); + game.override.enemyMoveset([Moves.THUNDER_WAVE]).moveset([Moves.SPLASH]).battleStyle("double"); vi.spyOn(allMoves[Moves.THUNDER_WAVE], "accuracy", "get").mockReturnValue(100); await game.classicMode.startBattle([Species.BULBASAUR]); @@ -93,7 +93,7 @@ describe("Abilities - Flower Veil", () => { }); it("should not prevent status conditions for a non-grass user and its non-grass allies", async () => { - game.override.enemyMoveset([Moves.THUNDER_WAVE]).moveset([Moves.SPLASH]).battleType("double"); + game.override.enemyMoveset([Moves.THUNDER_WAVE]).moveset([Moves.SPLASH]).battleStyle("double"); await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP]); const [user, ally] = game.scene.getPlayerField(); vi.spyOn(allMoves[Moves.THUNDER_WAVE], "accuracy", "get").mockReturnValue(100); @@ -113,7 +113,7 @@ describe("Abilities - Flower Veil", () => { *******************************************/ it("should prevent the status drops from enemies for the a grass user and its grass allies", async () => { - game.override.enemyMoveset([Moves.GROWL]).moveset([Moves.SPLASH]).battleType("double"); + game.override.enemyMoveset([Moves.GROWL]).moveset([Moves.SPLASH]).battleStyle("double"); await game.classicMode.startBattle([Species.BULBASAUR, Species.BULBASAUR]); const [user, ally] = game.scene.getPlayerField(); // Clear the ally ability to isolate the test @@ -126,7 +126,7 @@ describe("Abilities - Flower Veil", () => { }); it("should not prevent status drops for a non-grass user and its non-grass allies", async () => { - game.override.enemyMoveset([Moves.GROWL]).moveset([Moves.SPLASH]).battleType("double"); + game.override.enemyMoveset([Moves.GROWL]).moveset([Moves.SPLASH]).battleStyle("double"); await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP]); const [user, ally] = game.scene.getPlayerField(); // Clear the ally ability to isolate the test @@ -139,7 +139,7 @@ describe("Abilities - Flower Veil", () => { }); it("should not prevent self-inflicted stat drops from moves like Close Combat for a user or its allies", async () => { - game.override.moveset([Moves.CLOSE_COMBAT]).battleType("double"); + game.override.moveset([Moves.CLOSE_COMBAT]).battleStyle("double"); await game.classicMode.startBattle([Species.BULBASAUR, Species.BULBASAUR]); const [user, ally] = game.scene.getPlayerField(); // Clear the ally ability to isolate the test diff --git a/test/abilities/forecast.test.ts b/test/abilities/forecast.test.ts index 675b9a8b59c..03b5d993a54 100644 --- a/test/abilities/forecast.test.ts +++ b/test/abilities/forecast.test.ts @@ -75,7 +75,7 @@ describe("Abilities - Forecast", () => { async () => { game.override .moveset([Moves.RAIN_DANCE, Moves.SUNNY_DAY, Moves.SNOWSCAPE, Moves.SPLASH]) - .battleType("double") + .battleStyle("double") .starterForms({ [Species.KYOGRE]: 1, [Species.GROUDON]: 1, diff --git a/test/abilities/friend_guard.test.ts b/test/abilities/friend_guard.test.ts index 474c89adaf1..302343c167b 100644 --- a/test/abilities/friend_guard.test.ts +++ b/test/abilities/friend_guard.test.ts @@ -26,7 +26,7 @@ describe("Moves - Friend Guard", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("double") + .battleStyle("double") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset([Moves.TACKLE, Moves.SPLASH, Moves.DRAGON_RAGE]) .enemySpecies(Species.SHUCKLE) diff --git a/test/abilities/galvanize.test.ts b/test/abilities/galvanize.test.ts index c1e02c6c8d8..438ec498aa1 100644 --- a/test/abilities/galvanize.test.ts +++ b/test/abilities/galvanize.test.ts @@ -27,7 +27,7 @@ describe("Abilities - Galvanize", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .startingLevel(100) .ability(Abilities.GALVANIZE) .moveset([Moves.TACKLE, Moves.REVELATION_DANCE, Moves.FURY_SWIPES]) diff --git a/test/abilities/good_as_gold.test.ts b/test/abilities/good_as_gold.test.ts index 4c4741a331f..944c1d1bca1 100644 --- a/test/abilities/good_as_gold.test.ts +++ b/test/abilities/good_as_gold.test.ts @@ -32,7 +32,7 @@ describe("Abilities - Good As Gold", () => { game.override .moveset([Moves.SPLASH]) .ability(Abilities.GOOD_AS_GOLD) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -63,7 +63,7 @@ describe("Abilities - Good As Gold", () => { }); it("should not block any status moves that target the field, one side, or all pokemon", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.enemyMoveset([Moves.STEALTH_ROCK, Moves.HAZE]); game.override.moveset([Moves.SWORDS_DANCE, Moves.SAFEGUARD]); await game.classicMode.startBattle([Species.MAGIKARP, Species.FEEBAS]); @@ -85,7 +85,7 @@ describe("Abilities - Good As Gold", () => { }); it("should not block field targeted effects in singles", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemyMoveset([Moves.SPIKES]); await game.classicMode.startBattle([Species.MAGIKARP]); @@ -96,7 +96,7 @@ describe("Abilities - Good As Gold", () => { }); it("should block the ally's helping hand", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.HELPING_HAND, Moves.TACKLE]); await game.classicMode.startBattle([Species.MAGIKARP, Species.FEEBAS]); @@ -108,7 +108,7 @@ describe("Abilities - Good As Gold", () => { }); it("should block the ally's heal bell, but only if the good as gold user is on the field", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.HEAL_BELL, Moves.SPLASH]); game.override.statusEffect(StatusEffect.BURN); await game.classicMode.startBattle([Species.MAGIKARP, Species.FEEBAS, Species.ABRA]); @@ -130,7 +130,7 @@ describe("Abilities - Good As Gold", () => { }); it("should not block field targeted effects like rain dance", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemyMoveset([Moves.RAIN_DANCE]); game.override.weather(WeatherType.NONE); await game.classicMode.startBattle([Species.MAGIKARP]); diff --git a/test/abilities/gorilla_tactics.test.ts b/test/abilities/gorilla_tactics.test.ts index 48dab262b82..edaf1669809 100644 --- a/test/abilities/gorilla_tactics.test.ts +++ b/test/abilities/gorilla_tactics.test.ts @@ -23,7 +23,7 @@ describe("Abilities - Gorilla Tactics", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset([Moves.SPLASH, Moves.DISABLE]) .enemySpecies(Species.MAGIKARP) diff --git a/test/abilities/gulp_missile.test.ts b/test/abilities/gulp_missile.test.ts index 8ebd583d3ab..4db2ae4190d 100644 --- a/test/abilities/gulp_missile.test.ts +++ b/test/abilities/gulp_missile.test.ts @@ -42,7 +42,7 @@ describe("Abilities - Gulp Missile", () => { game = new GameManager(phaserGame); game.override .disableCrits() - .battleType("single") + .battleStyle("single") .moveset([Moves.SURF, Moves.DIVE, Moves.SPLASH, Moves.SUBSTITUTE]) .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/healer.test.ts b/test/abilities/healer.test.ts index 35aa74209b4..d06c4680e36 100644 --- a/test/abilities/healer.test.ts +++ b/test/abilities/healer.test.ts @@ -32,7 +32,7 @@ describe("Abilities - Healer", () => { game.override .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("double") + .battleStyle("double") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/heatproof.test.ts b/test/abilities/heatproof.test.ts index fa065d1ed03..f2fabf953d6 100644 --- a/test/abilities/heatproof.test.ts +++ b/test/abilities/heatproof.test.ts @@ -25,7 +25,7 @@ describe("Abilities - Heatproof", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.CHARMANDER) .enemyAbility(Abilities.HEATPROOF) diff --git a/test/abilities/honey_gather.test.ts b/test/abilities/honey_gather.test.ts index bea5c25c878..a74a40c9c1e 100644 --- a/test/abilities/honey_gather.test.ts +++ b/test/abilities/honey_gather.test.ts @@ -28,7 +28,7 @@ describe("Abilities - Honey Gather", () => { .startingLevel(100) .ability(Abilities.HONEY_GATHER) .passiveAbility(Abilities.RUN_AWAY) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/hustle.test.ts b/test/abilities/hustle.test.ts index 40197cf9e97..bf2889eab63 100644 --- a/test/abilities/hustle.test.ts +++ b/test/abilities/hustle.test.ts @@ -27,7 +27,7 @@ describe("Abilities - Hustle", () => { .ability(Abilities.HUSTLE) .moveset([Moves.TACKLE, Moves.GIGA_DRAIN, Moves.FISSURE]) .disableCrits() - .battleType("single") + .battleStyle("single") .enemyMoveset(Moves.SPLASH) .enemySpecies(Species.SHUCKLE) .enemyAbility(Abilities.BALL_FETCH); diff --git a/test/abilities/hyper_cutter.test.ts b/test/abilities/hyper_cutter.test.ts index fe5623e4e0f..99a9db28025 100644 --- a/test/abilities/hyper_cutter.test.ts +++ b/test/abilities/hyper_cutter.test.ts @@ -23,7 +23,7 @@ describe("Abilities - Hyper Cutter", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .moveset([Moves.SAND_ATTACK, Moves.NOBLE_ROAR, Moves.DEFOG, Moves.OCTOLOCK]) .ability(Abilities.BALL_FETCH) .enemySpecies(Species.SHUCKLE) diff --git a/test/abilities/ice_face.test.ts b/test/abilities/ice_face.test.ts index e85794928d6..38269c29af1 100644 --- a/test/abilities/ice_face.test.ts +++ b/test/abilities/ice_face.test.ts @@ -30,7 +30,7 @@ describe("Abilities - Ice Face", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.EISCUE); game.override.enemyAbility(Abilities.ICE_FACE); game.override.moveset([Moves.TACKLE, Moves.ICE_BEAM, Moves.TOXIC_THREAD, Moves.HAIL]); diff --git a/test/abilities/illuminate.test.ts b/test/abilities/illuminate.test.ts index 6518fec989b..ba26ed3b7af 100644 --- a/test/abilities/illuminate.test.ts +++ b/test/abilities/illuminate.test.ts @@ -29,7 +29,7 @@ describe("Abilities - Illuminate", () => { }); it("should prevent ACC stat stage from being lowered", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); await game.classicMode.startBattle(); diff --git a/test/abilities/illusion.test.ts b/test/abilities/illusion.test.ts index bdb235f458b..382d7d74a08 100644 --- a/test/abilities/illusion.test.ts +++ b/test/abilities/illusion.test.ts @@ -25,7 +25,7 @@ describe("Abilities - Illusion", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.ZORUA); game.override.enemyAbility(Abilities.ILLUSION); game.override.enemyMoveset(Moves.TACKLE); diff --git a/test/abilities/immunity.test.ts b/test/abilities/immunity.test.ts index 51e9598720b..dd9026cac50 100644 --- a/test/abilities/immunity.test.ts +++ b/test/abilities/immunity.test.ts @@ -23,9 +23,9 @@ describe("Abilities - Immunity", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([ Moves.SPLASH ]) + .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -33,12 +33,12 @@ describe("Abilities - Immunity", () => { }); it("should remove poison when gained", async () => { - game.override.ability(Abilities.IMMUNITY) + game.override + .ability(Abilities.IMMUNITY) .enemyAbility(Abilities.BALL_FETCH) .moveset(Moves.SKILL_SWAP) - .enemyMoveset(Moves.SPLASH), - - await game.classicMode.startBattle([ Species.FEEBAS ]); + .enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([Species.FEEBAS]); const enemy = game.scene.getEnemyPokemon(); enemy?.trySetStatus(StatusEffect.POISON); expect(enemy?.status?.effect).toBe(StatusEffect.POISON); diff --git a/test/abilities/imposter.test.ts b/test/abilities/imposter.test.ts index 2c7302d04b7..b5e902f442f 100644 --- a/test/abilities/imposter.test.ts +++ b/test/abilities/imposter.test.ts @@ -25,7 +25,7 @@ describe("Abilities - Imposter", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MEW) .enemyLevel(200) .enemyAbility(Abilities.BEAST_BOOST) diff --git a/test/abilities/infiltrator.test.ts b/test/abilities/infiltrator.test.ts index 6278439651c..10353f35391 100644 --- a/test/abilities/infiltrator.test.ts +++ b/test/abilities/infiltrator.test.ts @@ -30,7 +30,7 @@ describe("Abilities - Infiltrator", () => { game.override .moveset([Moves.TACKLE, Moves.WATER_GUN, Moves.SPORE, Moves.BABY_DOLL_EYES]) .ability(Abilities.INFILTRATOR) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/insomnia.test.ts b/test/abilities/insomnia.test.ts index 91fdc3fc668..49765a641b0 100644 --- a/test/abilities/insomnia.test.ts +++ b/test/abilities/insomnia.test.ts @@ -23,9 +23,9 @@ describe("Abilities - Insomnia", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([ Moves.SPLASH ]) + .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -33,12 +33,12 @@ describe("Abilities - Insomnia", () => { }); it("should remove sleep when gained", async () => { - game.override.ability(Abilities.INSOMNIA) + game.override + .ability(Abilities.INSOMNIA) .enemyAbility(Abilities.BALL_FETCH) .moveset(Moves.SKILL_SWAP) - .enemyMoveset(Moves.SPLASH), - - await game.classicMode.startBattle([ Species.FEEBAS ]); + .enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([Species.FEEBAS]); const enemy = game.scene.getEnemyPokemon(); enemy?.trySetStatus(StatusEffect.SLEEP); expect(enemy?.status?.effect).toBe(StatusEffect.SLEEP); diff --git a/test/abilities/intimidate.test.ts b/test/abilities/intimidate.test.ts index 53286d354c8..2888c575b0d 100644 --- a/test/abilities/intimidate.test.ts +++ b/test/abilities/intimidate.test.ts @@ -25,7 +25,7 @@ describe("Abilities - Intimidate", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.RATTATA) .enemyAbility(Abilities.INTIMIDATE) .enemyPassiveAbility(Abilities.HYDRATION) @@ -65,7 +65,7 @@ describe("Abilities - Intimidate", () => { }, 20000); it("should lower ATK stat stage by 1 for every enemy Pokemon in a double battle on entry", async () => { - game.override.battleType("double").startingWave(3); + game.override.battleStyle("double").startingWave(3); await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); game.onNextPrompt( "CheckSwitchPhase", diff --git a/test/abilities/intrepid_sword.test.ts b/test/abilities/intrepid_sword.test.ts index 28d0cd02c7f..b30ae4a9bd0 100644 --- a/test/abilities/intrepid_sword.test.ts +++ b/test/abilities/intrepid_sword.test.ts @@ -22,7 +22,7 @@ describe("Abilities - Intrepid Sword", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.ZACIAN); game.override.enemyAbility(Abilities.INTREPID_SWORD); game.override.ability(Abilities.INTREPID_SWORD); diff --git a/test/abilities/libero.test.ts b/test/abilities/libero.test.ts index 22abf1c248f..2e3668813c5 100644 --- a/test/abilities/libero.test.ts +++ b/test/abilities/libero.test.ts @@ -29,7 +29,7 @@ describe("Abilities - Libero", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.ability(Abilities.LIBERO); game.override.startingLevel(100); game.override.enemySpecies(Species.RATTATA); diff --git a/test/abilities/lightningrod.test.ts b/test/abilities/lightningrod.test.ts index 986899353ff..21a03baf12b 100644 --- a/test/abilities/lightningrod.test.ts +++ b/test/abilities/lightningrod.test.ts @@ -26,7 +26,7 @@ describe("Abilities - Lightningrod", () => { game.override .moveset([Moves.SPLASH, Moves.SHOCK_WAVE]) .ability(Abilities.BALL_FETCH) - .battleType("double") + .battleStyle("double") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/limber.test.ts b/test/abilities/limber.test.ts index 2b167cc155f..4cdaa86f44c 100644 --- a/test/abilities/limber.test.ts +++ b/test/abilities/limber.test.ts @@ -23,9 +23,9 @@ describe("Abilities - Limber", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([ Moves.SPLASH ]) + .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -33,12 +33,12 @@ describe("Abilities - Limber", () => { }); it("should remove paralysis when gained", async () => { - game.override.ability(Abilities.LIMBER) + game.override + .ability(Abilities.LIMBER) .enemyAbility(Abilities.BALL_FETCH) .moveset(Moves.SKILL_SWAP) - .enemyMoveset(Moves.SPLASH), - - await game.classicMode.startBattle([ Species.FEEBAS ]); + .enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([Species.FEEBAS]); const enemy = game.scene.getEnemyPokemon(); enemy?.trySetStatus(StatusEffect.PARALYSIS); expect(enemy?.status?.effect).toBe(StatusEffect.PARALYSIS); diff --git a/test/abilities/magic_bounce.test.ts b/test/abilities/magic_bounce.test.ts index 7886ac5fd5c..11131640a0f 100644 --- a/test/abilities/magic_bounce.test.ts +++ b/test/abilities/magic_bounce.test.ts @@ -30,7 +30,7 @@ describe("Abilities - Magic Bounce", () => { game = new GameManager(phaserGame); game.override .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .moveset([Moves.GROWL, Moves.SPLASH]) .disableCrits() .enemySpecies(Species.MAGIKARP) @@ -60,7 +60,7 @@ describe("Abilities - Magic Bounce", () => { }); it("should individually bounce back multi-target moves", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.GROWL, Moves.SPLASH]); await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP]); @@ -114,7 +114,7 @@ describe("Abilities - Magic Bounce", () => { }); it("should bounce back a spread status move against both pokemon", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.GROWL, Moves.SPLASH]); game.override.enemyMoveset([Moves.SPLASH]); await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP]); @@ -127,7 +127,7 @@ describe("Abilities - Magic Bounce", () => { }); it("should only bounce spikes back once in doubles when both targets have magic bounce", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.classicMode.startBattle([Species.MAGIKARP]); game.override.moveset([Moves.SPIKES]); @@ -227,7 +227,7 @@ describe("Abilities - Magic Bounce", () => { // TODO: stomping tantrum should consider moves that were bounced. it.todo("should cause stomping tantrum to double in power when the last move was bounced", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); await game.classicMode.startBattle([Species.MAGIKARP]); game.override.moveset([Moves.STOMPING_TANTRUM, Moves.CHARM]); @@ -309,7 +309,7 @@ describe("Abilities - Magic Bounce", () => { }); it("should always apply the leftmost available target's magic bounce when bouncing moves like sticky webs in doubles", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.STICKY_WEB, Moves.SPLASH, Moves.TRICK_ROOM]); await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP]); diff --git a/test/abilities/magma_armor.test.ts b/test/abilities/magma_armor.test.ts index b1d62f948d2..c5af522ca6f 100644 --- a/test/abilities/magma_armor.test.ts +++ b/test/abilities/magma_armor.test.ts @@ -23,9 +23,9 @@ describe("Abilities - Magma Armor", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([ Moves.SPLASH ]) + .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -33,12 +33,12 @@ describe("Abilities - Magma Armor", () => { }); it("should remove freeze when gained", async () => { - game.override.ability(Abilities.MAGMA_ARMOR) + game.override + .ability(Abilities.MAGMA_ARMOR) .enemyAbility(Abilities.BALL_FETCH) .moveset(Moves.SKILL_SWAP) - .enemyMoveset(Moves.SPLASH), - - await game.classicMode.startBattle([ Species.FEEBAS ]); + .enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([Species.FEEBAS]); const enemy = game.scene.getEnemyPokemon(); enemy?.trySetStatus(StatusEffect.FREEZE); expect(enemy?.status?.effect).toBe(StatusEffect.FREEZE); diff --git a/test/abilities/mimicry.test.ts b/test/abilities/mimicry.test.ts index df6f7905c83..598f5790aa8 100644 --- a/test/abilities/mimicry.test.ts +++ b/test/abilities/mimicry.test.ts @@ -25,7 +25,7 @@ describe("Abilities - Mimicry", () => { game.override .moveset([Moves.SPLASH]) .ability(Abilities.MIMICRY) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyMoveset(Moves.SPLASH); diff --git a/test/abilities/mirror_armor.test.ts b/test/abilities/mirror_armor.test.ts index 6b0c3f10c84..bd61f39ba75 100644 --- a/test/abilities/mirror_armor.test.ts +++ b/test/abilities/mirror_armor.test.ts @@ -27,7 +27,7 @@ describe("Ability - Mirror Armor", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.RATTATA) .enemyMoveset([Moves.SPLASH, Moves.STICKY_WEB, Moves.TICKLE, Moves.OCTOLOCK]) .enemyAbility(Abilities.BALL_FETCH) @@ -71,7 +71,7 @@ describe("Ability - Mirror Armor", () => { }); it("Player side + double battle Intimidate - opponents each lose -2 atk", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.ability(Abilities.MIRROR_ARMOR); game.override.enemyAbility(Abilities.INTIMIDATE); await game.classicMode.startBattle([Species.BULBASAUR, Species.CHARMANDER]); @@ -93,7 +93,7 @@ describe("Ability - Mirror Armor", () => { }); it("Enemy side + double battle Intimidate - players each lose -2 atk", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.enemyAbility(Abilities.MIRROR_ARMOR); game.override.ability(Abilities.INTIMIDATE); await game.classicMode.startBattle([Species.BULBASAUR, Species.CHARMANDER]); @@ -134,7 +134,7 @@ describe("Ability - Mirror Armor", () => { }); it("Player side + double battle Intimidate + Tickle - opponents each lose -3 atk, -1 def", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.ability(Abilities.MIRROR_ARMOR); game.override.enemyAbility(Abilities.INTIMIDATE); await game.classicMode.startBattle([Species.BULBASAUR, Species.CHARMANDER]); @@ -288,7 +288,7 @@ describe("Ability - Mirror Armor", () => { }); it("Double battle + sticky web applied player side - player switches out and enemy 1 should lose -1 speed", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.ability(Abilities.MIRROR_ARMOR); await game.classicMode.startBattle([Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE]); diff --git a/test/abilities/mold_breaker.test.ts b/test/abilities/mold_breaker.test.ts index 8f050a68d76..ba33909364f 100644 --- a/test/abilities/mold_breaker.test.ts +++ b/test/abilities/mold_breaker.test.ts @@ -24,9 +24,9 @@ describe("Abilities - Mold Breaker", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([ Moves.SPLASH ]) + .moveset([Moves.SPLASH]) .ability(Abilities.MOLD_BREAKER) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -34,17 +34,18 @@ describe("Abilities - Mold Breaker", () => { }); it("should turn off the ignore abilities arena variable after the user's move", async () => { - game.override.enemyMoveset(Moves.SPLASH) + game.override + .enemyMoveset(Moves.SPLASH) .ability(Abilities.MOLD_BREAKER) - .moveset([ Moves.ERUPTION ]) + .moveset([Moves.ERUPTION]) .startingLevel(100) .enemyLevel(2); - await game.classicMode.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([Species.MAGIKARP]); const enemy = game.scene.getEnemyPokemon()!; expect(enemy.isFainted()).toBe(false); game.move.select(Moves.SPLASH); - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("MoveEndPhase", true); expect(globalScene.arena.ignoreAbilities).toBe(false); }); diff --git a/test/abilities/moody.test.ts b/test/abilities/moody.test.ts index da24899a4b0..9b658820391 100644 --- a/test/abilities/moody.test.ts +++ b/test/abilities/moody.test.ts @@ -24,7 +24,7 @@ describe("Abilities - Moody", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.RATTATA) .enemyAbility(Abilities.BALL_FETCH) .ability(Abilities.MOODY) diff --git a/test/abilities/moxie.test.ts b/test/abilities/moxie.test.ts index ec93aebd2c0..bccdeda2b93 100644 --- a/test/abilities/moxie.test.ts +++ b/test/abilities/moxie.test.ts @@ -27,7 +27,7 @@ describe("Abilities - Moxie", () => { beforeEach(() => { game = new GameManager(phaserGame); const moveToUse = Moves.AERIAL_ACE; - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.RATTATA); game.override.enemyAbility(Abilities.MOXIE); game.override.ability(Abilities.MOXIE); @@ -54,7 +54,7 @@ describe("Abilities - Moxie", () => { it.todo( "should raise ATK stat stage by 1 when defeating an ally Pokemon", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); const moveToUse = Moves.AERIAL_ACE; await game.startBattle([Species.MIGHTYENA, Species.MIGHTYENA]); diff --git a/test/abilities/mummy.test.ts b/test/abilities/mummy.test.ts index 0971353c14d..c53b0b33598 100644 --- a/test/abilities/mummy.test.ts +++ b/test/abilities/mummy.test.ts @@ -24,7 +24,7 @@ describe("Abilities - Mummy", () => { game.override .moveset([Moves.SPLASH]) .ability(Abilities.MUMMY) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/mycelium_might.test.ts b/test/abilities/mycelium_might.test.ts index 8c7796ec736..4a5700045fa 100644 --- a/test/abilities/mycelium_might.test.ts +++ b/test/abilities/mycelium_might.test.ts @@ -24,7 +24,7 @@ describe("Abilities - Mycelium Might", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.disableCrits(); game.override.enemySpecies(Species.SHUCKLE); game.override.enemyAbility(Abilities.CLEAR_BODY); diff --git a/test/abilities/neutralizing_gas.test.ts b/test/abilities/neutralizing_gas.test.ts index 56a663db403..32c61b72e4d 100644 --- a/test/abilities/neutralizing_gas.test.ts +++ b/test/abilities/neutralizing_gas.test.ts @@ -31,7 +31,7 @@ describe("Abilities - Neutralizing Gas", () => { game.override .moveset([Moves.SPLASH]) .ability(Abilities.NEUTRALIZING_GAS) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -105,7 +105,7 @@ describe("Abilities - Neutralizing Gas", () => { }); it("should only deactivate when all setters are off the field", async () => { - game.override.enemyMoveset([Moves.ENTRAINMENT, Moves.SPLASH]).battleType("double"); + game.override.enemyMoveset([Moves.ENTRAINMENT, Moves.SPLASH]).battleStyle("double"); await game.classicMode.startBattle([Species.ACCELGOR, Species.ACCELGOR]); game.move.select(Moves.SPLASH, 0); @@ -148,7 +148,7 @@ describe("Abilities - Neutralizing Gas", () => { }); it("should deactivate upon catching a wild pokemon", async () => { - game.override.battleType("single").enemyAbility(Abilities.NEUTRALIZING_GAS).ability(Abilities.BALL_FETCH); + game.override.battleStyle("single").enemyAbility(Abilities.NEUTRALIZING_GAS).ability(Abilities.BALL_FETCH); await game.classicMode.startBattle([Species.MAGIKARP]); expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined(); @@ -174,7 +174,7 @@ describe("Abilities - Neutralizing Gas", () => { }); it("should not activate abilities of pokemon no longer on the field", async () => { - game.override.battleType("single").ability(Abilities.NEUTRALIZING_GAS).enemyAbility(Abilities.DELTA_STREAM); + game.override.battleStyle("single").ability(Abilities.NEUTRALIZING_GAS).enemyAbility(Abilities.DELTA_STREAM); await game.classicMode.startBattle([Species.MAGIKARP]); const enemy = game.scene.getEnemyPokemon()!; diff --git a/test/abilities/no_guard.test.ts b/test/abilities/no_guard.test.ts index 41b8fbd27b9..b34007bc700 100644 --- a/test/abilities/no_guard.test.ts +++ b/test/abilities/no_guard.test.ts @@ -33,7 +33,7 @@ describe("Abilities - No Guard", () => { }); it("should make moves always hit regardless of move accuracy", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); await game.classicMode.startBattle([Species.REGIELEKI]); diff --git a/test/abilities/oblivious.test.ts b/test/abilities/oblivious.test.ts index d5089ef6a72..a86899ec9c6 100644 --- a/test/abilities/oblivious.test.ts +++ b/test/abilities/oblivious.test.ts @@ -23,9 +23,9 @@ describe("Abilities - Oblivious", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([ Moves.SPLASH ]) + .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -33,12 +33,12 @@ describe("Abilities - Oblivious", () => { }); it("should remove taunt when gained", async () => { - game.override.ability(Abilities.OBLIVIOUS) + game.override + .ability(Abilities.OBLIVIOUS) .enemyAbility(Abilities.BALL_FETCH) .moveset(Moves.SKILL_SWAP) - .enemyMoveset(Moves.SPLASH), - - await game.classicMode.startBattle([ Species.FEEBAS ]); + .enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([Species.FEEBAS]); const enemy = game.scene.getEnemyPokemon(); enemy?.addTag(BattlerTagType.TAUNT); expect(enemy?.getTag(BattlerTagType.TAUNT)).toBeTruthy(); @@ -50,12 +50,12 @@ describe("Abilities - Oblivious", () => { }); it("should remove infatuation when gained", async () => { - game.override.ability(Abilities.OBLIVIOUS) + game.override + .ability(Abilities.OBLIVIOUS) .enemyAbility(Abilities.BALL_FETCH) .moveset(Moves.SKILL_SWAP) - .enemyMoveset(Moves.SPLASH), - - await game.classicMode.startBattle([ Species.FEEBAS ]); + .enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([Species.FEEBAS]); const enemy = game.scene.getEnemyPokemon(); vi.spyOn(enemy!, "isOppositeGender").mockReturnValue(true); enemy?.addTag(BattlerTagType.INFATUATED, 5, Moves.JUDGMENT, game.scene.getPlayerPokemon()?.id); // sourceID needs to be defined diff --git a/test/abilities/own_tempo.test.ts b/test/abilities/own_tempo.test.ts index 936b4311b20..b2f2c2f3030 100644 --- a/test/abilities/own_tempo.test.ts +++ b/test/abilities/own_tempo.test.ts @@ -23,9 +23,9 @@ describe("Abilities - Own Tempo", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([ Moves.SPLASH ]) + .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -33,12 +33,12 @@ describe("Abilities - Own Tempo", () => { }); it("should remove confusion when gained", async () => { - game.override.ability(Abilities.OWN_TEMPO) + game.override + .ability(Abilities.OWN_TEMPO) .enemyAbility(Abilities.BALL_FETCH) .moveset(Moves.SKILL_SWAP) - .enemyMoveset(Moves.SPLASH), - - await game.classicMode.startBattle([ Species.FEEBAS ]); + .enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([Species.FEEBAS]); const enemy = game.scene.getEnemyPokemon(); enemy?.addTag(BattlerTagType.CONFUSED); expect(enemy?.getTag(BattlerTagType.CONFUSED)).toBeTruthy(); diff --git a/test/abilities/parental_bond.test.ts b/test/abilities/parental_bond.test.ts index 2aa24e78d6e..d81486e7316 100644 --- a/test/abilities/parental_bond.test.ts +++ b/test/abilities/parental_bond.test.ts @@ -26,7 +26,7 @@ describe("Abilities - Parental Bond", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.disableCrits(); game.override.ability(Abilities.PARENTAL_BOND); game.override.enemySpecies(Species.SNORLAX); @@ -167,7 +167,7 @@ describe("Abilities - Parental Bond", () => { }); it("should not apply to multi-target moves", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.EARTHQUAKE]); game.override.passiveAbility(Abilities.LEVITATE); diff --git a/test/abilities/pastel_veil.test.ts b/test/abilities/pastel_veil.test.ts index 65e391b7c22..4ae9763c4a6 100644 --- a/test/abilities/pastel_veil.test.ts +++ b/test/abilities/pastel_veil.test.ts @@ -26,7 +26,7 @@ describe("Abilities - Pastel Veil", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("double") + .battleStyle("double") .moveset([Moves.TOXIC_THREAD, Moves.SPLASH]) .enemyAbility(Abilities.BALL_FETCH) .enemySpecies(Species.SUNKERN) diff --git a/test/abilities/perish_body.test.ts b/test/abilities/perish_body.test.ts index 424d35e2542..27e76cb52ad 100644 --- a/test/abilities/perish_body.test.ts +++ b/test/abilities/perish_body.test.ts @@ -21,7 +21,7 @@ describe("Abilities - Perish Song", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.disableCrits(); game.override.enemySpecies(Species.MAGIKARP); diff --git a/test/abilities/power_construct.test.ts b/test/abilities/power_construct.test.ts index c253f2ae4df..0ff90a2c0df 100644 --- a/test/abilities/power_construct.test.ts +++ b/test/abilities/power_construct.test.ts @@ -25,7 +25,7 @@ describe("Abilities - POWER CONSTRUCT", () => { beforeEach(() => { game = new GameManager(phaserGame); const moveToUse = Moves.SPLASH; - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.ability(Abilities.POWER_CONSTRUCT); game.override.moveset([moveToUse]); game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); diff --git a/test/abilities/power_spot.test.ts b/test/abilities/power_spot.test.ts index e29b5ecf775..3e4f79d7445 100644 --- a/test/abilities/power_spot.test.ts +++ b/test/abilities/power_spot.test.ts @@ -26,7 +26,7 @@ describe("Abilities - Power Spot", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.TACKLE, Moves.BREAKING_SWIPE, Moves.SPLASH, Moves.DAZZLING_GLEAM]); game.override.enemyMoveset(Moves.SPLASH); game.override.enemySpecies(Species.SHUCKLE); diff --git a/test/abilities/protean.test.ts b/test/abilities/protean.test.ts index 574033bb13f..efa6f33fe00 100644 --- a/test/abilities/protean.test.ts +++ b/test/abilities/protean.test.ts @@ -29,7 +29,7 @@ describe("Abilities - Protean", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.ability(Abilities.PROTEAN); game.override.startingLevel(100); game.override.enemySpecies(Species.RATTATA); diff --git a/test/abilities/protosynthesis.test.ts b/test/abilities/protosynthesis.test.ts index 882474b7cef..e312ebd572c 100644 --- a/test/abilities/protosynthesis.test.ts +++ b/test/abilities/protosynthesis.test.ts @@ -27,7 +27,7 @@ describe("Abilities - Protosynthesis", () => { game.override .moveset([Moves.SPLASH, Moves.TACKLE]) .ability(Abilities.PROTOSYNTHESIS) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/quick_draw.test.ts b/test/abilities/quick_draw.test.ts index 1277fd5d3cb..0d3171e947e 100644 --- a/test/abilities/quick_draw.test.ts +++ b/test/abilities/quick_draw.test.ts @@ -24,7 +24,7 @@ describe("Abilities - Quick Draw", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.starterSpecies(Species.MAGIKARP); game.override.ability(Abilities.QUICK_DRAW); diff --git a/test/abilities/sand_spit.test.ts b/test/abilities/sand_spit.test.ts index 6896c286eed..2b655f92466 100644 --- a/test/abilities/sand_spit.test.ts +++ b/test/abilities/sand_spit.test.ts @@ -22,7 +22,7 @@ describe("Abilities - Sand Spit", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.disableCrits(); game.override.enemySpecies(Species.MAGIKARP); diff --git a/test/abilities/sand_veil.test.ts b/test/abilities/sand_veil.test.ts index c7b12a11c0e..b82c79c681b 100644 --- a/test/abilities/sand_veil.test.ts +++ b/test/abilities/sand_veil.test.ts @@ -34,7 +34,7 @@ describe("Abilities - Sand Veil", () => { game.override.enemyMoveset([Moves.TWISTER, Moves.TWISTER, Moves.TWISTER, Moves.TWISTER]); game.override.startingLevel(100); game.override.enemyLevel(100); - game.override.weather(WeatherType.SANDSTORM).battleType("double"); + game.override.weather(WeatherType.SANDSTORM).battleStyle("double"); }); test("ability should increase the evasiveness of the source", async () => { diff --git a/test/abilities/sap_sipper.test.ts b/test/abilities/sap_sipper.test.ts index f4f02844cbc..2157177b84c 100644 --- a/test/abilities/sap_sipper.test.ts +++ b/test/abilities/sap_sipper.test.ts @@ -29,7 +29,7 @@ describe("Abilities - Sap Sipper", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .disableCrits() .ability(Abilities.SAP_SIPPER) .enemySpecies(Species.RATTATA) diff --git a/test/abilities/schooling.test.ts b/test/abilities/schooling.test.ts index 35244b08e4c..803b4d2062a 100644 --- a/test/abilities/schooling.test.ts +++ b/test/abilities/schooling.test.ts @@ -25,7 +25,7 @@ describe("Abilities - SCHOOLING", () => { beforeEach(() => { game = new GameManager(phaserGame); const moveToUse = Moves.SPLASH; - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.ability(Abilities.SCHOOLING); game.override.moveset([moveToUse]); game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); diff --git a/test/abilities/screen_cleaner.test.ts b/test/abilities/screen_cleaner.test.ts index d8be1d64697..840291f6420 100644 --- a/test/abilities/screen_cleaner.test.ts +++ b/test/abilities/screen_cleaner.test.ts @@ -24,7 +24,7 @@ describe("Abilities - Screen Cleaner", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.ability(Abilities.SCREEN_CLEANER); game.override.enemySpecies(Species.SHUCKLE); }); diff --git a/test/abilities/seed_sower.test.ts b/test/abilities/seed_sower.test.ts index d78007f7500..d8edbe59857 100644 --- a/test/abilities/seed_sower.test.ts +++ b/test/abilities/seed_sower.test.ts @@ -22,7 +22,7 @@ describe("Abilities - Seed Sower", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.disableCrits(); game.override.enemySpecies(Species.MAGIKARP); diff --git a/test/abilities/serene_grace.test.ts b/test/abilities/serene_grace.test.ts index 65ca96acbbc..2547971a4b8 100644 --- a/test/abilities/serene_grace.test.ts +++ b/test/abilities/serene_grace.test.ts @@ -26,7 +26,7 @@ describe("Abilities - Serene Grace", () => { game = new GameManager(phaserGame); game.override .disableCrits() - .battleType("single") + .battleStyle("single") .ability(Abilities.SERENE_GRACE) .moveset([Moves.AIR_SLASH]) .enemySpecies(Species.ALOLA_GEODUDE) diff --git a/test/abilities/sheer_force.test.ts b/test/abilities/sheer_force.test.ts index fae089958a5..ce3232a1869 100644 --- a/test/abilities/sheer_force.test.ts +++ b/test/abilities/sheer_force.test.ts @@ -26,7 +26,7 @@ describe("Abilities - Sheer Force", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .ability(Abilities.SHEER_FORCE) .enemySpecies(Species.ONIX) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/shield_dust.test.ts b/test/abilities/shield_dust.test.ts index 257ebe885df..4f6783eb66a 100644 --- a/test/abilities/shield_dust.test.ts +++ b/test/abilities/shield_dust.test.ts @@ -31,7 +31,7 @@ describe("Abilities - Shield Dust", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.ONIX); game.override.enemyAbility(Abilities.SHIELD_DUST); game.override.startingLevel(100); diff --git a/test/abilities/shields_down.test.ts b/test/abilities/shields_down.test.ts index 4bdf22869cb..2f9d2fb1f97 100644 --- a/test/abilities/shields_down.test.ts +++ b/test/abilities/shields_down.test.ts @@ -26,7 +26,7 @@ describe("Abilities - SHIELDS DOWN", () => { beforeEach(() => { game = new GameManager(phaserGame); const moveToUse = Moves.SPLASH; - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.ability(Abilities.SHIELDS_DOWN); game.override.moveset([moveToUse]); game.override.enemyMoveset([Moves.TACKLE]); diff --git a/test/abilities/simple.test.ts b/test/abilities/simple.test.ts index b6c5fd116c0..1f084b1bf4c 100644 --- a/test/abilities/simple.test.ts +++ b/test/abilities/simple.test.ts @@ -23,7 +23,7 @@ describe("Abilities - Simple", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.BULBASAUR) .enemyAbility(Abilities.SIMPLE) .ability(Abilities.INTIMIDATE) diff --git a/test/abilities/speed_boost.test.ts b/test/abilities/speed_boost.test.ts index fa20e74108f..45ee54ffb07 100644 --- a/test/abilities/speed_boost.test.ts +++ b/test/abilities/speed_boost.test.ts @@ -27,7 +27,7 @@ describe("Abilities - Speed Boost", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.SHUCKLE) .enemyAbility(Abilities.BALL_FETCH) .enemyLevel(100) diff --git a/test/abilities/stakeout.test.ts b/test/abilities/stakeout.test.ts index b464b3f1dfc..b3a7bdbf287 100644 --- a/test/abilities/stakeout.test.ts +++ b/test/abilities/stakeout.test.ts @@ -26,7 +26,7 @@ describe("Abilities - Stakeout", () => { game.override .moveset([Moves.SPLASH, Moves.SURF]) .ability(Abilities.STAKEOUT) - .battleType("single") + .battleStyle("single") .disableCrits() .startingLevel(100) .enemyLevel(100) diff --git a/test/abilities/stall.test.ts b/test/abilities/stall.test.ts index 5b67e5f4b7a..68b3fdedcd8 100644 --- a/test/abilities/stall.test.ts +++ b/test/abilities/stall.test.ts @@ -22,7 +22,7 @@ describe("Abilities - Stall", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.disableCrits(); game.override.enemySpecies(Species.REGIELEKI); game.override.enemyAbility(Abilities.STALL); diff --git a/test/abilities/steely_spirit.test.ts b/test/abilities/steely_spirit.test.ts index eb5e7aac601..be759724c3a 100644 --- a/test/abilities/steely_spirit.test.ts +++ b/test/abilities/steely_spirit.test.ts @@ -28,7 +28,7 @@ describe("Abilities - Steely Spirit", () => { beforeEach(() => { ironHeadPower = allMoves[moveToCheck].power; game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.enemySpecies(Species.SHUCKLE); game.override.enemyAbility(Abilities.BALL_FETCH); game.override.moveset([Moves.IRON_HEAD, Moves.SPLASH]); diff --git a/test/abilities/storm_drain.test.ts b/test/abilities/storm_drain.test.ts index 58ff477fa43..0cbad796ad8 100644 --- a/test/abilities/storm_drain.test.ts +++ b/test/abilities/storm_drain.test.ts @@ -26,7 +26,7 @@ describe("Abilities - Storm Drain", () => { game.override .moveset([Moves.SPLASH, Moves.WATER_GUN]) .ability(Abilities.BALL_FETCH) - .battleType("double") + .battleStyle("double") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/sturdy.test.ts b/test/abilities/sturdy.test.ts index 7b7254cff15..bda8c6d1e35 100644 --- a/test/abilities/sturdy.test.ts +++ b/test/abilities/sturdy.test.ts @@ -24,7 +24,7 @@ describe("Abilities - Sturdy", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.starterSpecies(Species.LUCARIO); game.override.startingLevel(100); diff --git a/test/abilities/super_luck.test.ts b/test/abilities/super_luck.test.ts index bc9524de801..9e0b6485734 100644 --- a/test/abilities/super_luck.test.ts +++ b/test/abilities/super_luck.test.ts @@ -24,7 +24,7 @@ describe("Abilities - Super Luck", () => { game.override .moveset([Moves.TACKLE]) .ability(Abilities.SUPER_LUCK) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/supreme_overlord.test.ts b/test/abilities/supreme_overlord.test.ts index a71bf0a9354..8af0a0ac37c 100644 --- a/test/abilities/supreme_overlord.test.ts +++ b/test/abilities/supreme_overlord.test.ts @@ -31,7 +31,7 @@ describe("Abilities - Supreme Overlord", () => { basePower = move.power; game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MAGIKARP) .enemyLevel(100) .startingLevel(1) diff --git a/test/abilities/sweet_veil.test.ts b/test/abilities/sweet_veil.test.ts index 650ee53a474..e609aa6e7d2 100644 --- a/test/abilities/sweet_veil.test.ts +++ b/test/abilities/sweet_veil.test.ts @@ -25,7 +25,7 @@ describe("Abilities - Sweet Veil", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.SPLASH, Moves.REST, Moves.YAWN]); game.override.enemySpecies(Species.MAGIKARP); game.override.enemyAbility(Abilities.BALL_FETCH); diff --git a/test/abilities/synchronize.test.ts b/test/abilities/synchronize.test.ts index 95ebf96f2fd..783201d7a5b 100644 --- a/test/abilities/synchronize.test.ts +++ b/test/abilities/synchronize.test.ts @@ -24,7 +24,7 @@ describe("Abilities - Synchronize", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .startingLevel(100) .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.SYNCHRONIZE) diff --git a/test/abilities/tera_shell.test.ts b/test/abilities/tera_shell.test.ts index a99ecfd4ce1..c387da30166 100644 --- a/test/abilities/tera_shell.test.ts +++ b/test/abilities/tera_shell.test.ts @@ -24,7 +24,7 @@ describe("Abilities - Tera Shell", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .ability(Abilities.TERA_SHELL) .moveset([Moves.SPLASH]) .enemySpecies(Species.SNORLAX) diff --git a/test/abilities/thermal_exchange.test.ts b/test/abilities/thermal_exchange.test.ts index 124c1dba286..c33b296d5ae 100644 --- a/test/abilities/thermal_exchange.test.ts +++ b/test/abilities/thermal_exchange.test.ts @@ -23,9 +23,9 @@ describe("Abilities - Thermal Exchange", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([ Moves.SPLASH ]) + .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -33,12 +33,12 @@ describe("Abilities - Thermal Exchange", () => { }); it("should remove burn when gained", async () => { - game.override.ability(Abilities.THERMAL_EXCHANGE) + game.override + .ability(Abilities.THERMAL_EXCHANGE) .enemyAbility(Abilities.BALL_FETCH) .moveset(Moves.SKILL_SWAP) - .enemyMoveset(Moves.SPLASH), - - await game.classicMode.startBattle([ Species.FEEBAS ]); + .enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([Species.FEEBAS]); const enemy = game.scene.getEnemyPokemon(); enemy?.trySetStatus(StatusEffect.BURN); expect(enemy?.status?.effect).toBe(StatusEffect.BURN); diff --git a/test/abilities/trace.test.ts b/test/abilities/trace.test.ts index 5d569208d33..7ec8d62ab51 100644 --- a/test/abilities/trace.test.ts +++ b/test/abilities/trace.test.ts @@ -25,7 +25,7 @@ describe("Abilities - Trace", () => { game.override .moveset([Moves.SPLASH]) .ability(Abilities.TRACE) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/unburden.test.ts b/test/abilities/unburden.test.ts index 769e078faf8..2af889d1da4 100644 --- a/test/abilities/unburden.test.ts +++ b/test/abilities/unburden.test.ts @@ -41,7 +41,7 @@ describe("Abilities - Unburden", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .startingLevel(1) .ability(Abilities.UNBURDEN) .moveset([Moves.SPLASH, Moves.KNOCK_OFF, Moves.PLUCK, Moves.FALSE_SWIPE]) @@ -231,7 +231,7 @@ describe("Abilities - Unburden", () => { }); it("should deactivate temporarily when a neutralizing gas user is on the field", async () => { - game.override.battleType("double").ability(Abilities.NONE); // Disable ability override so that we can properly set abilities below + game.override.battleStyle("double").ability(Abilities.NONE); // Disable ability override so that we can properly set abilities below await game.classicMode.startBattle([Species.TREECKO, Species.MEOWTH, Species.WEEZING]); const [treecko, _meowth, weezing] = game.scene.getPlayerParty(); @@ -359,7 +359,7 @@ describe("Abilities - Unburden", () => { // test for `.bypassFaint()` - doubles it("shouldn't persist when revived by revival blessing if activated while fainting", async () => { game.override - .battleType("double") + .battleStyle("double") .enemyMoveset([Moves.SPLASH, Moves.THIEF]) .moveset([Moves.SPLASH, Moves.REVIVAL_BLESSING]) .startingHeldItems([{ name: "WIDE_LENS" }]); diff --git a/test/abilities/unseen_fist.test.ts b/test/abilities/unseen_fist.test.ts index 459bb00628c..6c14e82fc39 100644 --- a/test/abilities/unseen_fist.test.ts +++ b/test/abilities/unseen_fist.test.ts @@ -24,7 +24,7 @@ describe("Abilities - Unseen Fist", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.starterSpecies(Species.URSHIFU); game.override.enemySpecies(Species.SNORLAX); game.override.enemyMoveset([Moves.PROTECT, Moves.PROTECT, Moves.PROTECT, Moves.PROTECT]); diff --git a/test/abilities/victory_star.test.ts b/test/abilities/victory_star.test.ts index 92db522871a..f3c0b5ad6b7 100644 --- a/test/abilities/victory_star.test.ts +++ b/test/abilities/victory_star.test.ts @@ -25,7 +25,7 @@ describe("Abilities - Victory Star", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.TACKLE, Moves.SPLASH]) - .battleType("double") + .battleStyle("double") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/vital_spirit.test.ts b/test/abilities/vital_spirit.test.ts index 3a53c3f520e..bb274310cc0 100644 --- a/test/abilities/vital_spirit.test.ts +++ b/test/abilities/vital_spirit.test.ts @@ -23,9 +23,9 @@ describe("Abilities - Vital Spirit", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([ Moves.SPLASH ]) + .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -33,12 +33,12 @@ describe("Abilities - Vital Spirit", () => { }); it("should remove sleep when gained", async () => { - game.override.ability(Abilities.INSOMNIA) + game.override + .ability(Abilities.INSOMNIA) .enemyAbility(Abilities.BALL_FETCH) .moveset(Moves.SKILL_SWAP) - .enemyMoveset(Moves.SPLASH), - - await game.classicMode.startBattle([ Species.FEEBAS ]); + .enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([Species.FEEBAS]); const enemy = game.scene.getEnemyPokemon(); enemy?.trySetStatus(StatusEffect.SLEEP); expect(enemy?.status?.effect).toBe(StatusEffect.SLEEP); diff --git a/test/abilities/volt_absorb.test.ts b/test/abilities/volt_absorb.test.ts index 10735f31987..920c822eb90 100644 --- a/test/abilities/volt_absorb.test.ts +++ b/test/abilities/volt_absorb.test.ts @@ -26,7 +26,7 @@ describe("Abilities - Volt Absorb", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.disableCrits(); }); diff --git a/test/abilities/wandering_spirit.test.ts b/test/abilities/wandering_spirit.test.ts index 375faa41972..639241aecc8 100644 --- a/test/abilities/wandering_spirit.test.ts +++ b/test/abilities/wandering_spirit.test.ts @@ -25,7 +25,7 @@ describe("Abilities - Wandering Spirit", () => { game.override .moveset([Moves.SPLASH]) .ability(Abilities.WANDERING_SPIRIT) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/water_bubble.test.ts b/test/abilities/water_bubble.test.ts index 0b85a5814da..c1e2acbd468 100644 --- a/test/abilities/water_bubble.test.ts +++ b/test/abilities/water_bubble.test.ts @@ -23,9 +23,9 @@ describe("Abilities - Water Bubble", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([ Moves.SPLASH ]) + .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -33,12 +33,12 @@ describe("Abilities - Water Bubble", () => { }); it("should remove burn when gained", async () => { - game.override.ability(Abilities.THERMAL_EXCHANGE) + game.override + .ability(Abilities.THERMAL_EXCHANGE) .enemyAbility(Abilities.BALL_FETCH) .moveset(Moves.SKILL_SWAP) - .enemyMoveset(Moves.SPLASH), - - await game.classicMode.startBattle([ Species.FEEBAS ]); + .enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([Species.FEEBAS]); const enemy = game.scene.getEnemyPokemon(); enemy?.trySetStatus(StatusEffect.BURN); expect(enemy?.status?.effect).toBe(StatusEffect.BURN); diff --git a/test/abilities/water_veil.test.ts b/test/abilities/water_veil.test.ts index 38c9a05600b..8e187ad8e58 100644 --- a/test/abilities/water_veil.test.ts +++ b/test/abilities/water_veil.test.ts @@ -23,9 +23,9 @@ describe("Abilities - Water Veil", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([ Moves.SPLASH ]) + .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -33,12 +33,12 @@ describe("Abilities - Water Veil", () => { }); it("should remove burn when gained", async () => { - game.override.ability(Abilities.THERMAL_EXCHANGE) + game.override + .ability(Abilities.THERMAL_EXCHANGE) .enemyAbility(Abilities.BALL_FETCH) .moveset(Moves.SKILL_SWAP) - .enemyMoveset(Moves.SPLASH), - - await game.classicMode.startBattle([ Species.FEEBAS ]); + .enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([Species.FEEBAS]); const enemy = game.scene.getEnemyPokemon(); enemy?.trySetStatus(StatusEffect.BURN); expect(enemy?.status?.effect).toBe(StatusEffect.BURN); diff --git a/test/abilities/wimp_out.test.ts b/test/abilities/wimp_out.test.ts index 294025a10e7..c46675376c1 100644 --- a/test/abilities/wimp_out.test.ts +++ b/test/abilities/wimp_out.test.ts @@ -31,7 +31,7 @@ describe("Abilities - Wimp Out", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .ability(Abilities.WIMP_OUT) .enemySpecies(Species.NINJASK) .enemyPassiveAbility(Abilities.NO_GUARD) @@ -342,7 +342,7 @@ describe("Abilities - Wimp Out", () => { }); it("Wimp Out activating should not cancel a double battle", async () => { - game.override.battleType("double").enemyAbility(Abilities.WIMP_OUT).enemyMoveset([Moves.SPLASH]).enemyLevel(1); + game.override.battleStyle("double").enemyAbility(Abilities.WIMP_OUT).enemyMoveset([Moves.SPLASH]).enemyLevel(1); await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]); const enemyLeadPokemon = game.scene.getEnemyParty()[0]; const enemySecPokemon = game.scene.getEnemyParty()[1]; @@ -508,7 +508,7 @@ describe("Abilities - Wimp Out", () => { .moveset([Moves.MATCHA_GOTCHA, Moves.FALSE_SWIPE]) .startingLevel(50) .enemyLevel(1) - .battleType("double") + .battleStyle("double") .startingWave(wave); await game.classicMode.startBattle([Species.RAICHU, Species.PIKACHU]); const [wimpod0, wimpod1] = game.scene.getEnemyField(); @@ -534,12 +534,12 @@ describe("Abilities - Wimp Out", () => { .enemyAbility(Abilities.WIMP_OUT) .startingLevel(50) .enemyLevel(1) - .enemyMoveset([ Moves.SPLASH, Moves.ENDURE ]) - .battleType("double") - .moveset([ Moves.DRAGON_ENERGY, Moves.SPLASH ]) + .enemyMoveset([Moves.SPLASH, Moves.ENDURE]) + .battleStyle("double") + .moveset([Moves.DRAGON_ENERGY, Moves.SPLASH]) .startingWave(wave); - await game.classicMode.startBattle([ Species.REGIDRAGO, Species.MAGIKARP ]); + await game.classicMode.startBattle([Species.REGIDRAGO, Species.MAGIKARP]); // turn 1 game.move.select(Moves.DRAGON_ENERGY, 0); @@ -549,6 +549,5 @@ describe("Abilities - Wimp Out", () => { await game.phaseInterceptor.to("SelectModifierPhase"); expect(game.scene.currentBattle.waveIndex).toBe(wave + 1); - }); }); diff --git a/test/abilities/wind_power.test.ts b/test/abilities/wind_power.test.ts index b28ac3362eb..66c72d454ab 100644 --- a/test/abilities/wind_power.test.ts +++ b/test/abilities/wind_power.test.ts @@ -23,7 +23,7 @@ describe("Abilities - Wind Power", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.SHIFTRY); game.override.enemyAbility(Abilities.WIND_POWER); game.override.moveset([Moves.TAILWIND, Moves.SPLASH, Moves.PETAL_BLIZZARD, Moves.SANDSTORM]); diff --git a/test/abilities/wind_rider.test.ts b/test/abilities/wind_rider.test.ts index 8fdae1b24ec..f8301aa03fc 100644 --- a/test/abilities/wind_rider.test.ts +++ b/test/abilities/wind_rider.test.ts @@ -23,7 +23,7 @@ describe("Abilities - Wind Rider", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.SHIFTRY) .enemyAbility(Abilities.WIND_RIDER) .moveset([Moves.TAILWIND, Moves.SPLASH, Moves.PETAL_BLIZZARD, Moves.SANDSTORM]) diff --git a/test/abilities/wonder_skin.test.ts b/test/abilities/wonder_skin.test.ts index 18d5be36aef..d039ba1e6a7 100644 --- a/test/abilities/wonder_skin.test.ts +++ b/test/abilities/wonder_skin.test.ts @@ -23,7 +23,7 @@ describe("Abilities - Wonder Skin", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.moveset([Moves.TACKLE, Moves.CHARM]); game.override.ability(Abilities.BALL_FETCH); game.override.enemySpecies(Species.SHUCKLE); diff --git a/test/abilities/zen_mode.test.ts b/test/abilities/zen_mode.test.ts index d552d8c88ca..1eb27a8f6c7 100644 --- a/test/abilities/zen_mode.test.ts +++ b/test/abilities/zen_mode.test.ts @@ -26,7 +26,7 @@ describe("Abilities - ZEN MODE", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/abilities/zero_to_hero.test.ts b/test/abilities/zero_to_hero.test.ts index 4565aa3e8b2..2cdc516dc6b 100644 --- a/test/abilities/zero_to_hero.test.ts +++ b/test/abilities/zero_to_hero.test.ts @@ -27,7 +27,7 @@ describe("Abilities - ZERO TO HERO", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .moveset(Moves.SPLASH) .enemyMoveset(Moves.SPLASH) .enemyAbility(Abilities.BALL_FETCH); diff --git a/test/arena/arena_gravity.test.ts b/test/arena/arena_gravity.test.ts index a5ce84667f0..0ce5ac0ea4c 100644 --- a/test/arena/arena_gravity.test.ts +++ b/test/arena/arena_gravity.test.ts @@ -26,7 +26,7 @@ describe("Arena - Gravity", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .moveset([Moves.TACKLE, Moves.GRAVITY, Moves.FISSURE]) .ability(Abilities.UNNERVE) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/arena/grassy_terrain.test.ts b/test/arena/grassy_terrain.test.ts index d92fb24be5a..f8ca07bd65e 100644 --- a/test/arena/grassy_terrain.test.ts +++ b/test/arena/grassy_terrain.test.ts @@ -22,7 +22,7 @@ describe("Arena - Grassy Terrain", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .disableCrits() .enemyLevel(1) .enemySpecies(Species.SHUCKLE) diff --git a/test/arena/weather_fog.test.ts b/test/arena/weather_fog.test.ts index 784c4886648..b1edf75704b 100644 --- a/test/arena/weather_fog.test.ts +++ b/test/arena/weather_fog.test.ts @@ -24,7 +24,7 @@ describe("Weather - Fog", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.weather(WeatherType.FOG).battleType("single"); + game.override.weather(WeatherType.FOG).battleStyle("single"); game.override.moveset([Moves.TACKLE]); game.override.ability(Abilities.BALL_FETCH); game.override.enemyAbility(Abilities.BALL_FETCH); diff --git a/test/arena/weather_hail.test.ts b/test/arena/weather_hail.test.ts index 7af2edf26f2..2fa4f71d8ca 100644 --- a/test/arena/weather_hail.test.ts +++ b/test/arena/weather_hail.test.ts @@ -24,7 +24,7 @@ describe("Weather - Hail", () => { game = new GameManager(phaserGame); game.override .weather(WeatherType.HAIL) - .battleType("single") + .battleStyle("single") .moveset(Moves.SPLASH) .enemyMoveset(Moves.SPLASH) .enemySpecies(Species.MAGIKARP); diff --git a/test/arena/weather_sandstorm.test.ts b/test/arena/weather_sandstorm.test.ts index d43983c4c01..e7620f6cf30 100644 --- a/test/arena/weather_sandstorm.test.ts +++ b/test/arena/weather_sandstorm.test.ts @@ -25,7 +25,7 @@ describe("Weather - Sandstorm", () => { game = new GameManager(phaserGame); game.override .weather(WeatherType.SANDSTORM) - .battleType("single") + .battleStyle("single") .moveset(Moves.SPLASH) .enemyMoveset(Moves.SPLASH) .enemySpecies(Species.MAGIKARP); @@ -60,7 +60,7 @@ describe("Weather - Sandstorm", () => { it("does not inflict damage to Rock, Ground and Steel type Pokemon", async () => { game.override - .battleType("double") + .battleStyle("double") .enemySpecies(Species.SANDSHREW) .ability(Abilities.BALL_FETCH) .enemyAbility(Abilities.BALL_FETCH); diff --git a/test/arena/weather_strong_winds.test.ts b/test/arena/weather_strong_winds.test.ts index 3a9235d9eb9..9fcdb18c872 100644 --- a/test/arena/weather_strong_winds.test.ts +++ b/test/arena/weather_strong_winds.test.ts @@ -24,7 +24,7 @@ describe("Weather - Strong Winds", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.startingLevel(10); game.override.enemySpecies(Species.TAILLOW); game.override.enemyAbility(Abilities.DELTA_STREAM); diff --git a/test/battle/ability_swap.test.ts b/test/battle/ability_swap.test.ts index 215321f26c2..c9f91df3a48 100644 --- a/test/battle/ability_swap.test.ts +++ b/test/battle/ability_swap.test.ts @@ -26,7 +26,7 @@ describe("Test Ability Swapping", () => { game.override .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/battle/battle-order.test.ts b/test/battle/battle-order.test.ts index 012f1ecd4bd..43fa1e59c14 100644 --- a/test/battle/battle-order.test.ts +++ b/test/battle/battle-order.test.ts @@ -24,7 +24,7 @@ describe("Battle order", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.MEWTWO); game.override.enemyAbility(Abilities.INSOMNIA); game.override.ability(Abilities.INSOMNIA); @@ -70,7 +70,7 @@ describe("Battle order", () => { }, 20000); it("double - both opponents faster than player 50/50 vs 150/150", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.startBattle([Species.BULBASAUR, Species.BLASTOISE]); const playerPokemon = game.scene.getPlayerField(); @@ -94,7 +94,7 @@ describe("Battle order", () => { }, 20000); it("double - speed tie except 1 - 100/100 vs 100/150", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.startBattle([Species.BULBASAUR, Species.BLASTOISE]); const playerPokemon = game.scene.getPlayerField(); @@ -118,7 +118,7 @@ describe("Battle order", () => { }, 20000); it("double - speed tie 100/150 vs 100/150", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.startBattle([Species.BULBASAUR, Species.BLASTOISE]); const playerPokemon = game.scene.getPlayerField(); diff --git a/test/battle/battle.test.ts b/test/battle/battle.test.ts index 36d197d1289..51304c7d5dd 100644 --- a/test/battle/battle.test.ts +++ b/test/battle/battle.test.ts @@ -94,7 +94,7 @@ describe("Test Battle Phase", () => { game.override.starterSpecies(Species.MEWTWO); game.override.enemySpecies(Species.RATTATA); game.override.startingLevel(2000); - game.override.startingWave(3).battleType("single"); + game.override.startingWave(3).battleStyle("single"); game.override.moveset([Moves.TACKLE]); game.override.enemyAbility(Abilities.HYDRATION); game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); @@ -111,7 +111,7 @@ describe("Test Battle Phase", () => { game.override.moveset([Moves.TACKLE]); game.override.enemyAbility(Abilities.HYDRATION); game.override.enemyMoveset([Moves.TAIL_WHIP, Moves.TAIL_WHIP, Moves.TAIL_WHIP, Moves.TAIL_WHIP]); - game.override.battleType("single"); + game.override.battleStyle("single"); await game.startBattle(); game.move.select(Moves.TACKLE); await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase, false); @@ -203,7 +203,7 @@ describe("Test Battle Phase", () => { }, 20000); it("2vs1", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.MIGHTYENA); game.override.enemyAbility(Abilities.HYDRATION); game.override.ability(Abilities.HYDRATION); @@ -213,7 +213,7 @@ describe("Test Battle Phase", () => { }, 20000); it("1vs1", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.MIGHTYENA); game.override.enemyAbility(Abilities.HYDRATION); game.override.ability(Abilities.HYDRATION); @@ -223,7 +223,7 @@ describe("Test Battle Phase", () => { }, 20000); it("2vs2", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.enemySpecies(Species.MIGHTYENA); game.override.enemyAbility(Abilities.HYDRATION); game.override.ability(Abilities.HYDRATION); @@ -234,7 +234,7 @@ describe("Test Battle Phase", () => { }, 20000); it("4vs2", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.enemySpecies(Species.MIGHTYENA); game.override.enemyAbility(Abilities.HYDRATION); game.override.ability(Abilities.HYDRATION); @@ -246,7 +246,7 @@ describe("Test Battle Phase", () => { it("kill opponent pokemon", async () => { const moveToUse = Moves.SPLASH; - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.starterSpecies(Species.MEWTWO); game.override.enemySpecies(Species.RATTATA); game.override.enemyAbility(Abilities.HYDRATION); @@ -266,7 +266,7 @@ describe("Test Battle Phase", () => { it("to next turn", async () => { const moveToUse = Moves.SPLASH; - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.starterSpecies(Species.MEWTWO); game.override.enemySpecies(Species.RATTATA); game.override.enemyAbility(Abilities.HYDRATION); @@ -285,7 +285,7 @@ describe("Test Battle Phase", () => { it("does not set new weather if staying in same biome", async () => { const moveToUse = Moves.SPLASH; game.override - .battleType("single") + .battleStyle("single") .starterSpecies(Species.MEWTWO) .enemySpecies(Species.RATTATA) .enemyAbility(Abilities.HYDRATION) @@ -309,7 +309,7 @@ describe("Test Battle Phase", () => { it("does not force switch if active pokemon faints at same time as enemy mon and is revived in post-battle", async () => { const moveToUse = Moves.TAKE_DOWN; game.override - .battleType("single") + .battleStyle("single") .starterSpecies(Species.SAWK) .enemySpecies(Species.RATTATA) .startingWave(1) diff --git a/test/battle/damage_calculation.test.ts b/test/battle/damage_calculation.test.ts index dab1fc81caa..e8b3b65bd29 100644 --- a/test/battle/damage_calculation.test.ts +++ b/test/battle/damage_calculation.test.ts @@ -26,7 +26,7 @@ describe("Battle Mechanics - Damage Calculation", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) diff --git a/test/battle/double_battle.test.ts b/test/battle/double_battle.test.ts index 21d27573d22..a30d55aac3d 100644 --- a/test/battle/double_battle.test.ts +++ b/test/battle/double_battle.test.ts @@ -33,7 +33,7 @@ describe("Double Battles", () => { // double-battle player's pokemon both fainted in same round, then revive one, and next double battle summons two player's pokemon successfully. // (There were bugs that either only summon one when can summon two, player stuck in switchPhase etc) it("3v2 edge case: player summons 2 pokemon on the next battle after being fainted and revived", async () => { - game.override.battleType("double").enemyMoveset(Moves.SPLASH).moveset(Moves.SPLASH); + game.override.battleStyle("double").enemyMoveset(Moves.SPLASH).moveset(Moves.SPLASH); await game.startBattle([Species.BULBASAUR, Species.CHARIZARD, Species.SQUIRTLE]); game.move.select(Moves.SPLASH); diff --git a/test/battle/inverse_battle.test.ts b/test/battle/inverse_battle.test.ts index 83109c35740..f8afa3518a9 100644 --- a/test/battle/inverse_battle.test.ts +++ b/test/battle/inverse_battle.test.ts @@ -30,7 +30,7 @@ describe("Inverse Battle", () => { game.challengeMode.addChallenge(Challenges.INVERSE_BATTLE, 1, 1); game.override - .battleType("single") + .battleStyle("single") .starterSpecies(Species.FEEBAS) .ability(Abilities.BALL_FETCH) .enemySpecies(Species.MAGIKARP) diff --git a/test/battle/special_battle.test.ts b/test/battle/special_battle.test.ts index cf7f3733484..46dd8eaa010 100644 --- a/test/battle/special_battle.test.ts +++ b/test/battle/special_battle.test.ts @@ -32,63 +32,63 @@ describe("Test Battle Phase", () => { }); it("startBattle 2vs1 boss", async () => { - game.override.battleType("single").startingWave(10); + game.override.battleStyle("single").startingWave(10); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 boss", async () => { - game.override.battleType("double").startingWave(10); + game.override.battleStyle("double").startingWave(10); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 trainer", async () => { - game.override.battleType("double").startingWave(5); + game.override.battleStyle("double").startingWave(5); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs1 trainer", async () => { - game.override.battleType("single").startingWave(5); + game.override.battleStyle("single").startingWave(5); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs1 rival", async () => { - game.override.battleType("single").startingWave(8); + game.override.battleStyle("single").startingWave(8); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 rival", async () => { - game.override.battleType("double").startingWave(8); + game.override.battleStyle("double").startingWave(8); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 1vs1 trainer", async () => { - game.override.battleType("single").startingWave(5); + game.override.battleStyle("single").startingWave(5); await game.startBattle([Species.BLASTOISE]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 trainer", async () => { - game.override.battleType("double").startingWave(5); + game.override.battleStyle("double").startingWave(5); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 4vs2 trainer", async () => { - game.override.battleType("double").startingWave(5); + game.override.battleStyle("double").startingWave(5); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD, Species.DARKRAI, Species.GABITE]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); diff --git a/test/boss-pokemon.test.ts b/test/boss-pokemon.test.ts index 6b150de2d2b..9df69da09b7 100644 --- a/test/boss-pokemon.test.ts +++ b/test/boss-pokemon.test.ts @@ -26,7 +26,7 @@ describe("Boss Pokemon / Shields", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .disableTrainerWaves() .disableCrits() .enemySpecies(Species.RATTATA) @@ -63,7 +63,7 @@ describe("Boss Pokemon / Shields", () => { }); it("should reduce the number of shields if we are in a double battle", async () => { - game.override.battleType("double").startingWave(150); // Floor 150 > 2 shields / 3 health segments + game.override.battleStyle("double").startingWave(150); // Floor 150 > 2 shields / 3 health segments await game.classicMode.startBattle([Species.MEWTWO]); @@ -105,7 +105,7 @@ describe("Boss Pokemon / Shields", () => { }); it("breaking multiple shields at once requires extra damage", async () => { - game.override.battleType("double").enemyHealthSegments(5); + game.override.battleStyle("double").enemyHealthSegments(5); await game.classicMode.startBattle([Species.MEWTWO]); @@ -140,7 +140,7 @@ describe("Boss Pokemon / Shields", () => { it("the number of stat stage boosts is consistent when several shields are broken at once", async () => { const shieldsToBreak = 4; - game.override.battleType("double").enemyHealthSegments(shieldsToBreak + 1); + game.override.battleStyle("double").enemyHealthSegments(shieldsToBreak + 1); await game.classicMode.startBattle([Species.MEWTWO]); diff --git a/test/daily_mode.test.ts b/test/daily_mode.test.ts index c530fca61a6..6b95543fb3b 100644 --- a/test/daily_mode.test.ts +++ b/test/daily_mode.test.ts @@ -57,7 +57,7 @@ describe("Shop modifications", async () => { game.override .startingWave(9) .startingBiome(Biome.ICE_CAVE) - .battleType("single") + .battleStyle("single") .startingLevel(100) // Avoid levelling up .disableTrainerWaves() .moveset([Moves.SPLASH]) diff --git a/test/data/status_effect.test.ts b/test/data/status_effect.test.ts index 0fd2daa308b..111136bf0a2 100644 --- a/test/data/status_effect.test.ts +++ b/test/data/status_effect.test.ts @@ -358,7 +358,7 @@ describe("Status Effects", () => { game.override .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -414,7 +414,7 @@ describe("Status Effects", () => { game.override .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/escape-calculations.test.ts b/test/escape-calculations.test.ts index b4504c7359c..d591bdec9fc 100644 --- a/test/escape-calculations.test.ts +++ b/test/escape-calculations.test.ts @@ -25,7 +25,7 @@ describe("Escape chance calculations", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.BULBASAUR) .enemyAbility(Abilities.INSOMNIA) .ability(Abilities.INSOMNIA); @@ -97,7 +97,7 @@ describe("Escape chance calculations", () => { }, 20000); it("double non-boss opponent", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.classicMode.startBattle([Species.BULBASAUR, Species.ABOMASNOW]); const playerPokemon = game.scene.getPlayerField(); @@ -262,7 +262,7 @@ describe("Escape chance calculations", () => { }, 20000); it("double boss opponent", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.startingWave(10); await game.classicMode.startBattle([Species.BULBASAUR, Species.ABOMASNOW]); diff --git a/test/evolution.test.ts b/test/evolution.test.ts index dd6795bf161..68d02402eac 100644 --- a/test/evolution.test.ts +++ b/test/evolution.test.ts @@ -28,7 +28,7 @@ describe("Evolution", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.MAGIKARP); game.override.enemyAbility(Abilities.BALL_FETCH); diff --git a/test/items/dire_hit.test.ts b/test/items/dire_hit.test.ts index 038d88ddc73..f6197e097c2 100644 --- a/test/items/dire_hit.test.ts +++ b/test/items/dire_hit.test.ts @@ -36,7 +36,7 @@ describe("Items - Dire Hit", () => { .enemyMoveset(Moves.SPLASH) .moveset([Moves.POUND]) .startingHeldItems([{ name: "DIRE_HIT" }]) - .battleType("single") + .battleStyle("single") .disableCrits(); }, 20000); diff --git a/test/items/eviolite.test.ts b/test/items/eviolite.test.ts index 2b82e2145e9..43fd6a795bb 100644 --- a/test/items/eviolite.test.ts +++ b/test/items/eviolite.test.ts @@ -22,7 +22,7 @@ describe("Items - Eviolite", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single").startingHeldItems([{ name: "EVIOLITE" }]); + game.override.battleStyle("single").startingHeldItems([{ name: "EVIOLITE" }]); }); it("should provide 50% boost to DEF and SPDEF for unevolved, unfused pokemon", async () => { diff --git a/test/items/exp_booster.test.ts b/test/items/exp_booster.test.ts index 2b1308f1afb..3fe31e5c202 100644 --- a/test/items/exp_booster.test.ts +++ b/test/items/exp_booster.test.ts @@ -24,7 +24,7 @@ describe("EXP Modifier Items", () => { game.override.enemyAbility(Abilities.BALL_FETCH); game.override.ability(Abilities.BALL_FETCH); - game.override.battleType("single"); + game.override.battleStyle("single"); }); it("EXP booster items stack multiplicatively", async () => { diff --git a/test/items/grip_claw.test.ts b/test/items/grip_claw.test.ts index aa7c23ca43d..2396a7ca072 100644 --- a/test/items/grip_claw.test.ts +++ b/test/items/grip_claw.test.ts @@ -27,7 +27,7 @@ describe("Items - Grip Claw", () => { game = new GameManager(phaserGame); game.override - .battleType("double") + .battleStyle("double") .moveset([Moves.TACKLE, Moves.SPLASH, Moves.ATTRACT]) .startingHeldItems([{ name: "GRIP_CLAW", count: 1 }]) .enemySpecies(Species.SNORLAX) @@ -101,7 +101,7 @@ describe("Items - Grip Claw", () => { it("should not allow Pollen Puff to steal items when healing ally", async () => { game.override - .battleType("double") + .battleStyle("double") .moveset([Moves.POLLEN_PUFF, Moves.ENDURE]) .startingHeldItems([ { name: "GRIP_CLAW", count: 1 }, diff --git a/test/items/leek.test.ts b/test/items/leek.test.ts index afb31a5f9fa..5f9be882bc1 100644 --- a/test/items/leek.test.ts +++ b/test/items/leek.test.ts @@ -29,7 +29,7 @@ describe("Items - Leek", () => { .startingHeldItems([{ name: "LEEK" }]) .moveset([Moves.TACKLE]) .disableCrits() - .battleType("single"); + .battleStyle("single"); }); it("should raise CRIT stage by 2 when held by FARFETCHD", async () => { diff --git a/test/items/leftovers.test.ts b/test/items/leftovers.test.ts index ad22e9c3cae..19739703f19 100644 --- a/test/items/leftovers.test.ts +++ b/test/items/leftovers.test.ts @@ -23,7 +23,7 @@ describe("Items - Leftovers", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.startingLevel(2000); game.override.ability(Abilities.UNNERVE); game.override.moveset([Moves.SPLASH]); diff --git a/test/items/light_ball.test.ts b/test/items/light_ball.test.ts index 1f5227142eb..e85fb1b602b 100644 --- a/test/items/light_ball.test.ts +++ b/test/items/light_ball.test.ts @@ -25,7 +25,7 @@ describe("Items - Light Ball", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); }); it("LIGHT_BALL activates in battle correctly", async () => { diff --git a/test/items/lock_capsule.test.ts b/test/items/lock_capsule.test.ts index 4e4182b3038..9cc6046307e 100644 --- a/test/items/lock_capsule.test.ts +++ b/test/items/lock_capsule.test.ts @@ -25,7 +25,7 @@ describe("Items - Lock Capsule", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .startingLevel(200) .moveset([Moves.SURF]) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/items/metal_powder.test.ts b/test/items/metal_powder.test.ts index ed96d3c498b..37686710848 100644 --- a/test/items/metal_powder.test.ts +++ b/test/items/metal_powder.test.ts @@ -25,7 +25,7 @@ describe("Items - Metal Powder", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); }); it("METAL_POWDER activates in battle correctly", async () => { diff --git a/test/items/multi_lens.test.ts b/test/items/multi_lens.test.ts index 176e8213f55..ff6154b8283 100644 --- a/test/items/multi_lens.test.ts +++ b/test/items/multi_lens.test.ts @@ -27,7 +27,7 @@ describe("Items - Multi Lens", () => { .moveset([Moves.TACKLE, Moves.TRAILBLAZE, Moves.TACHYON_CUTTER, Moves.FUTURE_SIGHT]) .ability(Abilities.BALL_FETCH) .startingHeldItems([{ name: "MULTI_LENS" }]) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) @@ -99,7 +99,7 @@ describe("Items - Multi Lens", () => { }); it("should enhance multi-target moves", async () => { - game.override.battleType("double").moveset([Moves.SWIFT, Moves.SPLASH]); + game.override.battleStyle("double").moveset([Moves.SWIFT, Moves.SPLASH]); await game.classicMode.startBattle([Species.MAGIKARP, Species.FEEBAS]); @@ -213,7 +213,7 @@ describe("Items - Multi Lens", () => { }); it("should not allow Pollen Puff to heal ally more than once", async () => { - game.override.battleType("double").moveset([Moves.POLLEN_PUFF, Moves.ENDURE]); + game.override.battleStyle("double").moveset([Moves.POLLEN_PUFF, Moves.ENDURE]); await game.classicMode.startBattle([Species.BULBASAUR, Species.OMANYTE]); const [, rightPokemon] = game.scene.getPlayerField(); diff --git a/test/items/mystical_rock.test.ts b/test/items/mystical_rock.test.ts index 0558bc21fe1..59119ce8611 100644 --- a/test/items/mystical_rock.test.ts +++ b/test/items/mystical_rock.test.ts @@ -29,7 +29,7 @@ describe("Items - Mystical Rock", () => { .enemyAbility(Abilities.BALL_FETCH) .moveset([Moves.SUNNY_DAY, Moves.GRASSY_TERRAIN]) .startingHeldItems([{ name: "MYSTICAL_ROCK", count: 2 }]) - .battleType("single"); + .battleStyle("single"); }); it("should increase weather duration by +2 turns per stack", async () => { diff --git a/test/items/quick_powder.test.ts b/test/items/quick_powder.test.ts index 7115cad8cd1..6937d6093f3 100644 --- a/test/items/quick_powder.test.ts +++ b/test/items/quick_powder.test.ts @@ -25,7 +25,7 @@ describe("Items - Quick Powder", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); }); it("QUICK_POWDER activates in battle correctly", async () => { diff --git a/test/items/reviver_seed.test.ts b/test/items/reviver_seed.test.ts index c06f354a94a..c109794d3d2 100644 --- a/test/items/reviver_seed.test.ts +++ b/test/items/reviver_seed.test.ts @@ -28,7 +28,7 @@ describe("Items - Reviver Seed", () => { game.override .moveset([Moves.SPLASH, Moves.TACKLE, Moves.ENDURE]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/items/scope_lens.test.ts b/test/items/scope_lens.test.ts index abd5cd7e75c..4d2fd63f87b 100644 --- a/test/items/scope_lens.test.ts +++ b/test/items/scope_lens.test.ts @@ -27,7 +27,7 @@ describe("Items - Scope Lens", () => { .enemyMoveset(Moves.SPLASH) .moveset([Moves.POUND]) .startingHeldItems([{ name: "SCOPE_LENS" }]) - .battleType("single") + .battleStyle("single") .disableCrits(); }, 20000); diff --git a/test/items/temp_stat_stage_booster.test.ts b/test/items/temp_stat_stage_booster.test.ts index 6417f898e3e..ccbabf01ccb 100644 --- a/test/items/temp_stat_stage_booster.test.ts +++ b/test/items/temp_stat_stage_booster.test.ts @@ -30,7 +30,7 @@ describe("Items - Temporary Stat Stage Boosters", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.SHUCKLE) .enemyMoveset(Moves.SPLASH) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/items/thick_club.test.ts b/test/items/thick_club.test.ts index 69ca316d455..9e9cd2e2ec8 100644 --- a/test/items/thick_club.test.ts +++ b/test/items/thick_club.test.ts @@ -25,7 +25,7 @@ describe("Items - Thick Club", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); }); it("THICK_CLUB activates in battle correctly", async () => { diff --git a/test/items/toxic_orb.test.ts b/test/items/toxic_orb.test.ts index 57e6b651b66..d02679e17c1 100644 --- a/test/items/toxic_orb.test.ts +++ b/test/items/toxic_orb.test.ts @@ -24,7 +24,7 @@ describe("Items - Toxic orb", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MAGIKARP) .ability(Abilities.BALL_FETCH) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/after_you.test.ts b/test/moves/after_you.test.ts index fde19b87b5d..3fa7c9ceb0a 100644 --- a/test/moves/after_you.test.ts +++ b/test/moves/after_you.test.ts @@ -25,7 +25,7 @@ describe("Moves - After You", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("double") + .battleStyle("double") .enemyLevel(5) .enemySpecies(Species.PIKACHU) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/alluring_voice.test.ts b/test/moves/alluring_voice.test.ts index 777078e4786..240e008f311 100644 --- a/test/moves/alluring_voice.test.ts +++ b/test/moves/alluring_voice.test.ts @@ -25,7 +25,7 @@ describe("Moves - Alluring Voice", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.ICE_SCALES) diff --git a/test/moves/aromatherapy.test.ts b/test/moves/aromatherapy.test.ts index fe7a008249f..c361f4e8bbd 100644 --- a/test/moves/aromatherapy.test.ts +++ b/test/moves/aromatherapy.test.ts @@ -26,7 +26,7 @@ describe("Moves - Aromatherapy", () => { game.override .moveset([Moves.AROMATHERAPY, Moves.SPLASH]) .statusEffect(StatusEffect.BURN) - .battleType("double") + .battleStyle("double") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH); }); diff --git a/test/moves/assist.test.ts b/test/moves/assist.test.ts index 68322a7f193..d0385399811 100644 --- a/test/moves/assist.test.ts +++ b/test/moves/assist.test.ts @@ -29,7 +29,7 @@ describe("Moves - Assist", () => { // because the normal moveset override doesn't allow for accurate testing of moveset changes game.override .ability(Abilities.BALL_FETCH) - .battleType("double") + .battleStyle("double") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyLevel(100) diff --git a/test/moves/astonish.test.ts b/test/moves/astonish.test.ts index 53922060ae6..1713df1de15 100644 --- a/test/moves/astonish.test.ts +++ b/test/moves/astonish.test.ts @@ -27,7 +27,7 @@ describe("Moves - Astonish", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.moveset([Moves.ASTONISH, Moves.SPLASH]); game.override.enemySpecies(Species.BLASTOISE); game.override.enemyAbility(Abilities.INSOMNIA); diff --git a/test/moves/aurora_veil.test.ts b/test/moves/aurora_veil.test.ts index 31f6497bae5..ef53b69b4e4 100644 --- a/test/moves/aurora_veil.test.ts +++ b/test/moves/aurora_veil.test.ts @@ -35,7 +35,7 @@ describe("Moves - Aurora Veil", () => { beforeEach(() => { game = new GameManager(phaserGame); globalScene = game.scene; - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.ability(Abilities.NONE); game.override.moveset([Moves.ABSORB, Moves.ROCK_SLIDE, Moves.TACKLE]); game.override.enemyLevel(100); @@ -62,7 +62,7 @@ describe("Moves - Aurora Veil", () => { }); it("reduces damage of physical attacks by a third in a double battle", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); const moveToUse = Moves.ROCK_SLIDE; await game.classicMode.startBattle([Species.SHUCKLE, Species.SHUCKLE]); @@ -98,7 +98,7 @@ describe("Moves - Aurora Veil", () => { }); it("reduces damage of special attacks by a third in a double battle", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); const moveToUse = Moves.DAZZLING_GLEAM; await game.classicMode.startBattle([Species.SHUCKLE, Species.SHUCKLE]); diff --git a/test/moves/autotomize.test.ts b/test/moves/autotomize.test.ts index 62ef185dea8..08e55f242bc 100644 --- a/test/moves/autotomize.test.ts +++ b/test/moves/autotomize.test.ts @@ -24,7 +24,7 @@ describe("Moves - Autotomize", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.AUTOTOMIZE, Moves.KINGS_SHIELD, Moves.FALSE_SWIPE]) - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH); }); diff --git a/test/moves/baddy_bad.test.ts b/test/moves/baddy_bad.test.ts index cba13c7ac68..ed6c9239eea 100644 --- a/test/moves/baddy_bad.test.ts +++ b/test/moves/baddy_bad.test.ts @@ -22,7 +22,7 @@ describe("Moves - Baddy Bad", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.SPLASH]) - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) diff --git a/test/moves/baneful_bunker.test.ts b/test/moves/baneful_bunker.test.ts index 4624d77dc42..4d0d7237c00 100644 --- a/test/moves/baneful_bunker.test.ts +++ b/test/moves/baneful_bunker.test.ts @@ -24,7 +24,7 @@ describe("Moves - Baneful Bunker", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.moveset(Moves.SLASH); diff --git a/test/moves/baton_pass.test.ts b/test/moves/baton_pass.test.ts index 9db6ec7c518..143ed285023 100644 --- a/test/moves/baton_pass.test.ts +++ b/test/moves/baton_pass.test.ts @@ -25,7 +25,7 @@ describe("Moves - Baton Pass", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) .moveset([Moves.BATON_PASS, Moves.NASTY_PLOT, Moves.SPLASH]) diff --git a/test/moves/beak_blast.test.ts b/test/moves/beak_blast.test.ts index 252b28448fd..45841cecd52 100644 --- a/test/moves/beak_blast.test.ts +++ b/test/moves/beak_blast.test.ts @@ -27,7 +27,7 @@ describe("Moves - Beak Blast", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .ability(Abilities.UNNERVE) .moveset([Moves.BEAK_BLAST]) .enemySpecies(Species.SNORLAX) diff --git a/test/moves/beat_up.test.ts b/test/moves/beat_up.test.ts index 7e67f2ea363..ad6cad40d32 100644 --- a/test/moves/beat_up.test.ts +++ b/test/moves/beat_up.test.ts @@ -23,7 +23,7 @@ describe("Moves - Beat Up", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.SNORLAX); game.override.enemyLevel(100); diff --git a/test/moves/burning_jealousy.test.ts b/test/moves/burning_jealousy.test.ts index 04966b24206..ea02bf5f4f5 100644 --- a/test/moves/burning_jealousy.test.ts +++ b/test/moves/burning_jealousy.test.ts @@ -25,7 +25,7 @@ describe("Moves - Burning Jealousy", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.ICE_SCALES) @@ -50,7 +50,7 @@ describe("Moves - Burning Jealousy", () => { }); it("should still burn the opponent if their stat stages were both raised and lowered in the same turn", async () => { - game.override.starterSpecies(0).battleType("double"); + game.override.starterSpecies(0).battleStyle("double"); await game.classicMode.startBattle([Species.FEEBAS, Species.ABRA]); const enemy = game.scene.getEnemyPokemon()!; diff --git a/test/moves/camouflage.test.ts b/test/moves/camouflage.test.ts index 0bbab6a629a..38cdef80fc1 100644 --- a/test/moves/camouflage.test.ts +++ b/test/moves/camouflage.test.ts @@ -27,7 +27,7 @@ describe("Moves - Camouflage", () => { game.override .moveset([Moves.CAMOUFLAGE]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.REGIELEKI) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/ceaseless_edge.test.ts b/test/moves/ceaseless_edge.test.ts index d54f1bd9f21..72e552bef6f 100644 --- a/test/moves/ceaseless_edge.test.ts +++ b/test/moves/ceaseless_edge.test.ts @@ -26,7 +26,7 @@ describe("Moves - Ceaseless Edge", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.RATTATA); game.override.enemyAbility(Abilities.RUN_AWAY); game.override.enemyPassiveAbility(Abilities.RUN_AWAY); diff --git a/test/moves/chilly_reception.test.ts b/test/moves/chilly_reception.test.ts index 39342a921b6..56da5dd400c 100644 --- a/test/moves/chilly_reception.test.ts +++ b/test/moves/chilly_reception.test.ts @@ -24,7 +24,7 @@ describe("Moves - Chilly Reception", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .moveset([Moves.CHILLY_RECEPTION, Moves.SNOWSCAPE]) .enemyMoveset(Array(4).fill(Moves.SPLASH)) .enemyAbility(Abilities.BALL_FETCH) @@ -70,7 +70,7 @@ describe("Moves - Chilly Reception", () => { // enemy uses another move and weather doesn't change it("check case - enemy not selecting chilly reception doesn't change weather ", async () => { game.override - .battleType("single") + .battleStyle("single") .enemyMoveset([Moves.CHILLY_RECEPTION, Moves.TACKLE]) .moveset(Array(4).fill(Moves.SPLASH)); @@ -85,7 +85,7 @@ describe("Moves - Chilly Reception", () => { it("enemy trainer - expected behavior ", async () => { game.override - .battleType("single") + .battleStyle("single") .startingWave(8) .enemyMoveset(Array(4).fill(Moves.CHILLY_RECEPTION)) .enemySpecies(Species.MAGIKARP) diff --git a/test/moves/chloroblast.test.ts b/test/moves/chloroblast.test.ts index f08eca100c4..175227bbd5e 100644 --- a/test/moves/chloroblast.test.ts +++ b/test/moves/chloroblast.test.ts @@ -24,7 +24,7 @@ describe("Moves - Chloroblast", () => { game.override .moveset([Moves.CHLOROBLAST]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/copycat.test.ts b/test/moves/copycat.test.ts index 0d9b0951f89..2e6e8098835 100644 --- a/test/moves/copycat.test.ts +++ b/test/moves/copycat.test.ts @@ -31,7 +31,7 @@ describe("Moves - Copycat", () => { game.override .moveset([Moves.COPYCAT, Moves.SPIKY_SHIELD, Moves.SWORDS_DANCE, Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .starterSpecies(Species.FEEBAS) .enemySpecies(Species.MAGIKARP) diff --git a/test/moves/crafty_shield.test.ts b/test/moves/crafty_shield.test.ts index 3a2df6a3446..c61e6d3848a 100644 --- a/test/moves/crafty_shield.test.ts +++ b/test/moves/crafty_shield.test.ts @@ -26,7 +26,7 @@ describe("Moves - Crafty Shield", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.CRAFTY_SHIELD, Moves.SPLASH, Moves.SWORDS_DANCE]); diff --git a/test/moves/defog.test.ts b/test/moves/defog.test.ts index 64904e964c4..58631150b6f 100644 --- a/test/moves/defog.test.ts +++ b/test/moves/defog.test.ts @@ -25,7 +25,7 @@ describe("Moves - Defog", () => { game.override .moveset([Moves.MIST, Moves.SAFEGUARD, Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.SHUCKLE) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/destiny_bond.test.ts b/test/moves/destiny_bond.test.ts index c39d40427ad..6e6446f464f 100644 --- a/test/moves/destiny_bond.test.ts +++ b/test/moves/destiny_bond.test.ts @@ -33,7 +33,7 @@ describe("Moves - Destiny Bond", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .ability(Abilities.UNNERVE) // Pre-emptively prevent flakiness from opponent berries .enemySpecies(Species.RATTATA) .enemyAbility(Abilities.RUN_AWAY) @@ -157,7 +157,7 @@ describe("Moves - Destiny Bond", () => { }); it("should not KO an ally", async () => { - game.override.moveset([Moves.DESTINY_BOND, Moves.CRUNCH]).battleType("double"); + game.override.moveset([Moves.DESTINY_BOND, Moves.CRUNCH]).battleStyle("double"); await game.classicMode.startBattle([Species.SHEDINJA, Species.BULBASAUR, Species.SQUIRTLE]); const enemyPokemon0 = game.scene.getEnemyField()[0]; @@ -201,7 +201,7 @@ describe("Moves - Destiny Bond", () => { }); it("should not cause a crash if the user is KO'd by Pledge moves", async () => { - game.override.moveset([Moves.GRASS_PLEDGE, Moves.WATER_PLEDGE]).battleType("double"); + game.override.moveset([Moves.GRASS_PLEDGE, Moves.WATER_PLEDGE]).battleStyle("double"); await game.classicMode.startBattle(defaultParty); const enemyPokemon0 = game.scene.getEnemyField()[0]; diff --git a/test/moves/diamond_storm.test.ts b/test/moves/diamond_storm.test.ts index 2363122f0d7..9ba62bbc52d 100644 --- a/test/moves/diamond_storm.test.ts +++ b/test/moves/diamond_storm.test.ts @@ -25,14 +25,14 @@ describe("Moves - Diamond Storm", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.DIAMOND_STORM]) - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH); }); it("should only increase defense once even if hitting 2 pokemon", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); const diamondStorm = allMoves[Moves.DIAMOND_STORM]; vi.spyOn(diamondStorm, "chance", "get").mockReturnValue(100); vi.spyOn(diamondStorm, "accuracy", "get").mockReturnValue(100); diff --git a/test/moves/dig.test.ts b/test/moves/dig.test.ts index 81339111656..a53456ec083 100644 --- a/test/moves/dig.test.ts +++ b/test/moves/dig.test.ts @@ -27,7 +27,7 @@ describe("Moves - Dig", () => { game = new GameManager(phaserGame); game.override .moveset(Moves.DIG) - .battleType("single") + .battleStyle("single") .startingLevel(100) .enemySpecies(Species.SNORLAX) .enemyLevel(100) diff --git a/test/moves/disable.test.ts b/test/moves/disable.test.ts index fdfb748df9d..d21716145a4 100644 --- a/test/moves/disable.test.ts +++ b/test/moves/disable.test.ts @@ -23,7 +23,7 @@ describe("Moves - Disable", () => { beforeEach(async () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .ability(Abilities.BALL_FETCH) .enemyAbility(Abilities.BALL_FETCH) .moveset([Moves.DISABLE, Moves.SPLASH]) diff --git a/test/moves/dive.test.ts b/test/moves/dive.test.ts index d7b53701a25..f33dc69b55f 100644 --- a/test/moves/dive.test.ts +++ b/test/moves/dive.test.ts @@ -27,7 +27,7 @@ describe("Moves - Dive", () => { game = new GameManager(phaserGame); game.override .moveset(Moves.DIVE) - .battleType("single") + .battleStyle("single") .startingLevel(100) .enemySpecies(Species.SNORLAX) .enemyLevel(100) diff --git a/test/moves/doodle.test.ts b/test/moves/doodle.test.ts index 822e415c918..25dc0ddaede 100644 --- a/test/moves/doodle.test.ts +++ b/test/moves/doodle.test.ts @@ -26,7 +26,7 @@ describe("Moves - Doodle", () => { game.override .moveset([Moves.SPLASH, Moves.DOODLE]) .ability(Abilities.ADAPTABILITY) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -43,7 +43,7 @@ describe("Moves - Doodle", () => { }); it("should copy the opponent's ability to itself and its ally in doubles", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]); game.move.select(Moves.DOODLE, 0, BattlerIndex.ENEMY); @@ -55,7 +55,7 @@ describe("Moves - Doodle", () => { }); it("should activate post-summon abilities", async () => { - game.override.battleType("double").enemyAbility(Abilities.INTIMIDATE); + game.override.battleStyle("double").enemyAbility(Abilities.INTIMIDATE); await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]); diff --git a/test/moves/double_team.test.ts b/test/moves/double_team.test.ts index f6791573132..8eac6be11f4 100644 --- a/test/moves/double_team.test.ts +++ b/test/moves/double_team.test.ts @@ -23,7 +23,7 @@ describe("Moves - Double Team", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.moveset([Moves.DOUBLE_TEAM]); game.override.disableCrits(); game.override.ability(Abilities.BALL_FETCH); diff --git a/test/moves/dragon_cheer.test.ts b/test/moves/dragon_cheer.test.ts index 30d5af3a51b..dcf7f13eb65 100644 --- a/test/moves/dragon_cheer.test.ts +++ b/test/moves/dragon_cheer.test.ts @@ -23,7 +23,7 @@ describe("Moves - Dragon Cheer", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("double") + .battleStyle("double") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) .enemyLevel(20) diff --git a/test/moves/dragon_rage.test.ts b/test/moves/dragon_rage.test.ts index 99d66421463..188c1511f37 100644 --- a/test/moves/dragon_rage.test.ts +++ b/test/moves/dragon_rage.test.ts @@ -31,7 +31,7 @@ describe("Moves - Dragon Rage", () => { beforeEach(async () => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.starterSpecies(Species.SNORLAX); game.override.moveset([Moves.DRAGON_RAGE]); diff --git a/test/moves/dragon_tail.test.ts b/test/moves/dragon_tail.test.ts index 37e8aa2fe1b..31e5560d4e0 100644 --- a/test/moves/dragon_tail.test.ts +++ b/test/moves/dragon_tail.test.ts @@ -28,7 +28,7 @@ describe("Moves - Dragon Tail", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .moveset([Moves.DRAGON_TAIL, Moves.SPLASH, Moves.FLAMETHROWER]) .enemySpecies(Species.WAILORD) .enemyMoveset(Moves.SPLASH) @@ -73,7 +73,7 @@ describe("Moves - Dragon Tail", () => { }); it("should proceed without crashing in a double battle", async () => { - game.override.battleType("double").enemyMoveset(Moves.SPLASH).enemyAbility(Abilities.ROUGH_SKIN); + game.override.battleStyle("double").enemyMoveset(Moves.SPLASH).enemyAbility(Abilities.ROUGH_SKIN); await game.classicMode.startBattle([Species.DRATINI, Species.DRATINI, Species.WAILORD, Species.WAILORD]); const leadPokemon = game.scene.getPlayerParty()[0]!; @@ -102,7 +102,7 @@ describe("Moves - Dragon Tail", () => { }); it("should redirect targets upon opponent flee", async () => { - game.override.battleType("double").enemyMoveset(Moves.SPLASH).enemyAbility(Abilities.ROUGH_SKIN); + game.override.battleStyle("double").enemyMoveset(Moves.SPLASH).enemyAbility(Abilities.ROUGH_SKIN); await game.classicMode.startBattle([Species.DRATINI, Species.DRATINI, Species.WAILORD, Species.WAILORD]); const leadPokemon = game.scene.getPlayerParty()[0]!; diff --git a/test/moves/dynamax_cannon.test.ts b/test/moves/dynamax_cannon.test.ts index 9cf3106b9c1..94f07ae500f 100644 --- a/test/moves/dynamax_cannon.test.ts +++ b/test/moves/dynamax_cannon.test.ts @@ -34,7 +34,7 @@ describe("Moves - Dynamax Cannon", () => { // Note that, for Waves 1-10, the level cap is 10 game.override.startingWave(1); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.disableCrits(); game.override.enemySpecies(Species.MAGIKARP); diff --git a/test/moves/electrify.test.ts b/test/moves/electrify.test.ts index 69e7504b406..25529e0b552 100644 --- a/test/moves/electrify.test.ts +++ b/test/moves/electrify.test.ts @@ -25,7 +25,7 @@ describe("Moves - Electrify", () => { game = new GameManager(phaserGame); game.override .moveset(Moves.ELECTRIFY) - .battleType("single") + .battleStyle("single") .startingLevel(100) .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/electro_shot.test.ts b/test/moves/electro_shot.test.ts index 05ab9c24a7c..0122bf04281 100644 --- a/test/moves/electro_shot.test.ts +++ b/test/moves/electro_shot.test.ts @@ -27,7 +27,7 @@ describe("Moves - Electro Shot", () => { game = new GameManager(phaserGame); game.override .moveset(Moves.ELECTRO_SHOT) - .battleType("single") + .battleStyle("single") .startingLevel(100) .enemySpecies(Species.SNORLAX) .enemyLevel(100) diff --git a/test/moves/encore.test.ts b/test/moves/encore.test.ts index 43b9eb6a77f..519e7860c04 100644 --- a/test/moves/encore.test.ts +++ b/test/moves/encore.test.ts @@ -27,7 +27,7 @@ describe("Moves - Encore", () => { game.override .moveset([Moves.SPLASH, Moves.ENCORE]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/endure.test.ts b/test/moves/endure.test.ts index 8fbb2272ece..190a689f46e 100644 --- a/test/moves/endure.test.ts +++ b/test/moves/endure.test.ts @@ -25,7 +25,7 @@ describe("Moves - Endure", () => { .moveset([Moves.THUNDER, Moves.BULLET_SEED, Moves.TOXIC, Moves.SHEER_COLD]) .ability(Abilities.SKILL_LINK) .startingLevel(100) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.NO_GUARD) diff --git a/test/moves/entrainment.test.ts b/test/moves/entrainment.test.ts index b2a0baf3e27..31a8ffcab85 100644 --- a/test/moves/entrainment.test.ts +++ b/test/moves/entrainment.test.ts @@ -25,7 +25,7 @@ describe("Moves - Entrainment", () => { game.override .moveset([Moves.SPLASH, Moves.ENTRAINMENT]) .ability(Abilities.ADAPTABILITY) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/fairy_lock.test.ts b/test/moves/fairy_lock.test.ts index a47143add4f..e967221bcae 100644 --- a/test/moves/fairy_lock.test.ts +++ b/test/moves/fairy_lock.test.ts @@ -26,7 +26,7 @@ describe("Moves - Fairy Lock", () => { game.override .moveset([Moves.FAIRY_LOCK, Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("double") + .battleStyle("double") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/fake_out.test.ts b/test/moves/fake_out.test.ts index 929c760ee5b..cbce16270e0 100644 --- a/test/moves/fake_out.test.ts +++ b/test/moves/fake_out.test.ts @@ -21,7 +21,7 @@ describe("Moves - Fake Out", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.CORVIKNIGHT) .moveset([Moves.FAKE_OUT, Moves.SPLASH]) .enemyMoveset(Moves.SPLASH) diff --git a/test/moves/false_swipe.test.ts b/test/moves/false_swipe.test.ts index 4fb5b81ef67..d6743477cae 100644 --- a/test/moves/false_swipe.test.ts +++ b/test/moves/false_swipe.test.ts @@ -26,7 +26,7 @@ describe("Moves - False Swipe", () => { .moveset([Moves.FALSE_SWIPE]) .ability(Abilities.BALL_FETCH) .startingLevel(1000) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/fell_stinger.test.ts b/test/moves/fell_stinger.test.ts index 2ffa44c5a3a..11731d8a06f 100644 --- a/test/moves/fell_stinger.test.ts +++ b/test/moves/fell_stinger.test.ts @@ -27,7 +27,7 @@ describe("Moves - Fell Stinger", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .moveset([Moves.FELL_STINGER, Moves.SALT_CURE, Moves.BIND, Moves.LEECH_SEED]) .startingLevel(50) .disableCrits() @@ -99,7 +99,7 @@ describe("Moves - Fell Stinger", () => { }); it("should not grant stat boost if enemy is KO'd by Salt Cure", async () => { - game.override.battleType("double").startingLevel(5); + game.override.battleStyle("double").startingLevel(5); const saltCure = allMoves[Moves.SALT_CURE]; const fellStinger = allMoves[Moves.FELL_STINGER]; vi.spyOn(saltCure, "accuracy", "get").mockReturnValue(100); @@ -124,7 +124,7 @@ describe("Moves - Fell Stinger", () => { }); it("should not grant stat boost if enemy dies to Bind or a similar effect", async () => { - game.override.battleType("double").startingLevel(5); + game.override.battleStyle("double").startingLevel(5); vi.spyOn(allMoves[Moves.BIND], "accuracy", "get").mockReturnValue(100); vi.spyOn(allMoves[Moves.FELL_STINGER], "power", "get").mockReturnValue(50000); @@ -147,7 +147,7 @@ describe("Moves - Fell Stinger", () => { }); it("should not grant stat boost if enemy dies to Leech Seed", async () => { - game.override.battleType("double").startingLevel(5); + game.override.battleStyle("double").startingLevel(5); vi.spyOn(allMoves[Moves.LEECH_SEED], "accuracy", "get").mockReturnValue(100); vi.spyOn(allMoves[Moves.FELL_STINGER], "power", "get").mockReturnValue(50000); diff --git a/test/moves/fissure.test.ts b/test/moves/fissure.test.ts index 63de58eb2e7..be6be079cf0 100644 --- a/test/moves/fissure.test.ts +++ b/test/moves/fissure.test.ts @@ -28,7 +28,7 @@ describe("Moves - Fissure", () => { beforeEach(async () => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.disableCrits(); game.override.starterSpecies(Species.SNORLAX); diff --git a/test/moves/flame_burst.test.ts b/test/moves/flame_burst.test.ts index a39c27d37b3..fb92537a238 100644 --- a/test/moves/flame_burst.test.ts +++ b/test/moves/flame_burst.test.ts @@ -35,7 +35,7 @@ describe("Moves - Flame Burst", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.FLAME_BURST, Moves.SPLASH]); game.override.disableCrits(); game.override.ability(Abilities.UNNERVE); diff --git a/test/moves/flower_shield.test.ts b/test/moves/flower_shield.test.ts index b66847651c1..4840c6f018f 100644 --- a/test/moves/flower_shield.test.ts +++ b/test/moves/flower_shield.test.ts @@ -28,7 +28,7 @@ describe("Moves - Flower Shield", () => { game = new GameManager(phaserGame); game.override.ability(Abilities.NONE); game.override.enemyAbility(Abilities.NONE); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.moveset([Moves.FLOWER_SHIELD, Moves.SPLASH]); game.override.enemyMoveset(Moves.SPLASH); }); @@ -51,7 +51,7 @@ describe("Moves - Flower Shield", () => { }); it("raises DEF stat stage by 1 for all Grass-type Pokemon on the field by one stage - double battle", async () => { - game.override.enemySpecies(Species.MAGIKARP).startingBiome(Biome.GRASS).battleType("double"); + game.override.enemySpecies(Species.MAGIKARP).startingBiome(Biome.GRASS).battleStyle("double"); await game.startBattle([Species.CHERRIM, Species.MAGIKARP]); const field = game.scene.getField(true); diff --git a/test/moves/fly.test.ts b/test/moves/fly.test.ts index 0bd7d22b2a7..f200e976704 100644 --- a/test/moves/fly.test.ts +++ b/test/moves/fly.test.ts @@ -28,7 +28,7 @@ describe("Moves - Fly", () => { game = new GameManager(phaserGame); game.override .moveset(Moves.FLY) - .battleType("single") + .battleStyle("single") .startingLevel(100) .enemySpecies(Species.SNORLAX) .enemyLevel(100) diff --git a/test/moves/focus_punch.test.ts b/test/moves/focus_punch.test.ts index 2dc5f20f2bf..e05eb008af7 100644 --- a/test/moves/focus_punch.test.ts +++ b/test/moves/focus_punch.test.ts @@ -28,7 +28,7 @@ describe("Moves - Focus Punch", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .ability(Abilities.UNNERVE) .moveset([Moves.FOCUS_PUNCH]) .enemySpecies(Species.GROUDON) diff --git a/test/moves/follow_me.test.ts b/test/moves/follow_me.test.ts index eeb11b36f24..68c4f111bb1 100644 --- a/test/moves/follow_me.test.ts +++ b/test/moves/follow_me.test.ts @@ -24,7 +24,7 @@ describe("Moves - Follow Me", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.starterSpecies(Species.AMOONGUSS); game.override.ability(Abilities.BALL_FETCH); game.override.enemySpecies(Species.SNORLAX); diff --git a/test/moves/forests_curse.test.ts b/test/moves/forests_curse.test.ts index 8850b92662d..f363fdbd19d 100644 --- a/test/moves/forests_curse.test.ts +++ b/test/moves/forests_curse.test.ts @@ -25,7 +25,7 @@ describe("Moves - Forest's Curse", () => { game.override .moveset([Moves.FORESTS_CURSE, Moves.TRICK_OR_TREAT]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/freeze_dry.test.ts b/test/moves/freeze_dry.test.ts index 8cab56ddfd2..62168afb960 100644 --- a/test/moves/freeze_dry.test.ts +++ b/test/moves/freeze_dry.test.ts @@ -24,7 +24,7 @@ describe("Moves - Freeze-Dry", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) diff --git a/test/moves/freezy_frost.test.ts b/test/moves/freezy_frost.test.ts index c1ac4054e70..4eb3114a5ba 100644 --- a/test/moves/freezy_frost.test.ts +++ b/test/moves/freezy_frost.test.ts @@ -24,7 +24,7 @@ describe("Moves - Freezy Frost", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.RATTATA) .enemyLevel(100) .enemyMoveset([Moves.HOWL, Moves.HOWL, Moves.HOWL, Moves.HOWL]) @@ -71,7 +71,7 @@ describe("Moves - Freezy Frost", () => { }); it("should clear all stat changes in double battle", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.classicMode.startBattle([Species.SHUCKLE, Species.RATTATA]); const [leftPlayer, rightPlayer] = game.scene.getPlayerField(); const [leftOpp, rightOpp] = game.scene.getEnemyField(); diff --git a/test/moves/fusion_bolt.test.ts b/test/moves/fusion_bolt.test.ts index fc47a0f04be..33498a857a9 100644 --- a/test/moves/fusion_bolt.test.ts +++ b/test/moves/fusion_bolt.test.ts @@ -30,7 +30,7 @@ describe("Moves - Fusion Bolt", () => { game.override.enemyAbility(Abilities.ROUGH_SKIN); game.override.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.startingWave(97); game.override.disableCrits(); }); diff --git a/test/moves/fusion_flare.test.ts b/test/moves/fusion_flare.test.ts index 17653cf58bc..61bb126a75a 100644 --- a/test/moves/fusion_flare.test.ts +++ b/test/moves/fusion_flare.test.ts @@ -30,7 +30,7 @@ describe("Moves - Fusion Flare", () => { game.override.enemySpecies(Species.RATTATA); game.override.enemyMoveset([Moves.REST, Moves.REST, Moves.REST, Moves.REST]); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.startingWave(97); game.override.disableCrits(); }); diff --git a/test/moves/fusion_flare_bolt.test.ts b/test/moves/fusion_flare_bolt.test.ts index c340aeea63f..697ac57e739 100644 --- a/test/moves/fusion_flare_bolt.test.ts +++ b/test/moves/fusion_flare_bolt.test.ts @@ -39,7 +39,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => { game.override.enemySpecies(Species.RESHIRAM); game.override.enemyMoveset([Moves.REST, Moves.REST, Moves.REST, Moves.REST]); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.startingWave(97); game.override.disableCrits(); diff --git a/test/moves/future_sight.test.ts b/test/moves/future_sight.test.ts index 40a940447e4..48be2451195 100644 --- a/test/moves/future_sight.test.ts +++ b/test/moves/future_sight.test.ts @@ -24,7 +24,7 @@ describe("Moves - Future Sight", () => { game.override .startingLevel(50) .moveset([Moves.FUTURE_SIGHT, Moves.SPLASH]) - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.STURDY) .enemyMoveset(Moves.SPLASH); diff --git a/test/moves/gastro_acid.test.ts b/test/moves/gastro_acid.test.ts index c9f2428845e..8247d29c0a0 100644 --- a/test/moves/gastro_acid.test.ts +++ b/test/moves/gastro_acid.test.ts @@ -22,7 +22,7 @@ describe("Moves - Gastro Acid", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.startingLevel(1); game.override.enemyLevel(100); game.override.ability(Abilities.NONE); @@ -61,7 +61,7 @@ describe("Moves - Gastro Acid", () => { }); it("fails if used on an enemy with an already-suppressed ability", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); await game.startBattle(); diff --git a/test/moves/geomancy.test.ts b/test/moves/geomancy.test.ts index 34281c96c60..51659f01b12 100644 --- a/test/moves/geomancy.test.ts +++ b/test/moves/geomancy.test.ts @@ -26,7 +26,7 @@ describe("Moves - Geomancy", () => { game = new GameManager(phaserGame); game.override .moveset(Moves.GEOMANCY) - .battleType("single") + .battleStyle("single") .startingLevel(100) .enemySpecies(Species.SNORLAX) .enemyLevel(100) diff --git a/test/moves/gigaton_hammer.test.ts b/test/moves/gigaton_hammer.test.ts index a6f7438a0a2..6275e5d2dcb 100644 --- a/test/moves/gigaton_hammer.test.ts +++ b/test/moves/gigaton_hammer.test.ts @@ -22,7 +22,7 @@ describe("Moves - Gigaton Hammer", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MAGIKARP) .starterSpecies(Species.FEEBAS) .moveset([Moves.GIGATON_HAMMER]) diff --git a/test/moves/glaive_rush.test.ts b/test/moves/glaive_rush.test.ts index d3531b172e2..3c2bcea7884 100644 --- a/test/moves/glaive_rush.test.ts +++ b/test/moves/glaive_rush.test.ts @@ -23,7 +23,7 @@ describe("Moves - Glaive Rush", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/growth.test.ts b/test/moves/growth.test.ts index 926593a4f72..37cd84638ba 100644 --- a/test/moves/growth.test.ts +++ b/test/moves/growth.test.ts @@ -24,7 +24,7 @@ describe("Moves - Growth", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemyAbility(Abilities.MOXIE); game.override.ability(Abilities.INSOMNIA); game.override.moveset([Moves.GROWTH]); diff --git a/test/moves/grudge.test.ts b/test/moves/grudge.test.ts index 161fa38edd2..ecde5351d6d 100644 --- a/test/moves/grudge.test.ts +++ b/test/moves/grudge.test.ts @@ -25,7 +25,7 @@ describe("Moves - Grudge", () => { game.override .moveset([Moves.EMBER, Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.SHEDINJA) .enemyAbility(Abilities.WONDER_GUARD) diff --git a/test/moves/guard_split.test.ts b/test/moves/guard_split.test.ts index 5db07e4e82c..d182e94b203 100644 --- a/test/moves/guard_split.test.ts +++ b/test/moves/guard_split.test.ts @@ -24,7 +24,7 @@ describe("Moves - Guard Split", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.NONE) .enemySpecies(Species.MEW) .enemyLevel(200) diff --git a/test/moves/guard_swap.test.ts b/test/moves/guard_swap.test.ts index be824672f32..2076f92ccb1 100644 --- a/test/moves/guard_swap.test.ts +++ b/test/moves/guard_swap.test.ts @@ -24,7 +24,7 @@ describe("Moves - Guard Swap", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) .enemySpecies(Species.INDEEDEE) diff --git a/test/moves/hard_press.test.ts b/test/moves/hard_press.test.ts index 8891f0bf0e2..8fe768cb8e4 100644 --- a/test/moves/hard_press.test.ts +++ b/test/moves/hard_press.test.ts @@ -27,7 +27,7 @@ describe("Moves - Hard Press", () => { beforeEach(() => { moveToCheck = allMoves[Moves.HARD_PRESS]; game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.ability(Abilities.BALL_FETCH); game.override.enemySpecies(Species.MUNCHLAX); game.override.enemyAbility(Abilities.BALL_FETCH); diff --git a/test/moves/haze.test.ts b/test/moves/haze.test.ts index d890678b466..4ddb6d1c7c5 100644 --- a/test/moves/haze.test.ts +++ b/test/moves/haze.test.ts @@ -23,7 +23,7 @@ describe("Moves - Haze", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.RATTATA); game.override.enemyLevel(100); diff --git a/test/moves/heal_bell.test.ts b/test/moves/heal_bell.test.ts index 4c0148bfd04..8ffb602c24f 100644 --- a/test/moves/heal_bell.test.ts +++ b/test/moves/heal_bell.test.ts @@ -26,7 +26,7 @@ describe("Moves - Heal Bell", () => { game.override .moveset([Moves.HEAL_BELL, Moves.SPLASH]) .statusEffect(StatusEffect.BURN) - .battleType("double") + .battleStyle("double") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH); }); diff --git a/test/moves/heart_swap.test.ts b/test/moves/heart_swap.test.ts index a3d892cd518..009db731951 100644 --- a/test/moves/heart_swap.test.ts +++ b/test/moves/heart_swap.test.ts @@ -24,7 +24,7 @@ describe("Moves - Heart Swap", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) .enemySpecies(Species.INDEEDEE) diff --git a/test/moves/hyper_beam.test.ts b/test/moves/hyper_beam.test.ts index 5cd54e9b46a..5b370f49e4c 100644 --- a/test/moves/hyper_beam.test.ts +++ b/test/moves/hyper_beam.test.ts @@ -26,7 +26,7 @@ describe("Moves - Hyper Beam", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.ability(Abilities.BALL_FETCH); game.override.enemySpecies(Species.SNORLAX); game.override.enemyAbility(Abilities.BALL_FETCH); diff --git a/test/moves/imprison.test.ts b/test/moves/imprison.test.ts index 89ef9981040..cefbaa52cad 100644 --- a/test/moves/imprison.test.ts +++ b/test/moves/imprison.test.ts @@ -23,7 +23,7 @@ describe("Moves - Imprison", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset([Moves.IMPRISON, Moves.SPLASH, Moves.GROWL]) .enemySpecies(Species.SHUCKLE) diff --git a/test/moves/instruct.test.ts b/test/moves/instruct.test.ts index 079c8803ddc..c5650d7bbd5 100644 --- a/test/moves/instruct.test.ts +++ b/test/moves/instruct.test.ts @@ -32,7 +32,7 @@ describe("Moves - Instruct", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.SHUCKLE) .enemyAbility(Abilities.NO_GUARD) .enemyLevel(100) @@ -89,7 +89,7 @@ describe("Moves - Instruct", () => { }); it("should repeat ally's attack on enemy", async () => { - game.override.battleType("double").enemyMoveset(Moves.SPLASH); + game.override.battleStyle("double").enemyMoveset(Moves.SPLASH); await game.classicMode.startBattle([Species.AMOONGUSS, Species.SHUCKLE]); const [amoonguss, shuckle] = game.scene.getPlayerField(); @@ -122,7 +122,7 @@ describe("Moves - Instruct", () => { }); it("should add moves to move queue for copycat", async () => { - game.override.battleType("double").moveset(Moves.INSTRUCT).enemyLevel(5); + game.override.battleStyle("double").moveset(Moves.INSTRUCT).enemyLevel(5); await game.classicMode.startBattle([Species.AMOONGUSS]); const [enemy1, enemy2] = game.scene.getEnemyField()!; @@ -179,7 +179,7 @@ describe("Moves - Instruct", () => { }); it("should redirect attacking moves if enemy faints", async () => { - game.override.battleType("double").enemyMoveset(Moves.SPLASH).enemySpecies(Species.MAGIKARP).enemyLevel(1); + game.override.battleStyle("double").enemyMoveset(Moves.SPLASH).enemySpecies(Species.MAGIKARP).enemyLevel(1); await game.classicMode.startBattle([Species.HISUI_ELECTRODE, Species.KOMMO_O]); const [electrode, kommo_o] = game.scene.getPlayerField()!; @@ -201,7 +201,7 @@ describe("Moves - Instruct", () => { expect(karp2.isFainted()).toBe(true); }); it("should allow for dancer copying of instructed dance move", async () => { - game.override.battleType("double").enemyMoveset([Moves.INSTRUCT, Moves.SPLASH]).enemyLevel(1000); + game.override.battleStyle("double").enemyMoveset([Moves.INSTRUCT, Moves.SPLASH]).enemyLevel(1000); await game.classicMode.startBattle([Species.ORICORIO, Species.VOLCARONA]); const [oricorio, volcarona] = game.scene.getPlayerField(); @@ -256,7 +256,7 @@ describe("Moves - Instruct", () => { }); it("should attempt to call enemy's disabled move, but move use itself should fail", async () => { - game.override.moveset([Moves.INSTRUCT, Moves.DISABLE]).battleType("double"); + game.override.moveset([Moves.INSTRUCT, Moves.DISABLE]).battleStyle("double"); await game.classicMode.startBattle([Species.AMOONGUSS, Species.DROWZEE]); const [enemy1, enemy2] = game.scene.getEnemyField(); @@ -372,7 +372,7 @@ describe("Moves - Instruct", () => { it("should respect moves' original priority for psychic terrain", async () => { game.override - .battleType("double") + .battleStyle("double") .moveset([Moves.QUICK_ATTACK, Moves.SPLASH, Moves.INSTRUCT]) .enemyMoveset([Moves.SPLASH, Moves.PSYCHIC_TERRAIN]); await game.classicMode.startBattle([Species.BANETTE, Species.KLEFKI]); @@ -395,7 +395,7 @@ describe("Moves - Instruct", () => { }); it("should still work w/ prankster in psychic terrain", async () => { - game.override.battleType("double").enemyMoveset([Moves.SPLASH, Moves.PSYCHIC_TERRAIN]); + game.override.battleStyle("double").enemyMoveset([Moves.SPLASH, Moves.PSYCHIC_TERRAIN]); await game.classicMode.startBattle([Species.BANETTE, Species.KLEFKI]); const [banette, klefki] = game.scene.getPlayerField()!; @@ -419,7 +419,7 @@ describe("Moves - Instruct", () => { it("should cause spread moves to correctly hit targets in doubles after singles", async () => { game.override - .battleType("even-doubles") + .battleStyle("even-doubles") .moveset([Moves.BREAKING_SWIPE, Moves.INSTRUCT, Moves.SPLASH]) .enemyMoveset(Moves.SONIC_BOOM) .enemySpecies(Species.AXEW) @@ -446,7 +446,7 @@ describe("Moves - Instruct", () => { it("should cause AoE moves to correctly hit everyone in doubles after singles", async () => { game.override - .battleType("even-doubles") + .battleStyle("even-doubles") .moveset([Moves.BRUTAL_SWING, Moves.INSTRUCT, Moves.SPLASH]) .enemySpecies(Species.AXEW) .enemyMoveset(Moves.SONIC_BOOM) @@ -504,7 +504,7 @@ describe("Moves - Instruct", () => { it("should cause multi-hit moves to hit the appropriate number of times in doubles", async () => { game.override - .battleType("double") + .battleStyle("double") .enemyAbility(Abilities.SKILL_LINK) .moveset([Moves.SPLASH, Moves.INSTRUCT]) .enemyMoveset([Moves.BULLET_SEED, Moves.SPLASH]) diff --git a/test/moves/jaw_lock.test.ts b/test/moves/jaw_lock.test.ts index fc71397e624..71896dc3b62 100644 --- a/test/moves/jaw_lock.test.ts +++ b/test/moves/jaw_lock.test.ts @@ -29,7 +29,7 @@ describe("Moves - Jaw Lock", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.INSOMNIA) .enemyMoveset(Moves.SPLASH) @@ -107,7 +107,7 @@ describe("Moves - Jaw Lock", () => { }); it("should not trap other targets after the first target is trapped", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.startBattle([Species.CHARMANDER, Species.BULBASAUR]); diff --git a/test/moves/lash_out.test.ts b/test/moves/lash_out.test.ts index 8395633f5c0..c80a8ce348a 100644 --- a/test/moves/lash_out.test.ts +++ b/test/moves/lash_out.test.ts @@ -24,7 +24,7 @@ describe("Moves - Lash Out", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.FUR_COAT) diff --git a/test/moves/last_respects.test.ts b/test/moves/last_respects.test.ts index ccab8a43415..a69ecb2e989 100644 --- a/test/moves/last_respects.test.ts +++ b/test/moves/last_respects.test.ts @@ -31,7 +31,7 @@ describe("Moves - Last Respects", () => { move = allMoves[Moves.LAST_RESPECTS]; basePower = move.power; game.override - .battleType("single") + .battleStyle("single") .disableCrits() .moveset([Moves.LAST_RESPECTS, Moves.EXPLOSION, Moves.LUNAR_DANCE]) .ability(Abilities.BALL_FETCH) diff --git a/test/moves/light_screen.test.ts b/test/moves/light_screen.test.ts index 9cc6944ed3e..12aeb29577a 100644 --- a/test/moves/light_screen.test.ts +++ b/test/moves/light_screen.test.ts @@ -34,7 +34,7 @@ describe("Moves - Light Screen", () => { beforeEach(() => { game = new GameManager(phaserGame); globalScene = game.scene; - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.ability(Abilities.NONE); game.override.moveset([Moves.ABSORB, Moves.DAZZLING_GLEAM, Moves.TACKLE]); game.override.enemyLevel(100); @@ -61,7 +61,7 @@ describe("Moves - Light Screen", () => { }); it("reduces damage of special attacks by a third in a double battle", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); const moveToUse = Moves.DAZZLING_GLEAM; await game.classicMode.startBattle([Species.SHUCKLE, Species.SHUCKLE]); diff --git a/test/moves/lucky_chant.test.ts b/test/moves/lucky_chant.test.ts index 21802574e79..e2a28a7bbe3 100644 --- a/test/moves/lucky_chant.test.ts +++ b/test/moves/lucky_chant.test.ts @@ -25,7 +25,7 @@ describe("Moves - Lucky Chant", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .moveset([Moves.LUCKY_CHANT, Moves.SPLASH, Moves.FOLLOW_ME]) .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.INSOMNIA) @@ -54,7 +54,7 @@ describe("Moves - Lucky Chant", () => { }); it("should prevent critical hits against the user's ally", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); diff --git a/test/moves/lunar_blessing.test.ts b/test/moves/lunar_blessing.test.ts index d97e6c978eb..ee35107fccd 100644 --- a/test/moves/lunar_blessing.test.ts +++ b/test/moves/lunar_blessing.test.ts @@ -22,7 +22,7 @@ describe("Moves - Lunar Blessing", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.enemySpecies(Species.SHUCKLE); game.override.enemyMoveset(Moves.SPLASH); diff --git a/test/moves/lunar_dance.test.ts b/test/moves/lunar_dance.test.ts index d3dceba087c..30abe765291 100644 --- a/test/moves/lunar_dance.test.ts +++ b/test/moves/lunar_dance.test.ts @@ -25,7 +25,7 @@ describe("Moves - Lunar Dance", () => { game = new GameManager(phaserGame); game.override .statusEffect(StatusEffect.BURN) - .battleType("double") + .battleStyle("double") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH); }); diff --git a/test/moves/magic_coat.test.ts b/test/moves/magic_coat.test.ts index 2cc8dea8938..23deef97318 100644 --- a/test/moves/magic_coat.test.ts +++ b/test/moves/magic_coat.test.ts @@ -30,7 +30,7 @@ describe("Moves - Magic Coat", () => { game = new GameManager(phaserGame); game.override .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -82,7 +82,7 @@ describe("Moves - Magic Coat", () => { }); it("should individually bounce back multi-target moves when used by both targets in doubles", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.GROWL, Moves.SPLASH]); await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP]); @@ -95,7 +95,7 @@ describe("Moves - Magic Coat", () => { }); it("should bounce back a spread status move against both pokemon", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.GROWL, Moves.SPLASH]); game.override.enemyMoveset([Moves.SPLASH, Moves.MAGIC_COAT]); await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP]); @@ -121,7 +121,7 @@ describe("Moves - Magic Coat", () => { }); it("should not bounce back a move that was just bounced", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.ability(Abilities.MAGIC_BOUNCE); game.override.moveset([Moves.GROWL, Moves.MAGIC_COAT]); game.override.enemyMoveset([Moves.SPLASH, Moves.MAGIC_COAT]); @@ -159,7 +159,7 @@ describe("Moves - Magic Coat", () => { }); it("should only bounce spikes back once when both targets use magic coat in doubles", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.classicMode.startBattle([Species.MAGIKARP]); game.override.moveset([Moves.SPIKES]); @@ -206,7 +206,7 @@ describe("Moves - Magic Coat", () => { // TODO: stomping tantrum should consider moves that were bounced. it.todo("should cause stomping tantrum to double in power when the last move was bounced", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); await game.classicMode.startBattle([Species.MAGIKARP]); game.override.moveset([Moves.STOMPING_TANTRUM, Moves.CHARM]); diff --git a/test/moves/magnet_rise.test.ts b/test/moves/magnet_rise.test.ts index 725bbb99276..62ad0c88091 100644 --- a/test/moves/magnet_rise.test.ts +++ b/test/moves/magnet_rise.test.ts @@ -23,7 +23,7 @@ describe("Moves - Magnet Rise", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.starterSpecies(Species.MAGNEZONE); game.override.enemySpecies(Species.RATTATA); game.override.enemyMoveset([Moves.DRILL_RUN, Moves.DRILL_RUN, Moves.DRILL_RUN, Moves.DRILL_RUN]); diff --git a/test/moves/make_it_rain.test.ts b/test/moves/make_it_rain.test.ts index 38460d99e63..4d94537bcec 100644 --- a/test/moves/make_it_rain.test.ts +++ b/test/moves/make_it_rain.test.ts @@ -24,7 +24,7 @@ describe("Moves - Make It Rain", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.MAKE_IT_RAIN, Moves.SPLASH]); game.override.enemySpecies(Species.SNORLAX); game.override.enemyAbility(Abilities.INSOMNIA); @@ -48,7 +48,7 @@ describe("Moves - Make It Rain", () => { it("should apply effects even if the target faints", async () => { game.override.enemyLevel(1); // ensures the enemy will faint - game.override.battleType("single"); + game.override.battleStyle("single"); await game.startBattle([Species.CHARIZARD]); diff --git a/test/moves/mat_block.test.ts b/test/moves/mat_block.test.ts index ddfa29a53da..9ed0f497af9 100644 --- a/test/moves/mat_block.test.ts +++ b/test/moves/mat_block.test.ts @@ -26,7 +26,7 @@ describe("Moves - Mat Block", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.MAT_BLOCK, Moves.SPLASH]); diff --git a/test/moves/metal_burst.test.ts b/test/moves/metal_burst.test.ts index 2cbc999436f..7fa5434dc58 100644 --- a/test/moves/metal_burst.test.ts +++ b/test/moves/metal_burst.test.ts @@ -27,7 +27,7 @@ describe("Moves - Metal Burst", () => { .moveset([Moves.METAL_BURST, Moves.FISSURE, Moves.PRECIPICE_BLADES]) .ability(Abilities.PURE_POWER) .startingLevel(10) - .battleType("double") + .battleStyle("double") .disableCrits() .enemySpecies(Species.PICHU) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/metronome.test.ts b/test/moves/metronome.test.ts index 80f32a3a6fb..bf177fb1a93 100644 --- a/test/moves/metronome.test.ts +++ b/test/moves/metronome.test.ts @@ -30,7 +30,7 @@ describe("Moves - Metronome", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.METRONOME, Moves.SPLASH]) - .battleType("single") + .battleStyle("single") .startingLevel(100) .starterSpecies(Species.REGIELEKI) .enemyLevel(100) @@ -79,7 +79,7 @@ describe("Moves - Metronome", () => { }); it("should only target ally for Aromatic Mist", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.classicMode.startBattle([Species.REGIELEKI, Species.RATTATA]); const [leftPlayer, rightPlayer] = game.scene.getPlayerField(); const [leftOpp, rightOpp] = game.scene.getEnemyField(); diff --git a/test/moves/mirror_move.test.ts b/test/moves/mirror_move.test.ts index 9178410adb2..438c594d839 100644 --- a/test/moves/mirror_move.test.ts +++ b/test/moves/mirror_move.test.ts @@ -27,7 +27,7 @@ describe("Moves - Mirror Move", () => { game.override .moveset([Moves.MIRROR_MOVE, Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -35,7 +35,7 @@ describe("Moves - Mirror Move", () => { }); it("should use the last move that the target used on the user", async () => { - game.override.battleType("double").enemyMoveset([Moves.TACKLE, Moves.GROWL]); + game.override.battleStyle("double").enemyMoveset([Moves.TACKLE, Moves.GROWL]); await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]); game.move.select(Moves.MIRROR_MOVE, 0, BattlerIndex.ENEMY); // target's last move is Tackle, enemy should receive damage from Mirror Move copying Tackle diff --git a/test/moves/mist.test.ts b/test/moves/mist.test.ts index 2deb6f9b90d..70cdf5b55a0 100644 --- a/test/moves/mist.test.ts +++ b/test/moves/mist.test.ts @@ -25,7 +25,7 @@ describe("Moves - Mist", () => { game.override .moveset([Moves.MIST, Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("double") + .battleStyle("double") .disableCrits() .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/moongeist_beam.test.ts b/test/moves/moongeist_beam.test.ts index 117fe513e17..82a2567377b 100644 --- a/test/moves/moongeist_beam.test.ts +++ b/test/moves/moongeist_beam.test.ts @@ -26,7 +26,7 @@ describe("Moves - Moongeist Beam", () => { .moveset([Moves.MOONGEIST_BEAM, Moves.METRONOME]) .ability(Abilities.BALL_FETCH) .startingLevel(200) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.STURDY) diff --git a/test/moves/multi_target.test.ts b/test/moves/multi_target.test.ts index 5d33c7860cb..fccf650416c 100644 --- a/test/moves/multi_target.test.ts +++ b/test/moves/multi_target.test.ts @@ -25,7 +25,7 @@ describe("Multi-target damage reduction", () => { game = new GameManager(phaserGame); game.override .disableCrits() - .battleType("double") + .battleStyle("double") .enemyLevel(100) .startingLevel(100) .enemySpecies(Species.POLIWAG) diff --git a/test/moves/nightmare.test.ts b/test/moves/nightmare.test.ts index e1cef0084ee..044856ae33d 100644 --- a/test/moves/nightmare.test.ts +++ b/test/moves/nightmare.test.ts @@ -24,7 +24,7 @@ describe("Moves - Nightmare", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.RATTATA) .enemyMoveset(Moves.SPLASH) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/obstruct.test.ts b/test/moves/obstruct.test.ts index d8e3a949f08..f35a5964bcb 100644 --- a/test/moves/obstruct.test.ts +++ b/test/moves/obstruct.test.ts @@ -22,7 +22,7 @@ describe("Moves - Obstruct", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MAGIKARP) .enemyMoveset(Moves.TACKLE) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/octolock.test.ts b/test/moves/octolock.test.ts index c9c5fd42f7e..fb57d0bfad5 100644 --- a/test/moves/octolock.test.ts +++ b/test/moves/octolock.test.ts @@ -25,7 +25,7 @@ describe("Moves - Octolock", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MAGIKARP) .enemyMoveset(Moves.SPLASH) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/order_up.test.ts b/test/moves/order_up.test.ts index f25114c12de..701d0489c25 100644 --- a/test/moves/order_up.test.ts +++ b/test/moves/order_up.test.ts @@ -29,7 +29,7 @@ describe("Moves - Order Up", () => { game.override .moveset(Moves.ORDER_UP) .ability(Abilities.COMMANDER) - .battleType("double") + .battleStyle("double") .disableCrits() .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/parting_shot.test.ts b/test/moves/parting_shot.test.ts index 699d960f882..a65c1a5b3a5 100644 --- a/test/moves/parting_shot.test.ts +++ b/test/moves/parting_shot.test.ts @@ -26,7 +26,7 @@ describe("Moves - Parting Shot", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.moveset([Moves.PARTING_SHOT, Moves.SPLASH]); game.override.enemyMoveset(Moves.SPLASH); game.override.startingLevel(5); diff --git a/test/moves/plasma_fists.test.ts b/test/moves/plasma_fists.test.ts index fe19ab4a460..b6a5ceaed68 100644 --- a/test/moves/plasma_fists.test.ts +++ b/test/moves/plasma_fists.test.ts @@ -25,7 +25,7 @@ describe("Moves - Plasma Fists", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.PLASMA_FISTS, Moves.TACKLE]) - .battleType("double") + .battleStyle("double") .startingLevel(100) .enemySpecies(Species.DUSCLOPS) .enemyAbility(Abilities.BALL_FETCH) @@ -56,7 +56,7 @@ describe("Moves - Plasma Fists", () => { }); it("should not affect Normal-type attacks boosted by Pixilate", async () => { - game.override.battleType("single").enemyAbility(Abilities.PIXILATE); + game.override.battleStyle("single").enemyAbility(Abilities.PIXILATE); await game.classicMode.startBattle([Species.ONIX]); @@ -74,7 +74,7 @@ describe("Moves - Plasma Fists", () => { }); it("should affect moves that become Normal type due to Normalize", async () => { - game.override.battleType("single").enemyAbility(Abilities.NORMALIZE).enemyMoveset(Moves.WATER_GUN); + game.override.battleStyle("single").enemyAbility(Abilities.NORMALIZE).enemyMoveset(Moves.WATER_GUN); await game.classicMode.startBattle([Species.DUSCLOPS]); diff --git a/test/moves/pledge_moves.test.ts b/test/moves/pledge_moves.test.ts index ee9e0b8b154..b3d13a27b83 100644 --- a/test/moves/pledge_moves.test.ts +++ b/test/moves/pledge_moves.test.ts @@ -30,7 +30,7 @@ describe("Moves - Pledge Moves", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("double") + .battleStyle("double") .startingLevel(100) .moveset([Moves.FIRE_PLEDGE, Moves.GRASS_PLEDGE, Moves.WATER_PLEDGE, Moves.SPLASH]) .enemySpecies(Species.SNORLAX) @@ -86,7 +86,7 @@ describe("Moves - Pledge Moves", () => { }); it("Fire Pledge - should not combine with an enemy's Pledge move", async () => { - game.override.battleType("single").enemyMoveset(Moves.GRASS_PLEDGE); + game.override.battleStyle("single").enemyMoveset(Moves.GRASS_PLEDGE); await game.classicMode.startBattle([Species.CHARIZARD]); diff --git a/test/moves/pollen_puff.test.ts b/test/moves/pollen_puff.test.ts index 3af3ea1f41d..31d5950b47d 100644 --- a/test/moves/pollen_puff.test.ts +++ b/test/moves/pollen_puff.test.ts @@ -25,7 +25,7 @@ describe("Moves - Pollen Puff", () => { game.override .moveset([Moves.POLLEN_PUFF]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -33,7 +33,7 @@ describe("Moves - Pollen Puff", () => { }); it("should not heal more than once when the user has a source of multi-hit", async () => { - game.override.battleType("double").moveset([Moves.POLLEN_PUFF, Moves.ENDURE]).ability(Abilities.PARENTAL_BOND); + game.override.battleStyle("double").moveset([Moves.POLLEN_PUFF, Moves.ENDURE]).ability(Abilities.PARENTAL_BOND); await game.classicMode.startBattle([Species.BULBASAUR, Species.OMANYTE]); const [_, rightPokemon] = game.scene.getPlayerField(); diff --git a/test/moves/powder.test.ts b/test/moves/powder.test.ts index 522b0b74ca7..6f7a6add054 100644 --- a/test/moves/powder.test.ts +++ b/test/moves/powder.test.ts @@ -27,7 +27,7 @@ describe("Moves - Powder", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override .enemySpecies(Species.SNORLAX) @@ -150,7 +150,7 @@ describe("Moves - Powder", () => { }); it("should cancel Fire-type moves generated by the target's Dancer ability", async () => { - game.override.battleType("double").enemySpecies(Species.BLASTOISE).enemyAbility(Abilities.DANCER); + game.override.battleStyle("double").enemySpecies(Species.BLASTOISE).enemyAbility(Abilities.DANCER); await game.classicMode.startBattle([Species.CHARIZARD, Species.CHARIZARD]); @@ -227,7 +227,7 @@ describe("Moves - Powder", () => { }); it("should cancel Grass Pledge if used after ally's Fire Pledge", async () => { - game.override.enemyMoveset([Moves.FIRE_PLEDGE, Moves.GRASS_PLEDGE]).battleType("double"); + game.override.enemyMoveset([Moves.FIRE_PLEDGE, Moves.GRASS_PLEDGE]).battleStyle("double"); await game.classicMode.startBattle([Species.CHARIZARD, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -244,7 +244,7 @@ describe("Moves - Powder", () => { }); it("should cancel Fire Pledge if used before ally's Water Pledge", async () => { - game.override.enemyMoveset([Moves.FIRE_PLEDGE, Moves.WATER_PLEDGE]).battleType("double"); + game.override.enemyMoveset([Moves.FIRE_PLEDGE, Moves.WATER_PLEDGE]).battleStyle("double"); await game.classicMode.startBattle([Species.CHARIZARD, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -261,7 +261,7 @@ describe("Moves - Powder", () => { }); it("should NOT cancel Fire Pledge if used after ally's Water Pledge", async () => { - game.override.enemyMoveset([Moves.FIRE_PLEDGE, Moves.WATER_PLEDGE]).battleType("double"); + game.override.enemyMoveset([Moves.FIRE_PLEDGE, Moves.WATER_PLEDGE]).battleStyle("double"); await game.classicMode.startBattle([Species.CHARIZARD, Species.CHARIZARD]); const enemyPokemon = game.scene.getEnemyPokemon()!; diff --git a/test/moves/power_shift.test.ts b/test/moves/power_shift.test.ts index fbc6d732d30..0fee044f5ad 100644 --- a/test/moves/power_shift.test.ts +++ b/test/moves/power_shift.test.ts @@ -23,7 +23,7 @@ describe("Moves - Power Shift", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.POWER_SHIFT, Moves.BULK_UP]) - .battleType("single") + .battleStyle("single") .ability(Abilities.BALL_FETCH) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH); diff --git a/test/moves/power_split.test.ts b/test/moves/power_split.test.ts index 9150a707ad5..f15275fce9e 100644 --- a/test/moves/power_split.test.ts +++ b/test/moves/power_split.test.ts @@ -24,7 +24,7 @@ describe("Moves - Power Split", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.NONE) .enemySpecies(Species.MEW) .enemyLevel(200) diff --git a/test/moves/power_swap.test.ts b/test/moves/power_swap.test.ts index d6f5e782e66..5f6aa022a51 100644 --- a/test/moves/power_swap.test.ts +++ b/test/moves/power_swap.test.ts @@ -24,7 +24,7 @@ describe("Moves - Power Swap", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) .enemySpecies(Species.INDEEDEE) diff --git a/test/moves/power_trick.test.ts b/test/moves/power_trick.test.ts index 0cd849bbcc5..181eeca81bc 100644 --- a/test/moves/power_trick.test.ts +++ b/test/moves/power_trick.test.ts @@ -25,7 +25,7 @@ describe("Moves - Power Trick", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) .enemySpecies(Species.MEW) diff --git a/test/moves/protect.test.ts b/test/moves/protect.test.ts index d50c490f7d3..183430f8654 100644 --- a/test/moves/protect.test.ts +++ b/test/moves/protect.test.ts @@ -27,7 +27,7 @@ describe("Moves - Protect", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.moveset([Moves.PROTECT]); game.override.enemySpecies(Species.SNORLAX); diff --git a/test/moves/psycho_shift.test.ts b/test/moves/psycho_shift.test.ts index 0a82189d201..678742906c7 100644 --- a/test/moves/psycho_shift.test.ts +++ b/test/moves/psycho_shift.test.ts @@ -26,7 +26,7 @@ describe("Moves - Psycho Shift", () => { .moveset([Moves.PSYCHO_SHIFT]) .ability(Abilities.BALL_FETCH) .statusEffect(StatusEffect.POISON) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyLevel(20) diff --git a/test/moves/purify.test.ts b/test/moves/purify.test.ts index 30d9df8ff67..191539d8cec 100644 --- a/test/moves/purify.test.ts +++ b/test/moves/purify.test.ts @@ -25,7 +25,7 @@ describe("Moves - Purify", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.starterSpecies(Species.PYUKUMUKU); game.override.startingLevel(10); diff --git a/test/moves/quash.test.ts b/test/moves/quash.test.ts index f85dbd89517..5bf8271320b 100644 --- a/test/moves/quash.test.ts +++ b/test/moves/quash.test.ts @@ -25,7 +25,7 @@ describe("Moves - Quash", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("double") + .battleStyle("double") .enemyLevel(1) .enemySpecies(Species.SLOWPOKE) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/quick_guard.test.ts b/test/moves/quick_guard.test.ts index 22d4a5078ac..d9970ce64fa 100644 --- a/test/moves/quick_guard.test.ts +++ b/test/moves/quick_guard.test.ts @@ -25,7 +25,7 @@ describe("Moves - Quick Guard", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.QUICK_GUARD, Moves.SPLASH, Moves.FOLLOW_ME]); @@ -84,7 +84,7 @@ describe("Moves - Quick Guard", () => { }); test("should fail if the user is the last to move in the turn", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemyMoveset([Moves.QUICK_GUARD]); await game.classicMode.startBattle([Species.CHARIZARD]); diff --git a/test/moves/rage_fist.test.ts b/test/moves/rage_fist.test.ts index f44901c5aba..687d805da78 100644 --- a/test/moves/rage_fist.test.ts +++ b/test/moves/rage_fist.test.ts @@ -27,7 +27,7 @@ describe("Moves - Rage Fist", () => { move = allMoves[Moves.RAGE_FIST]; game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .moveset([Moves.RAGE_FIST, Moves.SPLASH, Moves.SUBSTITUTE]) .startingLevel(100) .enemyLevel(1) diff --git a/test/moves/rage_powder.test.ts b/test/moves/rage_powder.test.ts index ab05ae2e0bc..284b558f842 100644 --- a/test/moves/rage_powder.test.ts +++ b/test/moves/rage_powder.test.ts @@ -22,7 +22,7 @@ describe("Moves - Rage Powder", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.enemySpecies(Species.SNORLAX); game.override.startingLevel(100); game.override.enemyLevel(100); diff --git a/test/moves/reflect.test.ts b/test/moves/reflect.test.ts index ac879a7cc2b..473b46bf097 100644 --- a/test/moves/reflect.test.ts +++ b/test/moves/reflect.test.ts @@ -34,7 +34,7 @@ describe("Moves - Reflect", () => { beforeEach(() => { game = new GameManager(phaserGame); globalScene = game.scene; - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.ability(Abilities.NONE); game.override.moveset([Moves.ABSORB, Moves.ROCK_SLIDE, Moves.TACKLE]); game.override.enemyLevel(100); @@ -60,7 +60,7 @@ describe("Moves - Reflect", () => { }); it("reduces damage of physical attacks by a third in a double battle", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); const moveToUse = Moves.ROCK_SLIDE; await game.classicMode.startBattle([Species.SHUCKLE, Species.SHUCKLE]); diff --git a/test/moves/reflect_type.test.ts b/test/moves/reflect_type.test.ts index 78371d35475..efd58bfeadf 100644 --- a/test/moves/reflect_type.test.ts +++ b/test/moves/reflect_type.test.ts @@ -22,7 +22,7 @@ describe("Moves - Reflect Type", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.ability(Abilities.BALL_FETCH).battleType("single").disableCrits().enemyAbility(Abilities.BALL_FETCH); + game.override.ability(Abilities.BALL_FETCH).battleStyle("single").disableCrits().enemyAbility(Abilities.BALL_FETCH); }); it("will make the user Normal/Grass if targetting a typeless Pokemon affected by Forest's Curse", async () => { diff --git a/test/moves/relic_song.test.ts b/test/moves/relic_song.test.ts index d8f1373b4c0..86195e81a24 100644 --- a/test/moves/relic_song.test.ts +++ b/test/moves/relic_song.test.ts @@ -24,7 +24,7 @@ describe("Moves - Relic Song", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.RELIC_SONG, Moves.SPLASH]) - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) .enemySpecies(Species.MAGIKARP) diff --git a/test/moves/retaliate.test.ts b/test/moves/retaliate.test.ts index e916c9ffeaa..9ad7cd7853b 100644 --- a/test/moves/retaliate.test.ts +++ b/test/moves/retaliate.test.ts @@ -26,7 +26,7 @@ describe("Moves - Retaliate", () => { retaliate = allMoves[Moves.RETALIATE]; game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.SNORLAX) .enemyMoveset([Moves.RETALIATE, Moves.RETALIATE, Moves.RETALIATE, Moves.RETALIATE]) .enemyLevel(100) diff --git a/test/moves/revival_blessing.test.ts b/test/moves/revival_blessing.test.ts index 87be20f60ad..860ce5524d4 100644 --- a/test/moves/revival_blessing.test.ts +++ b/test/moves/revival_blessing.test.ts @@ -27,7 +27,7 @@ describe("Moves - Revival Blessing", () => { game.override .moveset([Moves.SPLASH, Moves.REVIVAL_BLESSING, Moves.MEMENTO]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) @@ -88,7 +88,7 @@ describe("Moves - Revival Blessing", () => { it("should revive a player pokemon and immediately send it back out if used in the same turn it fainted in doubles", async () => { game.override - .battleType("double") + .battleStyle("double") .enemyMoveset([Moves.SPLASH, Moves.FISSURE]) .enemyAbility(Abilities.NO_GUARD) .enemyLevel(100); @@ -116,7 +116,7 @@ describe("Moves - Revival Blessing", () => { }); it("should not summon multiple pokemon to the same slot when reviving the enemy ally in doubles", async () => { - game.override.battleType("double").enemyMoveset([Moves.REVIVAL_BLESSING]).moveset([Moves.SPLASH]).startingWave(25); // 2nd rival battle - must have 3+ pokemon + game.override.battleStyle("double").enemyMoveset([Moves.REVIVAL_BLESSING]).moveset([Moves.SPLASH]).startingWave(25); // 2nd rival battle - must have 3+ pokemon await game.classicMode.startBattle([Species.ARCEUS, Species.GIRATINA]); const enemyFainting = game.scene.getEnemyField()[0]; diff --git a/test/moves/role_play.test.ts b/test/moves/role_play.test.ts index 2a899b6e987..d4893212003 100644 --- a/test/moves/role_play.test.ts +++ b/test/moves/role_play.test.ts @@ -25,7 +25,7 @@ describe("Moves - Role Play", () => { game.override .moveset([Moves.SPLASH, Moves.ROLE_PLAY]) .ability(Abilities.ADAPTABILITY) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/rollout.test.ts b/test/moves/rollout.test.ts index 89270c2dfc7..b477fd8274f 100644 --- a/test/moves/rollout.test.ts +++ b/test/moves/rollout.test.ts @@ -24,7 +24,7 @@ describe("Moves - Rollout", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override.disableCrits(); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.starterSpecies(Species.RATTATA); game.override.ability(Abilities.BALL_FETCH); game.override.enemySpecies(Species.BIDOOF); diff --git a/test/moves/roost.test.ts b/test/moves/roost.test.ts index a52b81085c8..e55c76ca220 100644 --- a/test/moves/roost.test.ts +++ b/test/moves/roost.test.ts @@ -25,7 +25,7 @@ describe("Moves - Roost", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.RELICANTH); game.override.startingLevel(100); game.override.enemyLevel(100); diff --git a/test/moves/round.test.ts b/test/moves/round.test.ts index 82f080a25ea..a58efb730f8 100644 --- a/test/moves/round.test.ts +++ b/test/moves/round.test.ts @@ -27,7 +27,7 @@ describe("Moves - Round", () => { game.override .moveset([Moves.SPLASH, Moves.ROUND]) .ability(Abilities.BALL_FETCH) - .battleType("double") + .battleStyle("double") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/safeguard.test.ts b/test/moves/safeguard.test.ts index 675c74f28d0..7804b63f5c5 100644 --- a/test/moves/safeguard.test.ts +++ b/test/moves/safeguard.test.ts @@ -26,7 +26,7 @@ describe("Moves - Safeguard", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.DRATINI) .enemyMoveset([Moves.SAFEGUARD]) .enemyAbility(Abilities.BALL_FETCH) @@ -71,7 +71,7 @@ describe("Moves - Safeguard", () => { }); it("protects ally from status", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.classicMode.startBattle(); diff --git a/test/moves/scale_shot.test.ts b/test/moves/scale_shot.test.ts index 2be632adb54..4731ccf9574 100644 --- a/test/moves/scale_shot.test.ts +++ b/test/moves/scale_shot.test.ts @@ -30,7 +30,7 @@ describe("Moves - Scale Shot", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.SCALE_SHOT]) - .battleType("single") + .battleStyle("single") .disableCrits() .ability(Abilities.NO_GUARD) .passiveAbility(Abilities.SKILL_LINK) diff --git a/test/moves/secret_power.test.ts b/test/moves/secret_power.test.ts index d769b112b70..cbc0cded28b 100644 --- a/test/moves/secret_power.test.ts +++ b/test/moves/secret_power.test.ts @@ -33,7 +33,7 @@ describe("Moves - Secret Power", () => { game.override .moveset([Moves.SECRET_POWER]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyLevel(60) @@ -65,7 +65,7 @@ describe("Moves - Secret Power", () => { .moveset([Moves.FIRE_PLEDGE, Moves.WATER_PLEDGE, Moves.SECRET_POWER, Moves.SPLASH]) .ability(Abilities.SERENE_GRACE) .enemyMoveset([Moves.SPLASH]) - .battleType("double"); + .battleStyle("double"); await game.classicMode.startBattle([Species.BLASTOISE, Species.CHARIZARD]); const sereneGraceAttr = allAbilities[Abilities.SERENE_GRACE].getAttrs(MoveEffectChanceMultiplierAbAttr)[0]; diff --git a/test/moves/shed_tail.test.ts b/test/moves/shed_tail.test.ts index 6744c4e9ed8..845399f6c27 100644 --- a/test/moves/shed_tail.test.ts +++ b/test/moves/shed_tail.test.ts @@ -25,7 +25,7 @@ describe("Moves - Shed Tail", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.SHED_TAIL]) - .battleType("single") + .battleStyle("single") .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH); diff --git a/test/moves/shell_side_arm.test.ts b/test/moves/shell_side_arm.test.ts index a5b065b76cb..e43bf6db037 100644 --- a/test/moves/shell_side_arm.test.ts +++ b/test/moves/shell_side_arm.test.ts @@ -30,7 +30,7 @@ describe("Moves - Shell Side Arm", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.SHELL_SIDE_ARM, Moves.SPLASH]) - .battleType("single") + .battleStyle("single") .startingLevel(100) .enemyLevel(100) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/shell_trap.test.ts b/test/moves/shell_trap.test.ts index 2df94cdb828..f6501c2cd9e 100644 --- a/test/moves/shell_trap.test.ts +++ b/test/moves/shell_trap.test.ts @@ -27,7 +27,7 @@ describe("Moves - Shell Trap", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("double") + .battleStyle("double") .moveset([Moves.SHELL_TRAP, Moves.SPLASH, Moves.BULLDOZE]) .enemySpecies(Species.SNORLAX) .enemyMoveset([Moves.RAZOR_LEAF]) @@ -128,7 +128,7 @@ describe("Moves - Shell Trap", () => { }); it("should not activate from a subsequent physical attack", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); vi.spyOn(allMoves[Moves.RAZOR_LEAF], "priority", "get").mockReturnValue(-4); await game.startBattle([Species.CHARIZARD]); diff --git a/test/moves/simple_beam.test.ts b/test/moves/simple_beam.test.ts index ce86f42671e..225fda28083 100644 --- a/test/moves/simple_beam.test.ts +++ b/test/moves/simple_beam.test.ts @@ -24,7 +24,7 @@ describe("Moves - Simple Beam", () => { game.override .moveset([Moves.SPLASH, Moves.SIMPLE_BEAM]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/sketch.test.ts b/test/moves/sketch.test.ts index dfbf2eca713..c9755189a71 100644 --- a/test/moves/sketch.test.ts +++ b/test/moves/sketch.test.ts @@ -27,7 +27,7 @@ describe("Moves - Sketch", () => { game = new GameManager(phaserGame); game.override .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.SHUCKLE) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/skill_swap.test.ts b/test/moves/skill_swap.test.ts index f807a85eaf6..562e4bb56ed 100644 --- a/test/moves/skill_swap.test.ts +++ b/test/moves/skill_swap.test.ts @@ -25,7 +25,7 @@ describe("Moves - Skill Swap", () => { game.override .moveset([Moves.SPLASH, Moves.SKILL_SWAP]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/sleep_talk.test.ts b/test/moves/sleep_talk.test.ts index d31eff34a7a..cbe3b6d7d3a 100644 --- a/test/moves/sleep_talk.test.ts +++ b/test/moves/sleep_talk.test.ts @@ -28,7 +28,7 @@ describe("Moves - Sleep Talk", () => { .moveset([Moves.SPLASH, Moves.SLEEP_TALK]) .statusEffect(StatusEffect.SLEEP) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/solar_beam.test.ts b/test/moves/solar_beam.test.ts index dffd4f210e5..49605a70c66 100644 --- a/test/moves/solar_beam.test.ts +++ b/test/moves/solar_beam.test.ts @@ -27,7 +27,7 @@ describe("Moves - Solar Beam", () => { game = new GameManager(phaserGame); game.override .moveset(Moves.SOLAR_BEAM) - .battleType("single") + .battleStyle("single") .startingLevel(100) .enemySpecies(Species.SNORLAX) .enemyLevel(100) diff --git a/test/moves/sparkly_swirl.test.ts b/test/moves/sparkly_swirl.test.ts index 6cd357c7e0e..b9df302933c 100644 --- a/test/moves/sparkly_swirl.test.ts +++ b/test/moves/sparkly_swirl.test.ts @@ -34,7 +34,7 @@ describe("Moves - Sparkly Swirl", () => { }); it("should cure status effect of the user, its ally, and all party pokemon", async () => { - game.override.battleType("double").statusEffect(StatusEffect.BURN); + game.override.battleStyle("double").statusEffect(StatusEffect.BURN); await game.classicMode.startBattle([Species.RATTATA, Species.RATTATA, Species.RATTATA]); const [leftPlayer, rightPlayer, partyPokemon] = game.scene.getPlayerParty(); const leftOpp = game.scene.getEnemyPokemon()!; @@ -58,7 +58,7 @@ describe("Moves - Sparkly Swirl", () => { }); it("should not cure status effect of the target/target's allies", async () => { - game.override.battleType("double").enemyStatusEffect(StatusEffect.BURN); + game.override.battleStyle("double").enemyStatusEffect(StatusEffect.BURN); await game.classicMode.startBattle([Species.RATTATA, Species.RATTATA]); const [leftOpp, rightOpp] = game.scene.getEnemyField(); diff --git a/test/moves/speed_swap.test.ts b/test/moves/speed_swap.test.ts index a1385ce5386..2b010885e34 100644 --- a/test/moves/speed_swap.test.ts +++ b/test/moves/speed_swap.test.ts @@ -24,7 +24,7 @@ describe("Moves - Speed Swap", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.NONE) .enemyMoveset(Moves.SPLASH) .enemySpecies(Species.MEW) diff --git a/test/moves/spikes.test.ts b/test/moves/spikes.test.ts index 76af15777bb..3dfa398d7d6 100644 --- a/test/moves/spikes.test.ts +++ b/test/moves/spikes.test.ts @@ -23,7 +23,7 @@ describe("Moves - Spikes", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) .ability(Abilities.BALL_FETCH) @@ -81,7 +81,7 @@ describe("Moves - Spikes", () => { it("should work when all targets fainted", async () => { game.override.enemySpecies(Species.DIGLETT); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.startingLevel(50); await game.classicMode.startBattle([Species.RAYQUAZA, Species.ROWLET]); diff --git a/test/moves/spit_up.test.ts b/test/moves/spit_up.test.ts index d71647bda52..c034117bc64 100644 --- a/test/moves/spit_up.test.ts +++ b/test/moves/spit_up.test.ts @@ -32,7 +32,7 @@ describe("Moves - Spit Up", () => { spitUp = allMoves[Moves.SPIT_UP]; game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.RATTATA); game.override.enemyMoveset(Moves.SPLASH); diff --git a/test/moves/spotlight.test.ts b/test/moves/spotlight.test.ts index 91705dbb2fa..2c4f652e408 100644 --- a/test/moves/spotlight.test.ts +++ b/test/moves/spotlight.test.ts @@ -22,7 +22,7 @@ describe("Moves - Spotlight", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.starterSpecies(Species.AMOONGUSS); game.override.enemySpecies(Species.SNORLAX); game.override.startingLevel(100); diff --git a/test/moves/steamroller.test.ts b/test/moves/steamroller.test.ts index ba96928e01d..b32b4551c81 100644 --- a/test/moves/steamroller.test.ts +++ b/test/moves/steamroller.test.ts @@ -25,7 +25,7 @@ describe("Moves - Steamroller", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.moveset([Moves.STEAMROLLER]).battleType("single").enemyAbility(Abilities.BALL_FETCH); + game.override.moveset([Moves.STEAMROLLER]).battleStyle("single").enemyAbility(Abilities.BALL_FETCH); }); it("should always hit a minimzed target with double damage", async () => { diff --git a/test/moves/stockpile.test.ts b/test/moves/stockpile.test.ts index 033f24d5229..4b8f51c32b2 100644 --- a/test/moves/stockpile.test.ts +++ b/test/moves/stockpile.test.ts @@ -27,7 +27,7 @@ describe("Moves - Stockpile", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.RATTATA); game.override.enemyMoveset(Moves.SPLASH); diff --git a/test/moves/struggle.test.ts b/test/moves/struggle.test.ts index 6b566df9d54..61c6cd23e10 100644 --- a/test/moves/struggle.test.ts +++ b/test/moves/struggle.test.ts @@ -24,7 +24,7 @@ describe("Moves - Struggle", () => { game.override .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/substitute.test.ts b/test/moves/substitute.test.ts index 23f7f4af4b9..2e82078418b 100644 --- a/test/moves/substitute.test.ts +++ b/test/moves/substitute.test.ts @@ -36,7 +36,7 @@ describe("Moves - Substitute", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .moveset([Moves.SUBSTITUTE, Moves.SWORDS_DANCE, Moves.TACKLE, Moves.SPLASH]) .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.INSOMNIA) diff --git a/test/moves/swallow.test.ts b/test/moves/swallow.test.ts index baa03801079..d548522068b 100644 --- a/test/moves/swallow.test.ts +++ b/test/moves/swallow.test.ts @@ -27,7 +27,7 @@ describe("Moves - Swallow", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.RATTATA); game.override.enemyMoveset(Moves.SPLASH); diff --git a/test/moves/syrup_bomb.test.ts b/test/moves/syrup_bomb.test.ts index 1e193793d82..8e9134497d0 100644 --- a/test/moves/syrup_bomb.test.ts +++ b/test/moves/syrup_bomb.test.ts @@ -25,7 +25,7 @@ describe("Moves - SYRUP BOMB", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) .ability(Abilities.BALL_FETCH) diff --git a/test/moves/tackle.test.ts b/test/moves/tackle.test.ts index 44fc698ec62..162836cd181 100644 --- a/test/moves/tackle.test.ts +++ b/test/moves/tackle.test.ts @@ -24,7 +24,7 @@ describe("Moves - Tackle", () => { beforeEach(() => { game = new GameManager(phaserGame); const moveToUse = Moves.TACKLE; - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.MAGIKARP); game.override.startingLevel(1); game.override.startingWave(97); diff --git a/test/moves/tail_whip.test.ts b/test/moves/tail_whip.test.ts index 41c39ab22ca..2d3ade2691d 100644 --- a/test/moves/tail_whip.test.ts +++ b/test/moves/tail_whip.test.ts @@ -25,7 +25,7 @@ describe("Moves - Tail whip", () => { beforeEach(() => { game = new GameManager(phaserGame); const moveToUse = Moves.TAIL_WHIP; - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.RATTATA); game.override.enemyAbility(Abilities.INSOMNIA); game.override.ability(Abilities.INSOMNIA); diff --git a/test/moves/tailwind.test.ts b/test/moves/tailwind.test.ts index 591b94408ce..40bae67b514 100644 --- a/test/moves/tailwind.test.ts +++ b/test/moves/tailwind.test.ts @@ -25,7 +25,7 @@ describe("Moves - Tailwind", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("double") + .battleStyle("double") .moveset([Moves.TAILWIND, Moves.SPLASH]) .enemyMoveset(Moves.SPLASH) .enemyAbility(Abilities.BALL_FETCH) @@ -54,7 +54,7 @@ describe("Moves - Tailwind", () => { }); it("lasts for 4 turns", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); await game.classicMode.startBattle([Species.MAGIKARP]); @@ -77,7 +77,7 @@ describe("Moves - Tailwind", () => { }); it("does not affect the opposing side", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); await game.classicMode.startBattle([Species.MAGIKARP]); diff --git a/test/moves/tar_shot.test.ts b/test/moves/tar_shot.test.ts index ac3ba534446..68f19e3ab51 100644 --- a/test/moves/tar_shot.test.ts +++ b/test/moves/tar_shot.test.ts @@ -24,7 +24,7 @@ describe("Moves - Tar Shot", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) .enemySpecies(Species.TANGELA) diff --git a/test/moves/taunt.test.ts b/test/moves/taunt.test.ts index adc1434c7dd..e0bb13c61fb 100644 --- a/test/moves/taunt.test.ts +++ b/test/moves/taunt.test.ts @@ -23,7 +23,7 @@ describe("Moves - Taunt", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset([Moves.TAUNT, Moves.SPLASH]) .enemySpecies(Species.SHUCKLE) diff --git a/test/moves/telekinesis.test.ts b/test/moves/telekinesis.test.ts index 1355cb975f3..d11cc0861f0 100644 --- a/test/moves/telekinesis.test.ts +++ b/test/moves/telekinesis.test.ts @@ -27,7 +27,7 @@ describe("Moves - Telekinesis", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.TELEKINESIS, Moves.TACKLE, Moves.MUD_SHOT, Moves.SMACK_DOWN]) - .battleType("single") + .battleStyle("single") .enemySpecies(Species.SNORLAX) .enemyLevel(60) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/tera_blast.test.ts b/test/moves/tera_blast.test.ts index c1a2b999fa0..5dc3a914a2e 100644 --- a/test/moves/tera_blast.test.ts +++ b/test/moves/tera_blast.test.ts @@ -34,7 +34,7 @@ describe("Moves - Tera Blast", () => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .disableCrits() .starterSpecies(Species.FEEBAS) .moveset([Moves.TERA_BLAST]) diff --git a/test/moves/tera_starstorm.test.ts b/test/moves/tera_starstorm.test.ts index 9f97b2a51aa..5ae0c575599 100644 --- a/test/moves/tera_starstorm.test.ts +++ b/test/moves/tera_starstorm.test.ts @@ -25,7 +25,7 @@ describe("Moves - Tera Starstorm", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.TERA_STARSTORM, Moves.SPLASH]) - .battleType("double") + .battleStyle("double") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) .enemyLevel(30) @@ -33,7 +33,7 @@ describe("Moves - Tera Starstorm", () => { }); it("changes type to Stellar when used by Terapagos in its Stellar Form", async () => { - game.override.battleType("single"); + game.override.battleStyle("single"); await game.classicMode.startBattle([Species.TERAPAGOS]); const terapagos = game.scene.getPlayerPokemon()!; diff --git a/test/moves/thousand_arrows.test.ts b/test/moves/thousand_arrows.test.ts index 109fc2c6936..7259fda8560 100644 --- a/test/moves/thousand_arrows.test.ts +++ b/test/moves/thousand_arrows.test.ts @@ -24,7 +24,7 @@ describe("Moves - Thousand Arrows", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.TOGETIC); game.override.startingLevel(100); game.override.enemyLevel(100); diff --git a/test/moves/throat_chop.test.ts b/test/moves/throat_chop.test.ts index 755e60fe425..aaae9c0f5bb 100644 --- a/test/moves/throat_chop.test.ts +++ b/test/moves/throat_chop.test.ts @@ -24,7 +24,7 @@ describe("Moves - Throat Chop", () => { game = new GameManager(phaserGame); game.override .moveset(Array(4).fill(Moves.GROWL)) - .battleType("single") + .battleStyle("single") .ability(Abilities.BALL_FETCH) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Array(4).fill(Moves.THROAT_CHOP)) diff --git a/test/moves/thunder_wave.test.ts b/test/moves/thunder_wave.test.ts index 9f907e38b62..abfb5828d3b 100644 --- a/test/moves/thunder_wave.test.ts +++ b/test/moves/thunder_wave.test.ts @@ -24,7 +24,7 @@ describe("Moves - Thunder Wave", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .starterSpecies(Species.PIKACHU) .moveset([Moves.THUNDER_WAVE]) .enemyMoveset(Moves.SPLASH); diff --git a/test/moves/tidy_up.test.ts b/test/moves/tidy_up.test.ts index 9d98feb13f5..ba7a1e07959 100644 --- a/test/moves/tidy_up.test.ts +++ b/test/moves/tidy_up.test.ts @@ -26,7 +26,7 @@ describe("Moves - Tidy Up", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.enemySpecies(Species.MAGIKARP); game.override.enemyAbility(Abilities.BALL_FETCH); game.override.enemyMoveset(Moves.SPLASH); diff --git a/test/moves/torment.test.ts b/test/moves/torment.test.ts index 75143053321..d06837d2806 100644 --- a/test/moves/torment.test.ts +++ b/test/moves/torment.test.ts @@ -24,7 +24,7 @@ describe("Moves - Torment", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset([Moves.TORMENT, Moves.SPLASH]) .enemySpecies(Species.SHUCKLE) diff --git a/test/moves/toxic.test.ts b/test/moves/toxic.test.ts index f2b1f82fe02..f908d27ec7e 100644 --- a/test/moves/toxic.test.ts +++ b/test/moves/toxic.test.ts @@ -23,7 +23,7 @@ describe("Moves - Toxic", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("single").moveset(Moves.TOXIC).enemySpecies(Species.MAGIKARP).enemyMoveset(Moves.SPLASH); + game.override.battleStyle("single").moveset(Moves.TOXIC).enemySpecies(Species.MAGIKARP).enemyMoveset(Moves.SPLASH); }); it("should be guaranteed to hit if user is Poison-type", async () => { diff --git a/test/moves/toxic_spikes.test.ts b/test/moves/toxic_spikes.test.ts index d457ec5cb56..624db27bb92 100644 --- a/test/moves/toxic_spikes.test.ts +++ b/test/moves/toxic_spikes.test.ts @@ -28,7 +28,7 @@ describe("Moves - Toxic Spikes", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .startingWave(5) .enemySpecies(Species.RATTATA) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/transform.test.ts b/test/moves/transform.test.ts index d37decf28f4..5bcb7c7ed4c 100644 --- a/test/moves/transform.test.ts +++ b/test/moves/transform.test.ts @@ -26,7 +26,7 @@ describe("Moves - Transform", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MEW) .enemyLevel(200) .enemyAbility(Abilities.BEAST_BOOST) diff --git a/test/moves/trick_or_treat.test.ts b/test/moves/trick_or_treat.test.ts index 108028f3008..3b32e09f72d 100644 --- a/test/moves/trick_or_treat.test.ts +++ b/test/moves/trick_or_treat.test.ts @@ -25,7 +25,7 @@ describe("Moves - Trick Or Treat", () => { game.override .moveset([Moves.FORESTS_CURSE, Moves.TRICK_OR_TREAT]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/triple_arrows.test.ts b/test/moves/triple_arrows.test.ts index eb434b25815..58ce8a9c528 100644 --- a/test/moves/triple_arrows.test.ts +++ b/test/moves/triple_arrows.test.ts @@ -32,7 +32,7 @@ describe("Moves - Triple Arrows", () => { game.override .ability(Abilities.BALL_FETCH) .moveset([Moves.TRIPLE_ARROWS]) - .battleType("single") + .battleStyle("single") .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.STURDY) .enemyMoveset(Moves.SPLASH); diff --git a/test/moves/u_turn.test.ts b/test/moves/u_turn.test.ts index f1d212f3f47..68bb7fe05c1 100644 --- a/test/moves/u_turn.test.ts +++ b/test/moves/u_turn.test.ts @@ -23,7 +23,7 @@ describe("Moves - U-turn", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .enemySpecies(Species.GENGAR) .startingLevel(90) .startingWave(97) diff --git a/test/moves/upper_hand.test.ts b/test/moves/upper_hand.test.ts index ecfd9f0735c..66359a94ccb 100644 --- a/test/moves/upper_hand.test.ts +++ b/test/moves/upper_hand.test.ts @@ -26,7 +26,7 @@ describe("Moves - Upper Hand", () => { game.override .moveset(Moves.UPPER_HAND) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/moves/whirlwind.test.ts b/test/moves/whirlwind.test.ts index d6124b6c766..b0ca1783f2f 100644 --- a/test/moves/whirlwind.test.ts +++ b/test/moves/whirlwind.test.ts @@ -10,6 +10,9 @@ import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { Status } from "#app/data/status-effect"; import { StatusEffect } from "#enums/status-effect"; +import { BattlerIndex } from "#app/battle"; +import { BattleType } from "#enums/battle-type"; +import { TrainerType } from "#enums/trainer-type"; describe("Moves - Whirlwind", () => { let phaserGame: Phaser.Game; @@ -28,8 +31,8 @@ describe("Moves - Whirlwind", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") - .moveset(Moves.SPLASH) + .battleStyle("single") + .moveset([Moves.SPLASH]) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset([Moves.SPLASH, Moves.WHIRLWIND]) .enemySpecies(Species.PIDGEY); @@ -41,7 +44,8 @@ describe("Moves - Whirlwind", () => { { move: Moves.SKY_DROP, name: "Sky Drop" }, ])("should not hit a flying target: $name (=$move)", async ({ move }) => { game.override.moveset([move]); - await game.classicMode.startBattle([Species.STARAPTOR]); + // Must have a pokemon in the back so that the move misses instead of fails. + await game.classicMode.startBattle([Species.STARAPTOR, Species.MAGIKARP]); const staraptor = game.scene.getPlayerPokemon()!; @@ -156,4 +160,60 @@ describe("Moves - Whirlwind", () => { expect(lapras.isOnField()).toBe(true); expect(eevee.isOnField()).toBe(false); }); + + it("should not pull in the other trainer's pokemon in a partner trainer battle", async () => { + game.override + .battleType(BattleType.TRAINER) + .randomTrainer({ + trainerType: TrainerType.BREEDER, + alwaysDouble: true, + }) + .enemyMoveset([Moves.SPLASH, Moves.LUNAR_DANCE]) + .moveset([Moves.WHIRLWIND, Moves.SPLASH]); + await game.classicMode.startBattle([Species.MAGIKARP, Species.TOTODILE]); + + // expect the enemy to have at least 4 pokemon, necessary for this check to even work + expect(game.scene.getEnemyParty().length, "enemy must have exactly 4 pokemon").toBe(4); + + const user = game.scene.getPlayerPokemon()!; + + console.log(user.getMoveset(false)); + + game.move.select(Moves.SPLASH); + game.move.select(Moves.SPLASH); + await game.forceEnemyMove(Moves.MEMENTO); + await game.forceEnemyMove(Moves.SPLASH); + await game.toNextTurn(); + + // Get the enemy pokemon id so we can check if is the same after switch. + const enemy_id = game.scene.getEnemyPokemon()!.id; + + // Hit the enemy that fainted with whirlwind. + game.move.select(Moves.WHIRLWIND, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, 1); + + await game.forceEnemyMove(Moves.SPLASH); + await game.forceEnemyMove(Moves.SPLASH); + + await game.toNextTurn(); + + // Expect the enemy pokemon to not have switched out. + expect(game.scene.getEnemyPokemon()!.id).toBe(enemy_id); + }); + + it("should force a wild pokemon to flee", async () => { + game.override + .battleType(BattleType.WILD) + .moveset([Moves.WHIRLWIND, Moves.SPLASH]) + .enemyMoveset(Moves.SPLASH) + .ability(Abilities.BALL_FETCH); + await game.classicMode.startBattle([Species.MAGIKARP]); + + const user = game.scene.getPlayerPokemon()!; + + game.move.select(Moves.WHIRLWIND); + await game.phaseInterceptor.to("BerryPhase"); + + expect(user.getLastXMoves(1)[0].result).toBe(MoveResult.SUCCESS); + }); }); diff --git a/test/moves/wide_guard.test.ts b/test/moves/wide_guard.test.ts index c466f104f67..85ebad806d7 100644 --- a/test/moves/wide_guard.test.ts +++ b/test/moves/wide_guard.test.ts @@ -25,7 +25,7 @@ describe("Moves - Wide Guard", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override.battleType("double"); + game.override.battleStyle("double"); game.override.moveset([Moves.WIDE_GUARD, Moves.SPLASH, Moves.SURF]); diff --git a/test/moves/will_o_wisp.test.ts b/test/moves/will_o_wisp.test.ts index 0d19fec954c..b4e4975896b 100644 --- a/test/moves/will_o_wisp.test.ts +++ b/test/moves/will_o_wisp.test.ts @@ -26,7 +26,7 @@ describe("Moves - Will-O-Wisp", () => { game.override .moveset([Moves.WILL_O_WISP, Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/phases/form-change-phase.test.ts b/test/phases/form-change-phase.test.ts index deac21ed0dd..974c64d9e5a 100644 --- a/test/phases/form-change-phase.test.ts +++ b/test/phases/form-change-phase.test.ts @@ -27,7 +27,7 @@ describe("Form Change Phase", () => { game.override .moveset([Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) diff --git a/test/phases/frenzy-move-reset.test.ts b/test/phases/frenzy-move-reset.test.ts index 2f628f8a8c4..6d3ec767722 100644 --- a/test/phases/frenzy-move-reset.test.ts +++ b/test/phases/frenzy-move-reset.test.ts @@ -25,7 +25,7 @@ describe("Frenzy Move Reset", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .battleType("single") + .battleStyle("single") .disableCrits() .starterSpecies(Species.MAGIKARP) .moveset(Moves.THRASH) diff --git a/test/phases/game-over-phase.test.ts b/test/phases/game-over-phase.test.ts index 438efc85167..40473a022cb 100644 --- a/test/phases/game-over-phase.test.ts +++ b/test/phases/game-over-phase.test.ts @@ -27,7 +27,7 @@ describe("Game Over Phase", () => { game.override .moveset([Moves.MEMENTO, Moves.ICE_BEAM, Moves.SPLASH]) .ability(Abilities.BALL_FETCH) - .battleType("single") + .battleStyle("single") .disableCrits() .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) diff --git a/test/reload.test.ts b/test/reload.test.ts index f54885eccfb..c69c0f9f484 100644 --- a/test/reload.test.ts +++ b/test/reload.test.ts @@ -48,7 +48,7 @@ describe("Reload", () => { it("should not have RNG inconsistencies after a biome switch", async () => { game.override .startingWave(10) - .battleType("single") + .battleStyle("single") .startingLevel(100) // Avoid levelling up .disableTrainerWaves() .moveset([Moves.SPLASH]) @@ -81,7 +81,7 @@ describe("Reload", () => { game.override .startingWave(10) .startingBiome(Biome.ICE_CAVE) // Will lead to Snowy Forest with randomly generated weather - .battleType("single") + .battleStyle("single") .startingLevel(100) // Avoid levelling up .disableTrainerWaves() .moveset([Moves.SPLASH]) @@ -116,7 +116,7 @@ describe("Reload", () => { }, 20000); it("should not have RNG inconsistencies at a Daily run double battle", async () => { - game.override.battleType("double"); + game.override.battleStyle("double"); await game.dailyMode.startBattle(); const preReloadRngState = Phaser.Math.RND.state(); @@ -129,7 +129,7 @@ describe("Reload", () => { }, 20000); it("should not have RNG inconsistencies at a Daily run Gym Leader fight", async () => { - game.override.battleType("single").startingWave(40); + game.override.battleStyle("single").startingWave(40); await game.dailyMode.startBattle(); const preReloadRngState = Phaser.Math.RND.state(); @@ -142,7 +142,7 @@ describe("Reload", () => { }, 20000); it("should not have RNG inconsistencies at a Daily run regular trainer fight", async () => { - game.override.battleType("single").startingWave(45); + game.override.battleStyle("single").startingWave(45); await game.dailyMode.startBattle(); const preReloadRngState = Phaser.Math.RND.state(); @@ -155,7 +155,7 @@ describe("Reload", () => { }, 20000); it("should not have RNG inconsistencies at a Daily run wave 50 Boss fight", async () => { - game.override.battleType("single").startingWave(50); + game.override.battleStyle("single").startingWave(50); await game.runToFinalBossEncounter([Species.BULBASAUR], GameModes.DAILY); const preReloadRngState = Phaser.Math.RND.state(); diff --git a/test/system/game_data.test.ts b/test/system/game_data.test.ts index 93e615711c4..94e82949fe6 100644 --- a/test/system/game_data.test.ts +++ b/test/system/game_data.test.ts @@ -22,7 +22,7 @@ describe("System - Game Data", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.SPLASH]) - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH); }); diff --git a/test/testUtils/gameManagerUtils.ts b/test/testUtils/gameManagerUtils.ts index 11636bd66b4..9e9c8f15f96 100644 --- a/test/testUtils/gameManagerUtils.ts +++ b/test/testUtils/gameManagerUtils.ts @@ -1,4 +1,5 @@ -import Battle, { BattleType } from "#app/battle"; +import Battle from "#app/battle"; +import { BattleType } from "#enums/battle-type"; import type BattleScene from "#app/battle-scene"; import { getDailyRunStarters } from "#app/data/daily-run"; import { Gender } from "#app/data/gender"; diff --git a/test/testUtils/helpers/overridesHelper.ts b/test/testUtils/helpers/overridesHelper.ts index 0ed1511255b..d570a1a4195 100644 --- a/test/testUtils/helpers/overridesHelper.ts +++ b/test/testUtils/helpers/overridesHelper.ts @@ -15,6 +15,8 @@ import type { WeatherType } from "#enums/weather-type"; import { expect, vi } from "vitest"; import { GameManagerHelper } from "./gameManagerHelper"; import { shiftCharCodes } from "#app/utils"; +import type { RandomTrainerOverride } from "#app/overrides"; +import type { BattleType } from "#enums/battle-type"; /** * Helper to handle overrides in tests @@ -28,7 +30,7 @@ export class OverridesHelper extends GameManagerHelper { /** * Override the starting biome * @warning Any event listeners that are attached to [NewArenaEvent](events\battle-scene.ts) may need to be handled down the line - * @param biome the biome to set + * @param biome - The biome to set */ public startingBiome(biome: Biome): this { this.game.scene.newArena(biome); @@ -37,8 +39,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the starting wave (index) - * @param wave the wave (index) to set. Classic: `1`-`200` + * Override the starting wave index + * @param wave - The wave to set. Classic: `1`-`200` * @returns `this` */ public startingWave(wave: number): this { @@ -48,8 +50,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the player (pokemon) starting level - * @param level the (pokemon) level to set + * Override the player pokemon's starting level + * @param level - The level to set * @returns `this` */ public startingLevel(level: Species | number): this { @@ -60,7 +62,7 @@ export class OverridesHelper extends GameManagerHelper { /** * Override the XP Multiplier - * @param value the XP multiplier to set + * @param value - The XP multiplier to set * @returns `this` */ public xpMultiplier(value: number): this { @@ -71,7 +73,7 @@ export class OverridesHelper extends GameManagerHelper { /** * Override the wave level cap - * @param cap the level cap value to set; 0 uses normal level caps and negative values + * @param cap - The level cap value to set; 0 uses normal level caps and negative values * disable it completely * @returns `this` */ @@ -90,8 +92,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the player (pokemon) starting held items - * @param items the items to hold + * Override the player pokemon's starting held items + * @param items - The items to hold * @returns `this` */ public startingHeldItems(items: ModifierOverride[]): this { @@ -101,8 +103,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the player (pokemon) {@linkcode Species | species} - * @param species the (pokemon) {@linkcode Species | species} to set + * Override the player pokemon's {@linkcode Species | species} + * @param species - The {@linkcode Species | species} to set * @returns `this` */ public starterSpecies(species: Species | number): this { @@ -112,7 +114,7 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the player (pokemon) to be a random fusion + * Override the player pokemon to be a random fusion * @returns `this` */ public enableStarterFusion(): this { @@ -122,8 +124,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the player (pokemon) fusion species - * @param species the fusion species to set + * Override the player pokemon's fusion species + * @param species - The fusion species to set * @returns `this` */ public starterFusionSpecies(species: Species | number): this { @@ -133,8 +135,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the player (pokemons) forms - * @param forms the (pokemon) forms to set + * Override the player pokemon's forms + * @param forms - The forms to set * @returns `this` */ public starterForms(forms: Partial>): this { @@ -148,7 +150,7 @@ export class OverridesHelper extends GameManagerHelper { /** * Override the player's starting modifiers - * @param modifiers the modifiers to set + * @param modifiers - The modifiers to set * @returns `this` */ public startingModifier(modifiers: ModifierOverride[]): this { @@ -158,8 +160,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the player (pokemon) {@linkcode Abilities | ability}. - * @param ability the (pokemon) {@linkcode Abilities | ability} to set + * Override the player pokemon's {@linkcode Abilities | ability}. + * @param ability - The {@linkcode Abilities | ability} to set * @returns `this` */ public ability(ability: Abilities): this { @@ -169,8 +171,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the player (pokemon) **passive** {@linkcode Abilities | ability} - * @param passiveAbility the (pokemon) **passive** {@linkcode Abilities | ability} to set + * Override the player pokemon's **passive** {@linkcode Abilities | ability} + * @param passiveAbility - The **passive** {@linkcode Abilities | ability} to set * @returns `this` */ public passiveAbility(passiveAbility: Abilities): this { @@ -180,8 +182,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Forces the status of the player (pokemon) **passive** {@linkcode Abilities | ability} - * @param hasPassiveAbility forces the passive to be active if `true`, inactive if `false` + * Forces the status of the player pokemon **passive** {@linkcode Abilities | ability} + * @param hasPassiveAbility - Forces the passive to be active if `true`, inactive if `false` * @returns `this` */ public hasPassiveAbility(hasPassiveAbility: boolean | null): this { @@ -194,8 +196,8 @@ export class OverridesHelper extends GameManagerHelper { return this; } /** - * Override the player (pokemon) {@linkcode Moves | moves}set - * @param moveset the {@linkcode Moves | moves}set to set + * Override the player pokemon's {@linkcode Moves | moves}set + * @param moveset - The {@linkcode Moves | moves}set to set * @returns `this` */ public moveset(moveset: Moves | Moves[]): this { @@ -209,8 +211,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the player (pokemon) {@linkcode StatusEffect | status-effect} - * @param statusEffect the {@linkcode StatusEffect | status-effect} to set + * Override the player pokemon's {@linkcode StatusEffect | status-effect} + * @param statusEffect - The {@linkcode StatusEffect | status-effect} to set * @returns */ public statusEffect(statusEffect: StatusEffect): this { @@ -229,6 +231,19 @@ export class OverridesHelper extends GameManagerHelper { return this; } + /** + * Override the trainer chosen when a random trainer is selected. + * + * Does not force the battle to be a trainer battle. + * @see {@linkcode setBattleType} + * @returns `this` + */ + public randomTrainer(trainer: RandomTrainerOverride | null): this { + vi.spyOn(Overrides, "RANDOM_TRAINER_OVERRIDE", "get").mockReturnValue(trainer); + this.log("Partner battle is forced!"); + return this; + } + /** * Override each wave to not have critical hits * @returns `this` @@ -240,8 +255,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the {@linkcode WeatherType | weather (type)} - * @param type {@linkcode WeatherType | weather type} to set + * Override the {@linkcode WeatherType | weather type} + * @param type - The {@linkcode WeatherType | weather type} to set * @returns `this` */ public weather(type: WeatherType): this { @@ -252,7 +267,7 @@ export class OverridesHelper extends GameManagerHelper { /** * Override the seed - * @param seed the seed to set + * @param seed - The seed to set * @returns `this` */ public seed(seed: string): this { @@ -264,20 +279,36 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the battle type (e.g., single or double). - * @see {@linkcode Overrides.BATTLE_TYPE_OVERRIDE} - * @param battleType battle type to set + * Override the battle style (e.g., single or double). + * @see {@linkcode Overrides.BATTLE_STYLE_OVERRIDE} + * @param battleStyle - The battle style to set * @returns `this` */ - public battleType(battleType: BattleStyle | null): this { - vi.spyOn(Overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue(battleType); - this.log(battleType === null ? "Battle type override disabled!" : `Battle type set to ${battleType}!`); + public battleStyle(battleStyle: BattleStyle | null): this { + vi.spyOn(Overrides, "BATTLE_STYLE_OVERRIDE", "get").mockReturnValue(battleStyle); + this.log(battleStyle === null ? "Battle type override disabled!" : `Battle type set to ${battleStyle}!`); return this; } /** - * Override the enemy (pokemon) {@linkcode Species | species} - * @param species the (pokemon) {@linkcode Species | species} to set + * Override the battle type (e.g., WILD, or Trainer) for non-scripted battles. + * @see {@linkcode Overrides.BATTLE_TYPE_OVERRIDE} + * @param battleType - The battle type to set + * @returns `this` + */ + public battleType(battleType: Exclude): this { + vi.spyOn(Overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue(battleType); + this.log( + battleType === null + ? "Battle type override disabled!" + : `Battle type set to ${battleType[battleType]} (=${battleType})!`, + ); + return this; + } + + /** + * Override the {@linkcode Species | species} of enemy pokemon + * @param species - The {@linkcode Species | species} to set * @returns `this` */ public enemySpecies(species: Species | number): this { @@ -287,7 +318,7 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the enemy (pokemon) to be a random fusion + * Override the enemy pokemon to be a random fusion * @returns `this` */ public enableEnemyFusion(): this { @@ -297,8 +328,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the enemy (pokemon) fusion species - * @param species the fusion species to set + * Override the enemy pokemon fusion species + * @param species - The fusion species to set * @returns `this` */ public enemyFusionSpecies(species: Species | number): this { @@ -308,8 +339,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the enemy (pokemon) {@linkcode Abilities | ability} - * @param ability the (pokemon) {@linkcode Abilities | ability} to set + * Override the {@linkcode Abilities | ability} of enemy pokemon + * @param ability - The {@linkcode Abilities | ability} to set * @returns `this` */ public enemyAbility(ability: Abilities): this { @@ -319,8 +350,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the enemy (pokemon) **passive** {@linkcode Abilities | ability} - * @param passiveAbility the (pokemon) **passive** {@linkcode Abilities | ability} to set + * Override the **passive** {@linkcode Abilities | ability} of enemy pokemon + * @param passiveAbility - The **passive** {@linkcode Abilities | ability} to set * @returns `this` */ public enemyPassiveAbility(passiveAbility: Abilities): this { @@ -330,8 +361,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Forces the status of the enemy (pokemon) **passive** {@linkcode Abilities | ability} - * @param hasPassiveAbility forces the passive to be active if `true`, inactive if `false` + * Forces the status of the enemy pokemon **passive** {@linkcode Abilities | ability} + * @param hasPassiveAbility - Forces the passive to be active if `true`, inactive if `false` * @returns `this` */ public enemyHasPassiveAbility(hasPassiveAbility: boolean | null): this { @@ -345,8 +376,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the enemy (pokemon) {@linkcode Moves | moves}set - * @param moveset the {@linkcode Moves | moves}set to set + * Override the {@linkcode Moves | move}set of enemy pokemon + * @param moveset - The {@linkcode Moves | move}set to set * @returns `this` */ public enemyMoveset(moveset: Moves | Moves[]): this { @@ -360,8 +391,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the enemy (pokemon) level - * @param level the level to set + * Override the level of enemy pokemon + * @param level - The level to set * @returns `this` */ public enemyLevel(level: number): this { @@ -371,8 +402,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the enemy (pokemon) {@linkcode StatusEffect | status-effect} - * @param statusEffect the {@linkcode StatusEffect | status-effect} to set + * Override the enemy {@linkcode StatusEffect | status-effect} for enemy pokemon + * @param statusEffect - The {@linkcode StatusEffect | status-effect} to set * @returns */ public enemyStatusEffect(statusEffect: StatusEffect): this { @@ -394,7 +425,7 @@ export class OverridesHelper extends GameManagerHelper { /** * Gives the player access to an Unlockable. - * @param unlockable The Unlockable(s) to enable. + * @param unlockable - The Unlockable(s) to enable. * @returns `this` */ public enableUnlockable(unlockable: Unlockables[]): this { @@ -405,7 +436,7 @@ export class OverridesHelper extends GameManagerHelper { /** * Override the items rolled at the end of a battle - * @param items the items to be rolled + * @param items - The items to be rolled * @returns `this` */ public itemRewards(items: ModifierOverride[]): this { @@ -463,8 +494,8 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the enemy (Pokemon) to have the given amount of health segments - * @param healthSegments the number of segments to give + * Override the enemy Pokemon to have the given amount of health segments + * @param healthSegments - The number of segments to give * - `0` (default): the health segments will be handled like in the game based on wave, level and species * - `1`: the Pokemon will not be a boss * - `2`+: the Pokemon will be a boss with the given number of health segments @@ -493,7 +524,7 @@ export class OverridesHelper extends GameManagerHelper { /** * Override the encounter chance for a mystery encounter. - * @param percentage the encounter chance in % + * @param percentage - The encounter chance in % * @returns `this` */ public mysteryEncounterChance(percentage: number): this { diff --git a/test/ui/battle_info.test.ts b/test/ui/battle_info.test.ts index 4c6274d5efb..c4548adc49c 100644 --- a/test/ui/battle_info.test.ts +++ b/test/ui/battle_info.test.ts @@ -32,7 +32,7 @@ describe("UI - Battle Info", () => { game = new GameManager(phaserGame); game.override .moveset([Moves.GUILLOTINE, Moves.SPLASH]) - .battleType("single") + .battleStyle("single") .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) .enemySpecies(Species.CATERPIE); diff --git a/test/ui/transfer-item.test.ts b/test/ui/transfer-item.test.ts index 476f0744436..cbbdc1d50ee 100644 --- a/test/ui/transfer-item.test.ts +++ b/test/ui/transfer-item.test.ts @@ -26,7 +26,7 @@ describe("UI - Transfer Items", () => { beforeEach(async () => { game = new GameManager(phaserGame); - game.override.battleType("single"); + game.override.battleStyle("single"); game.override.startingLevel(100); game.override.startingWave(1); game.override.startingHeldItems([ diff --git a/test/ui/type-hints.test.ts b/test/ui/type-hints.test.ts index fa7532fb674..fcb71186448 100644 --- a/test/ui/type-hints.test.ts +++ b/test/ui/type-hints.test.ts @@ -27,12 +27,12 @@ describe("UI - Type Hints", () => { beforeEach(async () => { game = new GameManager(phaserGame); game.settings.typeHints(true); //activate type hints - game.override.battleType("single").startingLevel(100).startingWave(1).enemyMoveset(Moves.SPLASH); + game.override.battleStyle("single").startingLevel(100).startingWave(1).enemyMoveset(Moves.SPLASH); }); it("check immunity color", async () => { game.override - .battleType("single") + .battleStyle("single") .startingLevel(100) .startingWave(1) .enemySpecies(Species.FLORGES) From a6e87c84382765a1922e675b2301eeb9b825e4a9 Mon Sep 17 00:00:00 2001 From: damocleas Date: Fri, 18 Apr 2025 22:25:05 -0400 Subject: [PATCH 35/52] [Bug] [Move] Supercell Slam now hits Minimized targets for double damage and can't miss (#5680) Added AlwaysHitMinimizeAttr and HitsTagForDoubleDamageAttr to Supercell Slam for Minimize --- src/data/moves/move.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 6e5e09839c1..7a2834c0322 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -11231,6 +11231,8 @@ export function initMoves() { new AttackMove(Moves.TEMPER_FLARE, PokemonType.FIRE, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 9) .attr(MovePowerMultiplierAttr, (user, target, move) => user.getLastXMoves(2)[1]?.result === MoveResult.MISS || user.getLastXMoves(2)[1]?.result === MoveResult.FAIL ? 2 : 1), new AttackMove(Moves.SUPERCELL_SLAM, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 100, 95, 15, -1, 0, 9) + .attr(AlwaysHitMinimizeAttr) + .attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED) .attr(MissEffectAttr, crashDamageFunc) .attr(NoEffectAttr, crashDamageFunc) .recklessMove(), From 5854b21da0a191b19413325c1b3ff048e4582323 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Sat, 19 Apr 2025 06:57:03 -0500 Subject: [PATCH 36/52] [Refactor] Remove circular imports part 1 (#5663) * Extract Mode enum out of UI and into its own file Reduces circular imports from 909 to 773 * Move around utility files Reduces cyclical dependencies from 773 to 765 * Remove starterColors and bypassLogin from battle-scene Reduces cyclical dependencies from 765 to 623 * Fix test runner error * Update import for bypassLogin in test * Update mocks for utils in tests * Fix broken tests * Update selectWithTera override * Update path for utils in ab-attr.ts * Update path for utils in ability-class.ts * Fix utils import path in healer.test.ts --- src/account.ts | 4 +- src/battle-scene.ts | 17 +- src/battle.ts | 2 +- src/data/abilities/ab-attrs/ab-attr.ts | 4 +- src/data/abilities/ability-class.ts | 2 +- src/data/abilities/ability.ts | 2 +- src/data/arena-tag.ts | 2 +- src/data/balance/biomes.ts | 2 +- src/data/balance/egg-moves.ts | 2 +- src/data/balance/pokemon-evolutions.ts | 6 +- src/data/battle-anims.ts | 4 +- src/data/battler-tags.ts | 4 +- src/data/berry.ts | 2 +- src/data/challenge.ts | 2 +- src/data/custom-pokemon-data.ts | 2 +- src/data/daily-run.ts | 2 +- src/data/egg.ts | 2 +- src/data/moves/move.ts | 2 +- .../encounters/a-trainers-test-encounter.ts | 2 +- .../encounters/absolute-avarice-encounter.ts | 2 +- .../encounters/berries-abound-encounter.ts | 2 +- .../encounters/bug-type-superfan-encounter.ts | 2 +- .../encounters/clowning-around-encounter.ts | 12 +- .../encounters/dark-deal-encounter.ts | 2 +- .../encounters/delibirdy-encounter.ts | 2 +- .../department-store-sale-encounter.ts | 2 +- .../encounters/fiery-fallout-encounter.ts | 2 +- .../encounters/fight-or-flight-encounter.ts | 2 +- .../global-trade-system-encounter.ts | 9 +- .../mysterious-challengers-encounter.ts | 2 +- .../encounters/mysterious-chest-encounter.ts | 2 +- .../encounters/safari-zone-encounter.ts | 2 +- .../shady-vitamin-dealer-encounter.ts | 2 +- .../teleporting-hijinks-encounter.ts | 2 +- .../the-expert-pokemon-breeder-encounter.ts | 2 +- .../the-pokemon-salesman-encounter.ts | 2 +- .../encounters/training-session-encounter.ts | 2 +- .../encounters/trash-to-treasure-encounter.ts | 2 +- .../encounters/uncommon-breed-encounter.ts | 2 +- .../encounters/weird-dream-encounter.ts | 2 +- .../mystery-encounter-option.ts | 2 +- .../mystery-encounter-requirements.ts | 2 +- .../mystery-encounter-save-data.ts | 2 +- .../mystery-encounters/mystery-encounter.ts | 4 +- .../can-learn-move-requirement.ts | 2 +- .../utils/encounter-dialogue-utils.ts | 2 +- .../utils/encounter-phase-utils.ts | 18 +- .../utils/encounter-pokemon-utils.ts | 20 +-- .../encounter-transformation-sequence.ts | 2 +- src/data/nature.ts | 2 +- src/data/pokeball.ts | 2 +- src/data/pokemon-forms.ts | 2 +- src/data/pokemon-species.ts | 2 +- src/data/status-effect.ts | 2 +- src/data/trainer-names.ts | 2 +- src/data/trainers/TrainerPartyTemplate.ts | 2 +- src/data/trainers/trainer-config.ts | 2 +- src/data/weather.ts | 2 +- src/enums/ui-mode.ts | 47 +++++ src/field/anims.ts | 2 +- src/field/arena.ts | 2 +- src/field/damage-number-handler.ts | 2 +- src/field/mystery-encounter-intro.ts | 2 +- src/field/pokemon-sprite-sparkle-handler.ts | 2 +- src/field/pokemon.ts | 8 +- src/field/trainer.ts | 2 +- src/game-mode.ts | 2 +- src/global-vars/bypass-login.ts | 1 + src/global-vars/starter-colors.ts | 4 + src/inputs-controller.ts | 12 +- src/loading-scene.ts | 2 +- src/modifier/modifier-type.ts | 2 +- src/modifier/modifier.ts | 2 +- src/phases/attempt-capture-phase.ts | 31 ++-- src/phases/attempt-run-phase.ts | 2 +- src/phases/berry-phase.ts | 2 +- src/phases/check-switch-phase.ts | 8 +- src/phases/command-phase.ts | 70 ++++---- src/phases/damage-anim-phase.ts | 2 +- src/phases/egg-hatch-phase.ts | 6 +- src/phases/egg-lapse-phase.ts | 4 +- src/phases/egg-summary-phase.ts | 6 +- src/phases/encounter-phase.ts | 6 +- src/phases/end-evolution-phase.ts | 4 +- src/phases/evolution-phase.ts | 8 +- src/phases/exp-phase.ts | 2 +- src/phases/faint-phase.ts | 2 +- src/phases/form-change-phase.ts | 8 +- src/phases/game-over-modifier-reward-phase.ts | 4 +- src/phases/game-over-phase.ts | 6 +- src/phases/learn-move-phase.ts | 14 +- src/phases/level-cap-phase.ts | 4 +- src/phases/level-up-phase.ts | 4 +- src/phases/login-phase.ts | 17 +- src/phases/money-reward-phase.ts | 2 +- src/phases/move-charge-phase.ts | 2 +- src/phases/move-effect-phase.ts | 4 +- src/phases/move-phase.ts | 2 +- src/phases/mystery-encounter-phases.ts | 12 +- src/phases/obtain-status-effect-phase.ts | 2 +- src/phases/party-heal-phase.ts | 2 +- src/phases/pokemon-anim-phase.ts | 2 +- src/phases/pokemon-heal-phase.ts | 2 +- src/phases/post-turn-status-effect-phase.ts | 2 +- src/phases/reload-session-phase.ts | 6 +- src/phases/revival-blessing-phase.ts | 8 +- src/phases/ribbon-modifier-reward-phase.ts | 4 +- src/phases/scan-ivs-phase.ts | 8 +- src/phases/select-biome-phase.ts | 8 +- src/phases/select-challenge-phase.ts | 4 +- src/phases/select-gender-phase.ts | 6 +- src/phases/select-modifier-phase.ts | 38 ++-- src/phases/select-starter-phase.ts | 8 +- src/phases/select-target-phase.ts | 6 +- src/phases/show-party-exp-bar-phase.ts | 2 +- src/phases/stat-stage-change-phase.ts | 2 +- src/phases/switch-phase.ts | 6 +- src/phases/title-phase.ts | 20 +-- src/phases/trainer-victory-phase.ts | 2 +- src/phases/turn-start-phase.ts | 2 +- src/phases/unavailable-phase.ts | 4 +- src/phases/unlock-phase.ts | 4 +- src/phases/weather-effect-phase.ts | 2 +- src/pipelines/field-sprite.ts | 2 +- src/pipelines/sprite.ts | 2 +- src/plugins/api/api-base.ts | 2 +- src/plugins/api/pokerogue-account-api.ts | 2 +- src/plugins/i18n.ts | 2 +- src/sprites/variant.ts | 2 +- src/starter-colors.ts | 4 + src/starting-wave.ts | 3 + src/system/achv.ts | 2 +- src/system/game-data.ts | 10 +- src/system/game-speed.ts | 2 +- src/system/settings/settings-gamepad.ts | 8 +- src/system/settings/settings-keyboard.ts | 4 +- src/system/settings/settings.ts | 6 +- .../version_migration/versions/v1_0_4.ts | 2 +- .../version_migration/versions/v1_7_0.ts | 2 +- src/timed-event-manager.ts | 4 +- src/tutorial.ts | 6 +- src/ui-inputs.ts | 24 +-- src/ui/abstact-option-select-ui-handler.ts | 12 +- src/ui/achvs-ui-handler.ts | 4 +- src/ui/admin-ui-handler.ts | 20 +-- src/ui/arena-flyout.ts | 2 +- src/ui/autocomplete-ui-handler.ts | 4 +- src/ui/awaitable-ui-handler.ts | 4 +- src/ui/ball-ui-handler.ts | 10 +- src/ui/base-stats-overlay.ts | 2 +- src/ui/battle-flyout.ts | 2 +- src/ui/battle-info.ts | 6 +- src/ui/battle-message-ui-handler.ts | 4 +- src/ui/bgm-bar.ts | 2 +- src/ui/candy-bar.ts | 4 +- src/ui/challenges-select-ui-handler.ts | 6 +- src/ui/char-sprite.ts | 2 +- src/ui/command-ui-handler.ts | 12 +- src/ui/confirm-ui-handler.ts | 4 +- src/ui/daily-run-scoreboard.ts | 2 +- src/ui/egg-gacha-ui-handler.ts | 6 +- src/ui/egg-hatch-scene-handler.ts | 4 +- src/ui/egg-list-ui-handler.ts | 4 +- src/ui/egg-summary-ui-handler.ts | 4 +- src/ui/evolution-scene-handler.ts | 4 +- src/ui/fight-ui-handler.ts | 19 +- src/ui/filter-text.ts | 4 +- src/ui/form-modal-ui-handler.ts | 6 +- src/ui/game-stats-ui-handler.ts | 6 +- src/ui/loading-modal-ui-handler.ts | 4 +- src/ui/login-form-ui-handler.ts | 16 +- src/ui/menu-ui-handler.ts | 46 ++--- src/ui/message-ui-handler.ts | 6 +- src/ui/modal-ui-handler.ts | 4 +- src/ui/modifier-select-ui-handler.ts | 6 +- src/ui/move-info-overlay.ts | 2 +- src/ui/mystery-encounter-ui-handler.ts | 10 +- src/ui/party-ui-handler.ts | 32 ++-- src/ui/pokedex-info-overlay.ts | 2 +- src/ui/pokedex-mon-container.ts | 2 +- src/ui/pokedex-page-ui-handler.ts | 84 ++++----- src/ui/pokedex-scan-ui-handler.ts | 12 +- src/ui/pokedex-ui-handler.ts | 18 +- src/ui/pokemon-hatch-info-container.ts | 4 +- src/ui/pokemon-icon-anim-handler.ts | 2 +- src/ui/pokemon-info-container.ts | 2 +- src/ui/registration-form-ui-handler.ts | 6 +- src/ui/run-history-ui-handler.ts | 8 +- src/ui/run-info-ui-handler.ts | 6 +- src/ui/save-slot-select-ui-handler.ts | 12 +- src/ui/saving-icon-handler.ts | 2 +- src/ui/session-reload-modal-ui-handler.ts | 4 +- .../settings/abstract-binding-ui-handler.ts | 4 +- .../abstract-control-settings-ui-handler.ts | 4 +- .../settings/abstract-settings-ui-handler.ts | 6 +- src/ui/settings/gamepad-binding-ui-handler.ts | 4 +- .../settings/keyboard-binding-ui-handler.ts | 4 +- src/ui/settings/navigationMenu.ts | 18 +- src/ui/settings/option-select-ui-handler.ts | 4 +- src/ui/settings/settings-audio-ui-handler.ts | 4 +- .../settings/settings-display-ui-handler.ts | 4 +- .../settings/settings-gamepad-ui-handler.ts | 6 +- .../settings/settings-keyboard-ui-handler.ts | 10 +- src/ui/settings/settings-ui-handler.ts | 4 +- src/ui/starter-select-ui-handler.ts | 78 ++++----- src/ui/summary-ui-handler.ts | 14 +- src/ui/target-select-ui-handler.ts | 6 +- src/ui/test-dialogue-ui-handler.ts | 12 +- src/ui/time-of-day-widget.ts | 2 +- src/ui/title-ui-handler.ts | 6 +- src/ui/ui-handler.ts | 4 +- src/ui/ui.ts | 163 +++++++----------- src/ui/unavailable-modal-ui-handler.ts | 7 +- src/{utils.ts => utils/common.ts} | 37 ---- src/utils/cookies.ts | 36 ++++ src/utils/utility-vars.ts | 1 + test/abilities/ability_timing.test.ts | 6 +- test/abilities/analytic.test.ts | 2 +- test/abilities/disguise.test.ts | 2 +- test/abilities/healer.test.ts | 2 +- test/abilities/heatproof.test.ts | 2 +- test/abilities/intimidate.test.ts | 10 +- test/abilities/parental_bond.test.ts | 2 +- test/abilities/shield_dust.test.ts | 2 +- test/abilities/stakeout.test.ts | 2 +- test/abilities/wimp_out.test.ts | 2 +- test/account.test.ts | 10 +- test/achievements/achievement.test.ts | 2 +- test/battle/battle.test.ts | 38 ++-- test/battle/special_battle.test.ts | 20 +-- test/boss-pokemon.test.ts | 2 +- test/daily_mode.test.ts | 6 +- test/eggs/egg.test.ts | 2 +- test/enemy_command.test.ts | 2 +- test/escape-calculations.test.ts | 2 +- test/evolution.test.ts | 2 +- test/game-mode.test.ts | 2 +- test/items/dire_hit.test.ts | 4 +- .../double_battle_chance_booster.test.ts | 4 +- test/items/eviolite.test.ts | 2 +- test/items/exp_booster.test.ts | 2 +- test/items/leek.test.ts | 2 +- test/items/light_ball.test.ts | 2 +- test/items/lock_capsule.test.ts | 4 +- test/items/metal_powder.test.ts | 2 +- test/items/quick_powder.test.ts | 2 +- test/items/temp_stat_stage_booster.test.ts | 4 +- test/items/thick_club.test.ts | 2 +- test/moves/aurora_veil.test.ts | 2 +- test/moves/belly_drum.test.ts | 2 +- test/moves/fillet_away.test.ts | 2 +- test/moves/light_screen.test.ts | 2 +- test/moves/multi_target.test.ts | 2 +- test/moves/pledge_moves.test.ts | 2 +- test/moves/reflect.test.ts | 2 +- test/moves/revival_blessing.test.ts | 2 +- test/moves/substitute.test.ts | 4 +- .../mystery-encounter/encounter-test-utils.ts | 30 ++-- .../a-trainers-test-encounter.test.ts | 6 +- .../berries-abound-encounter.test.ts | 6 +- .../bug-type-superfan-encounter.test.ts | 14 +- .../clowning-around-encounter.test.ts | 8 +- .../dancing-lessons-encounter.test.ts | 4 +- .../department-store-sale-encounter.test.ts | 10 +- .../encounters/field-trip-encounter.test.ts | 14 +- .../fight-or-flight-encounter.test.ts | 6 +- .../fun-and-games-encounter.test.ts | 20 +-- .../global-trade-system-encounter.test.ts | 6 +- .../mysterious-challengers-encounter.test.ts | 8 +- .../teleporting-hijinks-encounter.test.ts | 4 +- .../the-strong-stuff-encounter.test.ts | 4 +- .../the-winstrate-challenge-encounter.test.ts | 8 +- .../trash-to-treasure-encounter.test.ts | 6 +- .../encounters/weird-dream-encounter.test.ts | 6 +- test/phases/learn-move-phase.test.ts | 12 +- test/phases/mystery-encounter-phase.test.ts | 10 +- test/phases/phases.test.ts | 8 +- test/phases/select-modifier-phase.test.ts | 24 +-- .../plugins/api/pokerogue-account-api.test.ts | 17 +- test/reload.test.ts | 4 +- test/settingMenu/rebinding_setting.test.ts | 2 +- test/system/game_data.test.ts | 6 +- test/testUtils/gameManager.ts | 46 ++--- test/testUtils/gameWrapper.ts | 9 +- test/testUtils/helpers/challengeModeHelper.ts | 12 +- test/testUtils/helpers/classicModeHelper.ts | 12 +- test/testUtils/helpers/dailyModeHelper.ts | 14 +- test/testUtils/helpers/moveHelper.ts | 20 +-- test/testUtils/helpers/overridesHelper.ts | 2 +- test/testUtils/helpers/reloadHelper.ts | 10 +- test/testUtils/phaseInterceptor.ts | 13 +- test/testUtils/testFileInitialization.ts | 4 +- test/ui/starter-select.test.ts | 110 ++++++------ test/ui/transfer-item.test.ts | 10 +- test/ui/type-hints.test.ts | 10 +- {src => test}/utils.test.ts | 2 +- 296 files changed, 1186 insertions(+), 1147 deletions(-) create mode 100644 src/enums/ui-mode.ts create mode 100644 src/global-vars/bypass-login.ts create mode 100644 src/global-vars/starter-colors.ts create mode 100644 src/starter-colors.ts create mode 100644 src/starting-wave.ts rename src/{utils.ts => utils/common.ts} (92%) create mode 100644 src/utils/cookies.ts create mode 100644 src/utils/utility-vars.ts rename {src => test}/utils.test.ts (95%) diff --git a/src/account.ts b/src/account.ts index 7baa7d10a1a..3416fa6ed5e 100644 --- a/src/account.ts +++ b/src/account.ts @@ -1,7 +1,7 @@ import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; import type { UserInfo } from "#app/@types/UserInfo"; -import { bypassLogin } from "#app/battle-scene"; -import { randomString } from "#app/utils"; +import { bypassLogin } from "./global-vars/bypass-login"; +import { randomString } from "#app/utils/common"; export let loggedInUser: UserInfo | null = null; // This is a random string that is used to identify the client session - unique per session (tab or window) so that the game will only save on the one that the server is expecting diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 0fe4c7f7e4f..ecaffc5ed07 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -18,7 +18,7 @@ import { isNullOrUndefined, BooleanHolder, type Constructor, -} from "#app/utils"; +} from "#app/utils/common"; import type { Modifier, ModifierPredicate, TurnHeldItemTransferModifier } from "./modifier/modifier"; import { ConsumableModifier, @@ -185,8 +185,8 @@ 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"; - -export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; +import { starterColors } from "./global-vars/starter-colors"; +import { startingWave } from "./starting-wave"; const DEBUG_RNG = false; @@ -194,13 +194,6 @@ const OPP_IVS_OVERRIDE_VALIDATED: number[] = ( Array.isArray(Overrides.OPP_IVS_OVERRIDE) ? Overrides.OPP_IVS_OVERRIDE : new Array(6).fill(Overrides.OPP_IVS_OVERRIDE) ).map(iv => (Number.isNaN(iv) || iv === null || iv > 31 ? -1 : iv)); -export const startingWave = Overrides.STARTING_WAVE_OVERRIDE || 1; - -export let starterColors: StarterColors; -interface StarterColors { - [key: string]: [string, string]; -} - export interface PokeballCounts { [pb: string]: number; } @@ -810,11 +803,11 @@ export default class BattleScene extends SceneBase { } async initStarterColors(): Promise { - if (starterColors) { + if (Object.keys(starterColors).length > 0) { + // already initialized return; } const sc = await this.cachedFetch("./starter-colors.json").then(res => res.json()); - starterColors = {}; for (const key of Object.keys(sc)) { starterColors[key] = sc[key]; } diff --git a/src/battle.ts b/src/battle.ts index 3e2f293065a..6630d53bd67 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -8,7 +8,7 @@ import { shiftCharCodes, randSeedItem, randInt, -} from "#app/utils"; +} from "#app/utils/common"; import Trainer, { TrainerVariant } from "./field/trainer"; import type { GameMode } from "./game-mode"; import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier"; diff --git a/src/data/abilities/ab-attrs/ab-attr.ts b/src/data/abilities/ab-attrs/ab-attr.ts index c8ead691b25..a653c3f372d 100644 --- a/src/data/abilities/ab-attrs/ab-attr.ts +++ b/src/data/abilities/ab-attrs/ab-attr.ts @@ -1,6 +1,6 @@ import type { AbAttrCondition } from "#app/@types/ability-types"; import type Pokemon from "#app/field/pokemon"; -import type * as Utils from "#app/utils"; +import type { BooleanHolder } from "#app/utils/common"; export abstract class AbAttr { public showAbility: boolean; @@ -22,7 +22,7 @@ export abstract class AbAttr { _pokemon: Pokemon, _passive: boolean, _simulated: boolean, - _cancelled: Utils.BooleanHolder | null, + _cancelled: BooleanHolder | null, _args: any[], ): void {} diff --git a/src/data/abilities/ability-class.ts b/src/data/abilities/ability-class.ts index b4cda2482d4..387c5fb328b 100644 --- a/src/data/abilities/ability-class.ts +++ b/src/data/abilities/ability-class.ts @@ -3,7 +3,7 @@ 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 { Constructor } from "#app/utils"; +import type { Constructor } from "#app/utils/common"; export class Ability implements Localizable { public id: Abilities; diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index a3bd9b728f5..55a1a4eb902 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"; +import { BooleanHolder, NumberHolder, toDmgValue, isNullOrUndefined, randSeedItem, randSeedInt, type Constructor } 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"; diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 1fe1eca4bba..2ef98723cea 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import type { Arena } from "#app/field/arena"; import { PokemonType } from "#enums/pokemon-type"; -import { BooleanHolder, NumberHolder, toDmgValue } from "#app/utils"; +import { BooleanHolder, NumberHolder, toDmgValue } from "#app/utils/common"; import { allMoves } from "#app/data/moves/move"; import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; diff --git a/src/data/balance/biomes.ts b/src/data/balance/biomes.ts index c722291c66d..968164c7902 100644 --- a/src/data/balance/biomes.ts +++ b/src/data/balance/biomes.ts @@ -1,5 +1,5 @@ import { PokemonType } from "#enums/pokemon-type"; -import { randSeedInt, getEnumValues } from "#app/utils"; +import { randSeedInt, getEnumValues } from "#app/utils/common"; import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import i18next from "i18next"; diff --git a/src/data/balance/egg-moves.ts b/src/data/balance/egg-moves.ts index 74f6a2c1afb..b0e8d5160fa 100644 --- a/src/data/balance/egg-moves.ts +++ b/src/data/balance/egg-moves.ts @@ -1,5 +1,5 @@ import { allMoves } from "#app/data/moves/move"; -import { getEnumKeys, getEnumValues } from "#app/utils"; +import { getEnumKeys, getEnumValues } from "#app/utils/common"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/src/data/balance/pokemon-evolutions.ts b/src/data/balance/pokemon-evolutions.ts index 17f71f3c3c9..64409c3c989 100644 --- a/src/data/balance/pokemon-evolutions.ts +++ b/src/data/balance/pokemon-evolutions.ts @@ -3,7 +3,7 @@ import { Gender } from "#app/data/gender"; import { PokeballType } from "#enums/pokeball"; import type Pokemon from "#app/field/pokemon"; import { PokemonType } from "#enums/pokemon-type"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt } from "#app/utils/common"; import { WeatherType } from "#enums/weather-type"; import { Nature } from "#enums/nature"; import { Biome } from "#enums/biome"; @@ -14,6 +14,7 @@ import { DamageMoneyRewardModifier, ExtraModifierModifier, MoneyMultiplierModifi import { SpeciesFormKey } from "#enums/species-form-key"; import { speciesStarterCosts } from "./starters"; import i18next from "i18next"; +import { initI18n } from "#app/plugins/i18n"; export enum SpeciesWildEvolutionDelay { @@ -95,6 +96,9 @@ export class SpeciesFormEvolution { public description = ""; constructor(speciesId: Species, preFormKey: string | null, evoFormKey: string | null, level: number, item: EvolutionItem | null, condition: SpeciesEvolutionCondition | null, wildDelay?: SpeciesWildEvolutionDelay) { + if (!i18next.isInitialized) { + initI18n(); + } this.speciesId = speciesId; this.preFormKey = preFormKey; this.evoFormKey = evoFormKey; diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index 511c80bee72..0999e9db6ff 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -2,11 +2,11 @@ import { globalScene } from "#app/global-scene"; import { AttackMove, BeakBlastHeaderAttr, DelayedAttackAttr, SelfStatusMove, allMoves } from "./moves/move"; import { MoveFlags } from "#enums/MoveFlags"; import type Pokemon from "../field/pokemon"; -import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName } from "../utils"; +import { type nil, getFrameMs, getEnumKeys, getEnumValues, animationFileName } from "../utils/common"; import type { BattlerIndex } from "../battle"; import { Moves } from "#enums/moves"; import { SubstituteTag } from "./battler-tags"; -import { isNullOrUndefined } from "../utils"; +import { isNullOrUndefined } from "../utils/common"; import Phaser from "phaser"; import { EncounterAnim } from "#enums/encounter-anims"; diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 9b72f3083fd..3b2421897c9 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -33,7 +33,7 @@ import { PokemonHealPhase } from "#app/phases/pokemon-heal-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"; +import { BooleanHolder, getFrameMs, NumberHolder, toDmgValue } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; @@ -42,7 +42,7 @@ import { Species } from "#enums/species"; import { EFFECTIVE_STATS, getStatKey, Stat, type BattleStat, type EffectiveStat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; import { WeatherType } from "#enums/weather-type"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; export enum BattlerTagLapseType { FAINT, diff --git a/src/data/berry.ts b/src/data/berry.ts index e118b45711c..22950c0beca 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -2,7 +2,7 @@ import { getPokemonNameWithAffix } from "../messages"; import type Pokemon from "../field/pokemon"; import { HitResult } from "../field/pokemon"; import { getStatusEffectHealText } from "./status-effect"; -import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils"; +import { NumberHolder, toDmgValue, randSeedInt } from "#app/utils/common"; import { DoubleBerryEffectAbAttr, PostItemLostAbAttr, diff --git a/src/data/challenge.ts b/src/data/challenge.ts index cc5783ad1fb..f786152ca3d 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -1,4 +1,4 @@ -import { BooleanHolder, type NumberHolder, randSeedItem, deepCopy } from "#app/utils"; +import { BooleanHolder, type NumberHolder, randSeedItem, deepCopy } from "#app/utils/common"; import i18next from "i18next"; import type { DexAttrProps, GameData } from "#app/system/game-data"; import { defaultStarterSpecies } from "#app/system/game-data"; diff --git a/src/data/custom-pokemon-data.ts b/src/data/custom-pokemon-data.ts index d95d9f77b83..704835e9dbc 100644 --- a/src/data/custom-pokemon-data.ts +++ b/src/data/custom-pokemon-data.ts @@ -1,6 +1,6 @@ import type { Abilities } from "#enums/abilities"; import type { PokemonType } from "#enums/pokemon-type"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; import type { Nature } from "#enums/nature"; /** diff --git a/src/data/daily-run.ts b/src/data/daily-run.ts index 3438510d613..8a1632ce160 100644 --- a/src/data/daily-run.ts +++ b/src/data/daily-run.ts @@ -3,7 +3,7 @@ import type { Species } from "#enums/species"; import { globalScene } from "#app/global-scene"; import { PlayerPokemon } from "#app/field/pokemon"; import type { Starter } from "#app/ui/starter-select-ui-handler"; -import { randSeedGauss, randSeedInt, randSeedItem, getEnumValues } from "#app/utils"; +import { randSeedGauss, randSeedInt, randSeedItem, getEnumValues } from "#app/utils/common"; import type { PokemonSpeciesForm } from "#app/data/pokemon-species"; import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; import { speciesStarterCosts } from "#app/data/balance/starters"; diff --git a/src/data/egg.ts b/src/data/egg.ts index 13ab0bec479..55a253e843f 100644 --- a/src/data/egg.ts +++ b/src/data/egg.ts @@ -4,7 +4,7 @@ import type PokemonSpecies from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { speciesStarterCosts } from "#app/data/balance/starters"; import { VariantTier } from "#enums/variant-tier"; -import { randInt, randomString, randSeedInt, getIvsFromId } from "#app/utils"; +import { randInt, randomString, randSeedInt, getIvsFromId } from "#app/utils/common"; import Overrides from "#app/overrides"; import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import type { PlayerPokemon } from "#app/field/pokemon"; diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 7a2834c0322..513ab3f6a74 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"; +import { BooleanHolder, NumberHolder, isNullOrUndefined, toDmgValue, randSeedItem, randSeedInt, getEnumValues, toReadableString, type Constructor } from "#app/utils/common"; import { WeatherType } from "#enums/weather-type"; import type { ArenaTrapTag } from "../arena-tag"; import { ArenaTagSide, WeakenMoveTypeTag } from "../arena-tag"; 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 a49157f8e88..d8af7b6aac8 100644 --- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts +++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts @@ -14,7 +14,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { TrainerType } from "#enums/trainer-type"; import { Species } from "#enums/species"; import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt } from "#app/utils/common"; import i18next from "i18next"; import type { IEggOptions } from "#app/data/egg"; import { EggSourceType } from "#enums/egg-source-types"; diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index 85f40a41e51..0a270aebf37 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -24,7 +24,7 @@ import { BerryModifier, PokemonInstantReviveModifier } from "#app/modifier/modif import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Moves } from "#enums/moves"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { randInt } from "#app/utils"; +import { randInt } from "#app/utils/common"; import { BattlerIndex } from "#app/battle"; import { applyModifierTypeToPlayerPokemon, diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index 94e27e32773..bf49dfdea91 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -13,7 +13,7 @@ import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import type { BerryModifierType, ModifierTypeOption } from "#app/modifier/modifier-type"; import { ModifierPoolType, modifierTypes, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt } from "#app/utils/common"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { globalScene } from "#app/global-scene"; 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 1e4c9a3b957..8dfd1a270bd 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -16,7 +16,7 @@ import { TrainerSlot } from "#enums/trainer-slot"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; import { globalScene } from "#app/global-scene"; -import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils"; +import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils/common"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 5edc2e6bbc5..07688db4583 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -31,9 +31,9 @@ import { import { PokemonType } from "#enums/pokemon-type"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { randSeedInt, randSeedShuffle } from "#app/utils"; +import { randSeedInt, randSeedShuffle } from "#app/utils/common"; import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import type { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler"; import type { PlayerPokemon } from "#app/field/pokemon"; @@ -437,7 +437,7 @@ async function handleSwapAbility() { await showEncounterDialogue(`${namespace}:option.1.apply_ability_dialogue`, `${namespace}:speaker`); await showEncounterText(`${namespace}:option.1.apply_ability_message`); - globalScene.ui.setMode(Mode.MESSAGE).then(() => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { displayYesNoOptions(resolve); }); }); @@ -467,7 +467,7 @@ function displayYesNoOptions(resolve) { maxOptions: 7, yOffset: 0, }; - globalScene.ui.setModeWithoutClear(Mode.OPTION_SELECT, config, null, true); + globalScene.ui.setModeWithoutClear(UiMode.OPTION_SELECT, config, null, true); } function onYesAbilitySwap(resolve) { @@ -477,11 +477,11 @@ function onYesAbilitySwap(resolve) { applyAbilityOverrideToPokemon(pokemon, encounter.misc.ability); encounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender()); - globalScene.ui.setMode(Mode.MESSAGE).then(() => resolve(true)); + globalScene.ui.setMode(UiMode.MESSAGE).then(() => resolve(true)); }; const onPokemonNotSelected = () => { - globalScene.ui.setMode(Mode.MESSAGE).then(() => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { displayYesNoOptions(resolve); }); }; diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index 6c4c8f26deb..85ebf175f43 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -1,5 +1,5 @@ import type { PokemonType } from "#enums/pokemon-type"; -import { isNullOrUndefined, randSeedInt } from "#app/utils"; +import { isNullOrUndefined, randSeedInt } from "#app/utils/common"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import { globalScene } from "#app/global-scene"; diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts index 364484cb511..e57955c324a 100644 --- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts +++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts @@ -32,7 +32,7 @@ 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"; +import { randSeedItem } from "#app/utils/common"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index 9b8e2e24d12..6a26cf19d7f 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -4,7 +4,7 @@ import { } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { ModifierTypeFunc } from "#app/modifier/modifier-type"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt } from "#app/utils/common"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index f0b7a05a21c..f0fb6398334 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -30,7 +30,7 @@ import { PokemonMove } from "#app/field/pokemon"; import { Moves } from "#enums/moves"; import { EncounterBattleAnim } from "#app/data/battle-anims"; import { WeatherType } from "#enums/weather-type"; -import { isNullOrUndefined, randSeedInt } from "#app/utils"; +import { isNullOrUndefined, randSeedInt } from "#app/utils/common"; import { StatusEffect } from "#enums/status-effect"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { 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 595d13cf727..d9b4140c6ee 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -31,7 +31,7 @@ import { 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"; +import { randSeedInt } from "#app/utils/common"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; diff --git a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts index f80620647b0..63db5c7c5d6 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -23,7 +23,14 @@ import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species"; import { getTypeRgb } from "#app/data/type"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { NumberHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle, randSeedItem } from "#app/utils"; +import { + NumberHolder, + isNullOrUndefined, + randInt, + randSeedInt, + randSeedShuffle, + randSeedItem, +} from "#app/utils/common"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index 5f88ca083c0..b10f2f3dba2 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -12,7 +12,7 @@ import { modifierTypes } from "#app/modifier/modifier-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PartyMemberStrength } from "#enums/party-member-strength"; import { globalScene } from "#app/global-scene"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt } from "#app/utils/common"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index c295e36749a..8877bf36ce8 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -18,7 +18,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { GameOverPhase } from "#app/phases/game-over-phase"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt } from "#app/utils/common"; import { Moves } from "#enums/moves"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 8c45fde3079..602a8d397db 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -15,7 +15,7 @@ import { HiddenAbilityRateBoosterModifier, IvScannerModifier } from "#app/modifi import type { EnemyPokemon } from "#app/field/pokemon"; import { PokeballType } from "#enums/pokeball"; import { PlayerGender } from "#enums/player-gender"; -import { NumberHolder, randSeedInt } from "#app/utils"; +import { NumberHolder, randSeedInt } from "#app/utils/common"; import type PokemonSpecies from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index b9476d49fec..79f4b53a73e 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -8,7 +8,7 @@ import { import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt } from "#app/utils/common"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { Species } from "#enums/species"; import { globalScene } from "#app/global-scene"; diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index 806a89a7131..ef3532b080e 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -7,7 +7,7 @@ import { transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt } from "#app/utils/common"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { globalScene } from "#app/global-scene"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; 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 c189e341089..ab2f19cfb77 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 @@ -7,7 +7,7 @@ import { import { trainerConfigs } from "#app/data/trainers/trainer-config"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { globalScene } from "#app/global-scene"; -import { randSeedShuffle } from "#app/utils"; +import { randSeedShuffle } from "#app/utils/common"; import type MysteryEncounter from "../mystery-encounter"; import { MysteryEncounterBuilder } from "../mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; diff --git a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts index fb55c55a1a3..4e8e1c2524e 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -3,7 +3,7 @@ import { transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { isNullOrUndefined, randSeedInt } from "#app/utils"; +import { isNullOrUndefined, randSeedInt } from "#app/utils/common"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { globalScene } from "#app/global-scene"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index e8711be172d..11d00f1dd8c 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -15,7 +15,7 @@ import type { PokemonHeldItemModifier } from "#app/modifier/modifier"; import { AbilityAttr } from "#app/system/game-data"; import PokemonData from "#app/system/pokemon-data"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import { isNullOrUndefined, randSeedShuffle } from "#app/utils"; +import { isNullOrUndefined, randSeedShuffle } from "#app/utils/common"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { globalScene } from "#app/global-scene"; diff --git a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts index e60fe0ddc18..1ff96f21edc 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -27,7 +27,7 @@ import { Moves } from "#enums/moves"; import { BattlerIndex } from "#app/battle"; import { PokemonMove } from "#app/field/pokemon"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt } from "#app/utils/common"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounters/trashToTreasure"; diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index ed1866c7a1b..66c7f7afc56 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -27,7 +27,7 @@ import { getSpriteKeysFromPokemon, } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import PokemonData from "#app/system/pokemon-data"; -import { isNullOrUndefined, randSeedInt } from "#app/utils"; +import { isNullOrUndefined, randSeedInt } from "#app/utils/common"; import type { Moves } from "#enums/moves"; import { BattlerIndex } from "#app/battle"; import { SelfStatusMove } from "#app/data/moves/move"; diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 22ec52e976c..cd9ffefb516 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -17,7 +17,7 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { PokemonMove } from "#app/field/pokemon"; -import { NumberHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils"; +import { NumberHolder, isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils/common"; import type PokemonSpecies from "#app/data/pokemon-species"; import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species"; import type { PokemonHeldItemModifier } from "#app/modifier/modifier"; diff --git a/src/data/mystery-encounters/mystery-encounter-option.ts b/src/data/mystery-encounters/mystery-encounter-option.ts index f360658c2dc..57dd50fa972 100644 --- a/src/data/mystery-encounters/mystery-encounter-option.ts +++ b/src/data/mystery-encounters/mystery-encounter-option.ts @@ -12,7 +12,7 @@ import { } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import type { CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement"; import { CanLearnMoveRequirement } from "./requirements/can-learn-move-requirement"; -import { isNullOrUndefined, randSeedInt } from "#app/utils"; +import { isNullOrUndefined, randSeedInt } from "#app/utils/common"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; // biome-ignore lint/suspicious/noConfusingVoidType: void unions in callbacks are OK diff --git a/src/data/mystery-encounters/mystery-encounter-requirements.ts b/src/data/mystery-encounters/mystery-encounter-requirements.ts index 948e3e96ef0..49fd632932c 100644 --- a/src/data/mystery-encounters/mystery-encounter-requirements.ts +++ b/src/data/mystery-encounters/mystery-encounter-requirements.ts @@ -9,7 +9,7 @@ import { WeatherType } from "#enums/weather-type"; import type { PlayerPokemon } from "#app/field/pokemon"; import { AttackTypeBoosterModifier } from "#app/modifier/modifier"; import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; import type { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; diff --git a/src/data/mystery-encounters/mystery-encounter-save-data.ts b/src/data/mystery-encounters/mystery-encounter-save-data.ts index 7c8110628f0..dd633390e46 100644 --- a/src/data/mystery-encounters/mystery-encounter-save-data.ts +++ b/src/data/mystery-encounters/mystery-encounter-save-data.ts @@ -1,6 +1,6 @@ import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT } from "#app/data/mystery-encounters/mystery-encounters"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; import type { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; export class SeenEncounterData { diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index ff098d4d7dd..e305252ed0f 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -1,11 +1,11 @@ import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils"; +import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils/common"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; import type { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro"; import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt } from "#app/utils/common"; import type { StatusEffect } from "#enums/status-effect"; import type { OptionTextDisplay } from "./mystery-encounter-dialogue"; import type MysteryEncounterDialogue from "./mystery-encounter-dialogue"; diff --git a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts index a7ffe3e26ca..37194aef78e 100644 --- a/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts +++ b/src/data/mystery-encounters/requirements/can-learn-move-requirement.ts @@ -1,7 +1,7 @@ import type { Moves } from "#app/enums/moves"; import type { PlayerPokemon } from "#app/field/pokemon"; import { PokemonMove } from "#app/field/pokemon"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; import { EncounterPokemonRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; import { globalScene } from "#app/global-scene"; diff --git a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts index 94790145687..296d94093d9 100644 --- a/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-dialogue-utils.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import type { TextStyle } from "#app/ui/text"; import { getTextWithColors } from "#app/ui/text"; import { UiTheme } from "#enums/ui-theme"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; import i18next from "i18next"; /** diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 69b0d81520a..d77b70caa31 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -30,8 +30,8 @@ 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"; import { PartyUiMode } from "#app/ui/party-ui-handler"; -import { Mode } from "#app/ui/ui"; -import { isNullOrUndefined, randSeedInt, randomString, randSeedItem } from "#app/utils"; +import { UiMode } from "#enums/ui-mode"; +import { isNullOrUndefined, randSeedInt, randomString, randSeedItem } from "#app/utils/common"; import type { BattlerTagType } from "#enums/battler-tag-type"; import { Biome } from "#enums/biome"; import type { TrainerType } from "#enums/trainer-type"; @@ -563,7 +563,7 @@ export function selectPokemonForOption( // Open party screen to choose pokemon globalScene.ui.setMode( - Mode.PARTY, + UiMode.PARTY, PartyUiMode.SELECT, -1, (slotIndex: number, _option: PartyOption) => { @@ -581,7 +581,7 @@ export function selectPokemonForOption( } // There is a second option to choose after selecting the Pokemon - globalScene.ui.setMode(Mode.MESSAGE).then(() => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { const displayOptions = () => { // Always appends a cancel option to bottom of options const fullOptions = secondaryOptions @@ -623,7 +623,7 @@ export function selectPokemonForOption( if (fullOptions[0].onHover) { fullOptions[0].onHover(); } - globalScene.ui.setModeWithoutClear(Mode.OPTION_SELECT, config, null, true); + globalScene.ui.setModeWithoutClear(UiMode.OPTION_SELECT, config, null, true); }; const textPromptKey = @@ -673,20 +673,20 @@ export function selectOptionThenPokemon( const modeToSetOnExit = globalScene.ui.getMode(); const displayOptions = (config: OptionSelectConfig) => { - globalScene.ui.setMode(Mode.MESSAGE).then(() => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { if (!optionSelectPromptKey) { // Do hover over the starting selection option if (fullOptions[0].onHover) { fullOptions[0].onHover(); } - globalScene.ui.setMode(Mode.OPTION_SELECT, config); + globalScene.ui.setMode(UiMode.OPTION_SELECT, config); } else { showEncounterText(optionSelectPromptKey).then(() => { // Do hover over the starting selection option if (fullOptions[0].onHover) { fullOptions[0].onHover(); } - globalScene.ui.setMode(Mode.OPTION_SELECT, config); + globalScene.ui.setMode(UiMode.OPTION_SELECT, config); }); } }); @@ -695,7 +695,7 @@ export function selectOptionThenPokemon( const selectPokemonAfterOption = (selectedOptionIndex: number) => { // Open party screen to choose a Pokemon globalScene.ui.setMode( - Mode.PARTY, + UiMode.PARTY, PartyUiMode.SELECT, -1, (slotIndex: number, _option: PartyOption) => { diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index a4787e819b8..ed94a46ac18 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import i18next from "i18next"; -import { isNullOrUndefined, randSeedInt } from "#app/utils"; +import { isNullOrUndefined, randSeedInt } from "#app/utils/common"; import { PokemonHeldItemModifier } from "#app/modifier/modifier"; import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; @@ -14,7 +14,7 @@ import { PlayerGender } from "#enums/player-gender"; import { addPokeballCaptureStars, addPokeballOpenParticles } from "#app/field/anims"; import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect"; import { achvs } from "#app/system/achv"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import type { PartyOption } from "#app/ui/party-ui-handler"; import { PartyUiMode } from "#app/ui/party-ui-handler"; import { Species } from "#enums/species"; @@ -714,7 +714,7 @@ export async function catchPokemon( () => { globalScene.pokemonInfoContainer.makeRoomForConfirmUi(1, true); globalScene.ui.setMode( - Mode.CONFIRM, + UiMode.CONFIRM, () => { const newPokemon = globalScene.addPlayerPokemon( pokemon.species, @@ -729,12 +729,12 @@ export async function catchPokemon( pokemon, ); globalScene.ui.setMode( - Mode.SUMMARY, + UiMode.SUMMARY, newPokemon, 0, SummaryUiMode.DEFAULT, () => { - globalScene.ui.setMode(Mode.MESSAGE).then(() => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { promptRelease(); }); }, @@ -749,13 +749,13 @@ export async function catchPokemon( female: pokemon.gender === Gender.FEMALE, }; globalScene.ui.setOverlayMode( - Mode.POKEDEX_PAGE, + UiMode.POKEDEX_PAGE, pokemon.species, pokemon.formIndex, attributes, null, () => { - globalScene.ui.setMode(Mode.MESSAGE).then(() => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { promptRelease(); }); }, @@ -763,11 +763,11 @@ export async function catchPokemon( }, () => { globalScene.ui.setMode( - Mode.PARTY, + UiMode.PARTY, PartyUiMode.RELEASE, 0, (slotIndex: number, _option: PartyOption) => { - globalScene.ui.setMode(Mode.MESSAGE).then(() => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { if (slotIndex < 6) { addToParty(slotIndex); } else { @@ -778,7 +778,7 @@ export async function catchPokemon( ); }, () => { - globalScene.ui.setMode(Mode.MESSAGE).then(() => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { removePokemon(); end(); }); diff --git a/src/data/mystery-encounters/utils/encounter-transformation-sequence.ts b/src/data/mystery-encounters/utils/encounter-transformation-sequence.ts index 15085bb2bf8..578c2efefdb 100644 --- a/src/data/mystery-encounters/utils/encounter-transformation-sequence.ts +++ b/src/data/mystery-encounters/utils/encounter-transformation-sequence.ts @@ -1,5 +1,5 @@ import type { PlayerPokemon } from "#app/field/pokemon"; -import { getFrameMs } from "#app/utils"; +import { getFrameMs } from "#app/utils/common"; import { cos, sin } from "#app/field/anims"; import { getTypeRgb } from "#app/data/type"; import { globalScene } from "#app/global-scene"; diff --git a/src/data/nature.ts b/src/data/nature.ts index 2ab4723c10d..83b3ee7538d 100644 --- a/src/data/nature.ts +++ b/src/data/nature.ts @@ -1,4 +1,4 @@ -import { toReadableString } from "#app/utils"; +import { toReadableString } from "#app/utils/common"; import { TextStyle, getBBCodeFrag } from "../ui/text"; import { Nature } from "#enums/nature"; import { UiTheme } from "#enums/ui-theme"; diff --git a/src/data/pokeball.ts b/src/data/pokeball.ts index b0744237755..7a44ebdda7c 100644 --- a/src/data/pokeball.ts +++ b/src/data/pokeball.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { CriticalCatchChanceBoosterModifier } from "#app/modifier/modifier"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { PokeballType } from "#enums/pokeball"; import i18next from "i18next"; diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index 63e166c7fc4..f76462d2725 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -3,7 +3,7 @@ import type Pokemon from "../field/pokemon"; import { StatusEffect } from "#enums/status-effect"; import { allMoves } from "./moves/move"; import { MoveCategory } from "#enums/MoveCategory"; -import type { Constructor, nil } from "#app/utils"; +import type { Constructor, nil } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 75ea07edd40..95ff28e61e0 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -8,7 +8,7 @@ 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"; +import { isNullOrUndefined, capitalizeString, randSeedInt, randSeedGauss, randSeedItem } 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"; diff --git a/src/data/status-effect.ts b/src/data/status-effect.ts index fe4fa380d46..a90304c9f7d 100644 --- a/src/data/status-effect.ts +++ b/src/data/status-effect.ts @@ -1,4 +1,4 @@ -import { randIntRange } from "#app/utils"; +import { randIntRange } from "#app/utils/common"; import { StatusEffect } from "#enums/status-effect"; import type { ParseKeys } from "i18next"; import i18next from "i18next"; diff --git a/src/data/trainer-names.ts b/src/data/trainer-names.ts index 195e5041d28..8714dad0fc9 100644 --- a/src/data/trainer-names.ts +++ b/src/data/trainer-names.ts @@ -1,5 +1,5 @@ import { TrainerType } from "#enums/trainer-type"; -import { toReadableString } from "#app/utils"; +import { toReadableString } from "#app/utils/common"; class TrainerNameConfig { public urls: string[]; diff --git a/src/data/trainers/TrainerPartyTemplate.ts b/src/data/trainers/TrainerPartyTemplate.ts index adbaacc6b55..5d02ffdc6af 100644 --- a/src/data/trainers/TrainerPartyTemplate.ts +++ b/src/data/trainers/TrainerPartyTemplate.ts @@ -1,4 +1,4 @@ -import { startingWave } from "#app/battle-scene"; +import { startingWave } from "#app/starting-wave"; import { globalScene } from "#app/global-scene"; import { PartyMemberStrength } from "#enums/party-member-strength"; diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index d9922ecc097..fec1d4757e7 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import { modifierTypes } from "#app/modifier/modifier-type"; import { PokemonMove } from "#app/field/pokemon"; -import { toReadableString, isNullOrUndefined, randSeedItem, randSeedInt } from "#app/utils"; +import { toReadableString, isNullOrUndefined, randSeedItem, randSeedInt } from "#app/utils/common"; import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { tmSpecies } from "#app/data/balance/tms"; diff --git a/src/data/weather.ts b/src/data/weather.ts index 31b460bbddb..81559304661 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -5,7 +5,7 @@ import type Pokemon from "../field/pokemon"; import { PokemonType } from "#enums/pokemon-type"; import type Move from "./moves/move"; import { AttackMove } from "./moves/move"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt } from "#app/utils/common"; import { SuppressWeatherEffectAbAttr } from "./abilities/ability"; import { TerrainType, getTerrainName } from "./terrain"; import i18next from "i18next"; diff --git a/src/enums/ui-mode.ts b/src/enums/ui-mode.ts new file mode 100644 index 00000000000..dcf6bd2a238 --- /dev/null +++ b/src/enums/ui-mode.ts @@ -0,0 +1,47 @@ +export enum UiMode { + MESSAGE, + TITLE, + COMMAND, + FIGHT, + BALL, + TARGET_SELECT, + MODIFIER_SELECT, + SAVE_SLOT, + PARTY, + SUMMARY, + STARTER_SELECT, + EVOLUTION_SCENE, + EGG_HATCH_SCENE, + EGG_HATCH_SUMMARY, + CONFIRM, + OPTION_SELECT, + MENU, + MENU_OPTION_SELECT, + SETTINGS, + SETTINGS_DISPLAY, + SETTINGS_AUDIO, + SETTINGS_GAMEPAD, + GAMEPAD_BINDING, + SETTINGS_KEYBOARD, + KEYBOARD_BINDING, + ACHIEVEMENTS, + GAME_STATS, + EGG_LIST, + EGG_GACHA, + POKEDEX, + POKEDEX_SCAN, + POKEDEX_PAGE, + LOGIN_FORM, + REGISTRATION_FORM, + LOADING, + SESSION_RELOAD, + UNAVAILABLE, + CHALLENGE_SELECT, + RENAME_POKEMON, + RUN_HISTORY, + RUN_INFO, + TEST_DIALOGUE, + AUTO_COMPLETE, + ADMIN, + MYSTERY_ENCOUNTER +} diff --git a/src/field/anims.ts b/src/field/anims.ts index eb895c2d8f9..2fd23e4262b 100644 --- a/src/field/anims.ts +++ b/src/field/anims.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import { PokeballType } from "#enums/pokeball"; import type { Variant } from "#app/sprites/variant"; -import { getFrameMs, randGauss } from "#app/utils"; +import { getFrameMs, randGauss } from "#app/utils/common"; export function addPokeballOpenParticles(x: number, y: number, pokeballType: PokeballType): void { switch (pokeballType) { diff --git a/src/field/arena.ts b/src/field/arena.ts index 1bc465c7dbb..f083180490b 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import type { BiomeTierTrainerPools, PokemonPools } from "#app/data/balance/biomes"; import { biomePokemonPools, BiomePoolTier, biomeTrainerPools } from "#app/data/balance/biomes"; -import { randSeedInt, NumberHolder, isNullOrUndefined, type Constructor } from "#app/utils"; +import { randSeedInt, NumberHolder, isNullOrUndefined, type Constructor } from "#app/utils/common"; import type PokemonSpecies from "#app/data/pokemon-species"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { diff --git a/src/field/damage-number-handler.ts b/src/field/damage-number-handler.ts index a527b148fff..bfb85018dd6 100644 --- a/src/field/damage-number-handler.ts +++ b/src/field/damage-number-handler.ts @@ -2,7 +2,7 @@ import { TextStyle, addTextObject } from "../ui/text"; import type { DamageResult } from "./pokemon"; import type Pokemon from "./pokemon"; import { HitResult } from "./pokemon"; -import { formatStat, fixedInt } from "#app/utils"; +import { formatStat, fixedInt } from "#app/utils/common"; import type { BattlerIndex } from "../battle"; import { globalScene } from "#app/global-scene"; diff --git a/src/field/mystery-encounter-intro.ts b/src/field/mystery-encounter-intro.ts index e1fb0c37074..b6212b6b031 100644 --- a/src/field/mystery-encounter-intro.ts +++ b/src/field/mystery-encounter-intro.ts @@ -2,7 +2,7 @@ import type { GameObjects } from "phaser"; import { globalScene } from "#app/global-scene"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import type { Species } from "#enums/species"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import type { Variant } from "#app/sprites/variant"; import { doShinySparkleAnim } from "#app/field/anims"; diff --git a/src/field/pokemon-sprite-sparkle-handler.ts b/src/field/pokemon-sprite-sparkle-handler.ts index d2f69500258..cceb0bd7717 100644 --- a/src/field/pokemon-sprite-sparkle-handler.ts +++ b/src/field/pokemon-sprite-sparkle-handler.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import Pokemon from "./pokemon"; -import { fixedInt, randInt } from "#app/utils"; +import { fixedInt, randInt } from "#app/utils/common"; export default class PokemonSpriteSparkleHandler { private sprites: Set; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 27c4edea297..0242820dcde 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -55,7 +55,7 @@ import { getStarterValueFriendshipCap, speciesStarterCosts, } from "#app/data/balance/starters"; -import { NumberHolder, randSeedInt, getIvsFromId, BooleanHolder, randSeedItem, isNullOrUndefined, getEnumValues, toDmgValue, fixedInt, rgbaToInt, rgbHexToRgba, rgbToHsv, deltaRgb, isBetween, type nil, type Constructor } from "#app/utils"; +import { NumberHolder, randSeedInt, getIvsFromId, BooleanHolder, randSeedItem, isNullOrUndefined, getEnumValues, toDmgValue, fixedInt, rgbaToInt, rgbHexToRgba, rgbToHsv, deltaRgb, isBetween, type nil, type Constructor } from "#app/utils/common"; import type { TypeDamageMultiplier } from "#app/data/type"; import { getTypeDamageMultiplier, getTypeRgb } from "#app/data/type"; import { PokemonType } from "#enums/pokemon-type"; @@ -193,7 +193,7 @@ import { import { allAbilities } from "#app/data/data-lists"; import type PokemonData from "#app/system/pokemon-data"; import { BattlerIndex } from "#app/battle"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import type { PartyOption } from "#app/ui/party-ui-handler"; import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; @@ -6581,7 +6581,7 @@ export class PlayerPokemon extends Pokemon { this.leaveField(switchType === SwitchType.SWITCH); globalScene.ui.setMode( - Mode.PARTY, + UiMode.PARTY, PartyUiMode.FAINT_SWITCH, this.getFieldIndex(), (slotIndex: number, option: PartyOption) => { @@ -6599,7 +6599,7 @@ export class PlayerPokemon extends Pokemon { MoveEndPhase, ); } - globalScene.ui.setMode(Mode.MESSAGE).then(resolve); + globalScene.ui.setMode(UiMode.MESSAGE).then(resolve); }, PartyUiHandler.FilterNonFainted, ); diff --git a/src/field/trainer.ts b/src/field/trainer.ts index 1679e6f12e0..6b0a54b2103 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -11,7 +11,7 @@ import { TrainerSlot } from "#enums/trainer-slot"; import { TrainerPoolTier } from "#enums/trainer-pool-tier"; import { TeraAIMode } from "#enums/tera-ai-mode"; import type { EnemyPokemon } from "#app/field/pokemon"; -import { randSeedWeightedItem, randSeedItem, randSeedInt } from "#app/utils"; +import { randSeedWeightedItem, randSeedItem, randSeedInt } from "#app/utils/common"; import type { PersistentModifier } from "#app/modifier/modifier"; import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; import { getIsInitialized, initI18n } from "#app/plugins/i18n"; diff --git a/src/game-mode.ts b/src/game-mode.ts index 4779fda50e8..dfe6b8cf123 100644 --- a/src/game-mode.ts +++ b/src/game-mode.ts @@ -7,7 +7,7 @@ import type PokemonSpecies from "./data/pokemon-species"; import { allSpecies } from "./data/pokemon-species"; import type { Arena } from "./field/arena"; import Overrides from "#app/overrides"; -import { randSeedInt, randSeedItem } from "#app/utils"; +import { randSeedInt, randSeedItem } from "#app/utils/common"; import { Biome } from "#enums/biome"; import { Species } from "#enums/species"; import { Challenges } from "./enums/challenges"; diff --git a/src/global-vars/bypass-login.ts b/src/global-vars/bypass-login.ts new file mode 100644 index 00000000000..3595a076101 --- /dev/null +++ b/src/global-vars/bypass-login.ts @@ -0,0 +1 @@ +export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; diff --git a/src/global-vars/starter-colors.ts b/src/global-vars/starter-colors.ts new file mode 100644 index 00000000000..6abe028be99 --- /dev/null +++ b/src/global-vars/starter-colors.ts @@ -0,0 +1,4 @@ +export const starterColors: StarterColors = {}; +interface StarterColors { + [key: string]: [string, string]; +} diff --git a/src/inputs-controller.ts b/src/inputs-controller.ts index f92ce3957ab..7fde0f2aca8 100644 --- a/src/inputs-controller.ts +++ b/src/inputs-controller.ts @@ -1,11 +1,11 @@ import Phaser from "phaser"; -import { deepCopy, getEnumValues } from "#app/utils"; +import { deepCopy, getEnumValues } from "#app/utils/common"; import pad_generic from "./configs/inputs/pad_generic"; import pad_unlicensedSNES from "./configs/inputs/pad_unlicensedSNES"; import pad_xbox360 from "./configs/inputs/pad_xbox360"; import pad_dualshock from "./configs/inputs/pad_dualshock"; import pad_procon from "./configs/inputs/pad_procon"; -import { Mode } from "./ui/ui"; +import { UiMode } from "#enums/ui-mode"; import type SettingsGamepadUiHandler from "./ui/settings/settings-gamepad-ui-handler"; import type SettingsKeyboardUiHandler from "./ui/settings/settings-keyboard-ui-handler"; import cfg_keyboard_qwerty from "./configs/inputs/cfg_keyboard_qwerty"; @@ -235,7 +235,7 @@ export class InputsController { if (gamepadName) { this.selectedDevice[Device.GAMEPAD] = gamepadName.toLowerCase(); } - const handler = globalScene.ui?.handlers[Mode.SETTINGS_GAMEPAD] as SettingsGamepadUiHandler; + const handler = globalScene.ui?.handlers[UiMode.SETTINGS_GAMEPAD] as SettingsGamepadUiHandler; handler?.updateChosenGamepadDisplay(); } @@ -248,7 +248,7 @@ export class InputsController { if (layoutKeyboard) { this.selectedDevice[Device.KEYBOARD] = layoutKeyboard.toLowerCase(); } - const handler = globalScene.ui?.handlers[Mode.SETTINGS_KEYBOARD] as SettingsKeyboardUiHandler; + const handler = globalScene.ui?.handlers[UiMode.SETTINGS_KEYBOARD] as SettingsKeyboardUiHandler; handler?.updateChosenKeyboardDisplay(); } @@ -296,7 +296,7 @@ export class InputsController { globalScene.gameData?.saveMappingConfigs(gamepadID, this.configs[gamepadID]); } this.lastSource = "gamepad"; - const handler = globalScene.ui?.handlers[Mode.SETTINGS_GAMEPAD] as SettingsGamepadUiHandler; + const handler = globalScene.ui?.handlers[UiMode.SETTINGS_GAMEPAD] as SettingsGamepadUiHandler; handler?.updateChosenGamepadDisplay(); } @@ -406,7 +406,7 @@ export class InputsController { this.lastSource = "gamepad"; if ( !this.selectedDevice[Device.GAMEPAD] || - (globalScene.ui.getMode() !== Mode.GAMEPAD_BINDING && + (globalScene.ui.getMode() !== UiMode.GAMEPAD_BINDING && this.selectedDevice[Device.GAMEPAD] !== pad.id.toLowerCase()) ) { this.setChosenGamepad(pad.id); diff --git a/src/loading-scene.ts b/src/loading-scene.ts index 4ec2fdf1bb2..914e6e961e2 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -4,7 +4,7 @@ import CacheBustedLoaderPlugin from "#app/plugins/cache-busted-loader-plugin"; import { SceneBase } from "#app/scene-base"; import { WindowVariant, getWindowVariantSuffix } from "#app/ui/ui-theme"; import { isMobile } from "#app/touch-controls"; -import { localPing, getEnumValues, hasAllLocalizedSprites, getEnumKeys } from "#app/utils"; +import { localPing, getEnumValues, hasAllLocalizedSprites, getEnumKeys } from "#app/utils/common"; import { initPokemonPrevolutions, initPokemonStarters } from "#app/data/balance/pokemon-evolutions"; import { initBiomes } from "#app/data/balance/biomes"; import { initEggMoves } from "#app/data/balance/egg-moves"; diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 8feb60c7778..219a6b6344b 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -114,7 +114,7 @@ import { NumberHolder, padInt, randSeedInt, -} from "#app/utils"; +} from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 851fa33cedc..3eaf4e3c510 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"; +import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, toDmgValue } from "#app/utils/common"; import { BattlerTagType } from "#enums/battler-tag-type"; import { BerryType } from "#enums/berry-type"; import type { Moves } from "#enums/moves"; diff --git a/src/phases/attempt-capture-phase.ts b/src/phases/attempt-capture-phase.ts index 78021da4066..795aa7010e1 100644 --- a/src/phases/attempt-capture-phase.ts +++ b/src/phases/attempt-capture-phase.ts @@ -19,7 +19,7 @@ import { achvs } from "#app/system/achv"; import type { PartyOption } from "#app/ui/party-ui-handler"; import { PartyUiMode } from "#app/ui/party-ui-handler"; import { SummaryUiMode } from "#app/ui/summary-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import type { PokeballType } from "#enums/pokeball"; import { StatusEffect } from "#enums/status-effect"; import i18next from "i18next"; @@ -295,7 +295,7 @@ export class AttemptCapturePhase extends PokemonPhase { () => { globalScene.pokemonInfoContainer.makeRoomForConfirmUi(1, true); globalScene.ui.setMode( - Mode.CONFIRM, + UiMode.CONFIRM, () => { const newPokemon = globalScene.addPlayerPokemon( pokemon.species, @@ -310,12 +310,12 @@ export class AttemptCapturePhase extends PokemonPhase { pokemon, ); globalScene.ui.setMode( - Mode.SUMMARY, + UiMode.SUMMARY, newPokemon, 0, SummaryUiMode.DEFAULT, () => { - globalScene.ui.setMode(Mode.MESSAGE).then(() => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { promptRelease(); }); }, @@ -329,19 +329,26 @@ export class AttemptCapturePhase extends PokemonPhase { form: pokemon.formIndex, female: pokemon.gender === Gender.FEMALE, }; - globalScene.ui.setOverlayMode(Mode.POKEDEX_PAGE, pokemon.species, attributes, null, null, () => { - globalScene.ui.setMode(Mode.MESSAGE).then(() => { - promptRelease(); - }); - }); + globalScene.ui.setOverlayMode( + UiMode.POKEDEX_PAGE, + pokemon.species, + attributes, + null, + null, + () => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { + promptRelease(); + }); + }, + ); }, () => { globalScene.ui.setMode( - Mode.PARTY, + UiMode.PARTY, PartyUiMode.RELEASE, this.fieldIndex, (slotIndex: number, _option: PartyOption) => { - globalScene.ui.setMode(Mode.MESSAGE).then(() => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { if (slotIndex < 6) { addToParty(slotIndex); } else { @@ -352,7 +359,7 @@ export class AttemptCapturePhase extends PokemonPhase { ); }, () => { - globalScene.ui.setMode(Mode.MESSAGE).then(() => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { removePokemon(); end(); }); diff --git a/src/phases/attempt-run-phase.ts b/src/phases/attempt-run-phase.ts index 5c51e5c589d..eed5c3c522e 100644 --- a/src/phases/attempt-run-phase.ts +++ b/src/phases/attempt-run-phase.ts @@ -9,7 +9,7 @@ import { StatusEffect } from "#enums/status-effect"; import type { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import i18next from "i18next"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { BattleEndPhase } from "./battle-end-phase"; import { NewBattlePhase } from "./new-battle-phase"; import { PokemonPhase } from "./pokemon-phase"; diff --git a/src/phases/berry-phase.ts b/src/phases/berry-phase.ts index ae593f66f34..b20b1736d4f 100644 --- a/src/phases/berry-phase.ts +++ b/src/phases/berry-phase.ts @@ -4,7 +4,7 @@ import { BerryUsedEvent } from "#app/events/battle-scene"; import { getPokemonNameWithAffix } from "#app/messages"; import { BerryModifier } from "#app/modifier/modifier"; import i18next from "i18next"; -import { BooleanHolder } from "#app/utils"; +import { BooleanHolder } from "#app/utils/common"; import { FieldPhase } from "./field-phase"; import { CommonAnimPhase } from "./common-anim-phase"; import { globalScene } from "#app/global-scene"; diff --git a/src/phases/check-switch-phase.ts b/src/phases/check-switch-phase.ts index ba4837fd7cc..9d73411fd37 100644 --- a/src/phases/check-switch-phase.ts +++ b/src/phases/check-switch-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import { BattleStyle } from "#app/enums/battle-style"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { getPokemonNameWithAffix } from "#app/messages"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import { BattlePhase } from "./battle-phase"; import { SummonMissingPhase } from "./summon-missing-phase"; @@ -64,14 +64,14 @@ export class CheckSwitchPhase extends BattlePhase { null, () => { globalScene.ui.setMode( - Mode.CONFIRM, + UiMode.CONFIRM, () => { - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.unshiftPhase(new SwitchPhase(SwitchType.INITIAL_SWITCH, this.fieldIndex, false, true)); this.end(); }, () => { - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); this.end(); }, ); diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index 30343f92aa3..c3e558e1d86 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -15,12 +15,12 @@ import type { PlayerPokemon, TurnMove } from "#app/field/pokemon"; import { FieldPosition } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +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"; +import { isNullOrUndefined } from "#app/utils/common"; import { ArenaTagSide } from "#app/data/arena-tag"; import { ArenaTagType } from "#app/enums/arena-tag-type"; @@ -38,7 +38,7 @@ export class CommandPhase extends FieldPhase { globalScene.updateGameInfo(); - const commandUiHandler = globalScene.ui.handlers[Mode.COMMAND]; + const commandUiHandler = globalScene.ui.handlers[UiMode.COMMAND]; // If one of these conditions is true, we always reset the cursor to Command.FIGHT const cursorResetEvent = @@ -127,7 +127,7 @@ export class CommandPhase extends FieldPhase { ) { this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, queuedMove); } else { - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); } } } else { @@ -136,9 +136,9 @@ export class CommandPhase extends FieldPhase { globalScene.currentBattle.mysteryEncounter?.skipToFightInput ) { globalScene.ui.clearText(); - globalScene.ui.setMode(Mode.FIGHT, this.fieldIndex); + globalScene.ui.setMode(UiMode.FIGHT, this.fieldIndex); } else { - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); } } } @@ -209,7 +209,7 @@ export class CommandPhase extends FieldPhase { success = true; } else if (cursor < playerPokemon.getMoveset().length) { const move = playerPokemon.getMoveset()[cursor]; - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); // Decides between a Disabled, Not Implemented, or No PP translation message const errorMessage = playerPokemon.isMoveRestricted(move.moveId, playerPokemon) @@ -226,7 +226,7 @@ export class CommandPhase extends FieldPhase { null, () => { globalScene.ui.clearText(); - globalScene.ui.setMode(Mode.FIGHT, this.fieldIndex); + globalScene.ui.setMode(UiMode.FIGHT, this.fieldIndex); }, null, true, @@ -244,27 +244,27 @@ export class CommandPhase extends FieldPhase { globalScene.arena.biomeType === Biome.END && (!globalScene.gameMode.isClassic || globalScene.gameMode.isFreshStartChallenge() || notInDex) ) { - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.showText( i18next.t("battle:noPokeballForce"), null, () => { globalScene.ui.showText("", 0); - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); }, null, true, ); } else if (globalScene.currentBattle.battleType === BattleType.TRAINER) { - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.showText( i18next.t("battle:noPokeballTrainer"), null, () => { globalScene.ui.showText("", 0); - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); }, null, true, @@ -273,14 +273,14 @@ export class CommandPhase extends FieldPhase { globalScene.currentBattle.isBattleMysteryEncounter() && !globalScene.currentBattle.mysteryEncounter!.catchAllowed ) { - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.showText( i18next.t("battle:noPokeballMysteryEncounter"), null, () => { globalScene.ui.showText("", 0); - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); }, null, true, @@ -291,14 +291,14 @@ export class CommandPhase extends FieldPhase { .filter(p => p.isActive(true)) .map(p => p.getBattlerIndex()); if (targets.length > 1) { - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.showText( i18next.t("battle:noPokeballMulti"), null, () => { globalScene.ui.showText("", 0); - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); }, null, true, @@ -311,14 +311,14 @@ export class CommandPhase extends FieldPhase { !targetPokemon?.hasAbility(Abilities.WONDER_GUARD, false, true) && cursor < PokeballType.MASTER_BALL ) { - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.showText( i18next.t("battle:noPokeballStrong"), null, () => { globalScene.ui.showText("", 0); - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); }, null, true, @@ -347,14 +347,14 @@ export class CommandPhase extends FieldPhase { (arena.biomeType === Biome.END || (!isNullOrUndefined(mysteryEncounterFleeAllowed) && !mysteryEncounterFleeAllowed)) ) { - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.showText( i18next.t("battle:noEscapeForce"), null, () => { globalScene.ui.showText("", 0); - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); }, null, true, @@ -364,14 +364,14 @@ export class CommandPhase extends FieldPhase { (currentBattle.battleType === BattleType.TRAINER || currentBattle.mysteryEncounter?.encounterMode === MysteryEncounterMode.TRAINER_BATTLE) ) { - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.showText( i18next.t("battle:noEscapeTrainer"), null, () => { globalScene.ui.showText("", 0); - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); }, null, true, @@ -389,7 +389,7 @@ export class CommandPhase extends FieldPhase { } } else if (trappedAbMessages.length > 0) { if (!isSwitch) { - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); } globalScene.ui.showText( trappedAbMessages[0], @@ -397,7 +397,7 @@ export class CommandPhase extends FieldPhase { () => { globalScene.ui.showText("", 0); if (!isSwitch) { - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); } }, null, @@ -412,8 +412,8 @@ export class CommandPhase extends FieldPhase { break; } if (!isSwitch) { - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.MESSAGE); } const showNoEscapeText = (tag: any) => { globalScene.ui.showText( @@ -429,7 +429,7 @@ export class CommandPhase extends FieldPhase { () => { globalScene.ui.showText("", 0); if (!isSwitch) { - globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); + globalScene.ui.setMode(UiMode.COMMAND, this.fieldIndex); } }, null, @@ -471,6 +471,6 @@ export class CommandPhase extends FieldPhase { } end() { - globalScene.ui.setMode(Mode.MESSAGE).then(() => super.end()); + globalScene.ui.setMode(UiMode.MESSAGE).then(() => super.end()); } } diff --git a/src/phases/damage-anim-phase.ts b/src/phases/damage-anim-phase.ts index 696a2e55b6f..b9581573f2e 100644 --- a/src/phases/damage-anim-phase.ts +++ b/src/phases/damage-anim-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#app/battle"; import { BattleSpec } from "#enums/battle-spec"; import { type DamageResult, HitResult } from "#app/field/pokemon"; -import { fixedInt } from "#app/utils"; +import { fixedInt } from "#app/utils/common"; import { PokemonPhase } from "#app/phases/pokemon-phase"; export class DamageAnimPhase extends PokemonPhase { diff --git a/src/phases/egg-hatch-phase.ts b/src/phases/egg-hatch-phase.ts index 07eeeb0f8ae..69bcf741383 100644 --- a/src/phases/egg-hatch-phase.ts +++ b/src/phases/egg-hatch-phase.ts @@ -8,10 +8,10 @@ import { achvs } from "#app/system/achv"; import EggCounterContainer from "#app/ui/egg-counter-container"; import type EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler"; import PokemonInfoContainer from "#app/ui/pokemon-info-container"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { fixedInt, getFrameMs, randInt } from "#app/utils"; +import { fixedInt, getFrameMs, randInt } from "#app/utils/common"; import type { EggLapsePhase } from "./egg-lapse-phase"; import type { EggHatchData } from "#app/data/egg-hatch-data"; import { doShinySparkleAnim } from "#app/field/anims"; @@ -76,7 +76,7 @@ export class EggHatchPhase extends Phase { start() { super.start(); - globalScene.ui.setModeForceTransition(Mode.EGG_HATCH_SCENE).then(() => { + globalScene.ui.setModeForceTransition(UiMode.EGG_HATCH_SCENE).then(() => { if (!this.egg) { return this.end(); } diff --git a/src/phases/egg-lapse-phase.ts b/src/phases/egg-lapse-phase.ts index 397eb970fec..4632e264c1d 100644 --- a/src/phases/egg-lapse-phase.ts +++ b/src/phases/egg-lapse-phase.ts @@ -5,7 +5,7 @@ import { Phase } from "#app/phase"; import i18next from "i18next"; import Overrides from "#app/overrides"; import { EggHatchPhase } from "./egg-hatch-phase"; -import { Mode } from "#app/ui/ui"; +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"; @@ -41,7 +41,7 @@ export class EggLapsePhase extends Phase { 0, ); globalScene.ui.setModeWithoutClear( - Mode.CONFIRM, + UiMode.CONFIRM, () => { this.hatchEggsSkipped(eggsToHatch); this.showSummary(); diff --git a/src/phases/egg-summary-phase.ts b/src/phases/egg-summary-phase.ts index 9d9259d1e67..d16cafa7611 100644 --- a/src/phases/egg-summary-phase.ts +++ b/src/phases/egg-summary-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import type { EggHatchData } from "#app/data/egg-hatch-data"; /** @@ -22,7 +22,7 @@ export class EggSummaryPhase extends Phase { // updates next pokemon once the current update has been completed const updateNextPokemon = (i: number) => { if (i >= this.eggHatchData.length) { - globalScene.ui.setModeForceTransition(Mode.EGG_HATCH_SUMMARY, this.eggHatchData).then(() => { + globalScene.ui.setModeForceTransition(UiMode.EGG_HATCH_SUMMARY, this.eggHatchData).then(() => { globalScene.fadeOutBgm(undefined, false); }); } else { @@ -39,7 +39,7 @@ export class EggSummaryPhase extends Phase { end() { globalScene.time.delayedCall(250, () => globalScene.setModifiersVisible(true)); - globalScene.ui.setModeForceTransition(Mode.MESSAGE).then(() => { + globalScene.ui.setModeForceTransition(UiMode.MESSAGE).then(() => { super.end(); }); } diff --git a/src/phases/encounter-phase.ts b/src/phases/encounter-phase.ts index c196608f91e..6fd11c416a2 100644 --- a/src/phases/encounter-phase.ts +++ b/src/phases/encounter-phase.ts @@ -29,8 +29,8 @@ 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 { Mode } from "#app/ui/ui"; -import { randSeedInt, randSeedItem } from "#app/utils"; +import { UiMode } from "#enums/ui-mode"; +import { randSeedInt, randSeedItem } from "#app/utils/common"; import { BattleSpec } from "#enums/battle-spec"; import { Biome } from "#enums/biome"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; @@ -298,7 +298,7 @@ export class EncounterPhase extends BattlePhase { globalScene.currentBattle.trainer!.genAI(globalScene.getEnemyParty()); } - globalScene.ui.setMode(Mode.MESSAGE).then(() => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { if (!this.loaded) { this.trySetWeatherIfNewBiome(); // Set weather before session gets saved // Game syncs to server on waves X1 and X6 (As of 1.2.0) diff --git a/src/phases/end-evolution-phase.ts b/src/phases/end-evolution-phase.ts index e0bdc7e0d68..579920dde90 100644 --- a/src/phases/end-evolution-phase.ts +++ b/src/phases/end-evolution-phase.ts @@ -1,11 +1,11 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; export class EndEvolutionPhase extends Phase { start() { super.start(); - globalScene.ui.setModeForceTransition(Mode.MESSAGE).then(() => this.end()); + globalScene.ui.setModeForceTransition(UiMode.MESSAGE).then(() => this.end()); } } diff --git a/src/phases/evolution-phase.ts b/src/phases/evolution-phase.ts index 203c7542eff..7b013555f40 100644 --- a/src/phases/evolution-phase.ts +++ b/src/phases/evolution-phase.ts @@ -5,8 +5,8 @@ import { globalScene } from "#app/global-scene"; import type { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; import { FusionSpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; import type EvolutionSceneHandler from "#app/ui/evolution-scene-handler"; -import { fixedInt, getFrameMs, randInt } from "#app/utils"; -import { Mode } from "#app/ui/ui"; +import { fixedInt, getFrameMs, randInt } from "#app/utils/common"; +import { UiMode } from "#enums/ui-mode"; import { cos, sin } from "#app/field/anims"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; @@ -53,7 +53,7 @@ export class EvolutionPhase extends Phase { } setMode(): Promise { - return globalScene.ui.setModeForceTransition(Mode.EVOLUTION_SCENE); + return globalScene.ui.setModeForceTransition(UiMode.EVOLUTION_SCENE); } start() { @@ -280,7 +280,7 @@ export class EvolutionPhase extends Phase { this.end(); }; globalScene.ui.setOverlayMode( - Mode.CONFIRM, + UiMode.CONFIRM, () => { globalScene.ui.revertMode(); this.pokemon.pauseEvolutions = true; diff --git a/src/phases/exp-phase.ts b/src/phases/exp-phase.ts index b7d62c92bcf..8841a90d5b1 100644 --- a/src/phases/exp-phase.ts +++ b/src/phases/exp-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import { getPokemonNameWithAffix } from "#app/messages"; import { ExpBoosterModifier } from "#app/modifier/modifier"; import i18next from "i18next"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { PlayerPartyMemberPokemonPhase } from "./player-party-member-pokemon-phase"; import { LevelUpPhase } from "./level-up-phase"; diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 2719206a6cc..5a25cf6330d 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -29,7 +29,7 @@ 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"; +import { isNullOrUndefined } from "#app/utils/common"; import { FRIENDSHIP_LOSS_FROM_FAINT } from "#app/data/balance/starters"; import { BattlerTagType } from "#enums/battler-tag-type"; diff --git a/src/phases/form-change-phase.ts b/src/phases/form-change-phase.ts index bf94284b117..ac7edadf244 100644 --- a/src/phases/form-change-phase.ts +++ b/src/phases/form-change-phase.ts @@ -1,10 +1,10 @@ import { globalScene } from "#app/global-scene"; -import { fixedInt } from "#app/utils"; +import { fixedInt } from "#app/utils/common"; import { achvs } from "../system/achv"; import type { SpeciesFormChange } from "../data/pokemon-forms"; import { getSpeciesFormChangeMessage } from "../data/pokemon-forms"; import type { PlayerPokemon } from "../field/pokemon"; -import { Mode } from "../ui/ui"; +import { UiMode } from "#enums/ui-mode"; import type PartyUiHandler from "../ui/party-ui-handler"; import { getPokemonNameWithAffix } from "../messages"; import { EndEvolutionPhase } from "./end-evolution-phase"; @@ -31,7 +31,7 @@ export class FormChangePhase extends EvolutionPhase { if (!this.modal) { return super.setMode(); } - return globalScene.ui.setOverlayMode(Mode.EVOLUTION_SCENE); + return globalScene.ui.setOverlayMode(UiMode.EVOLUTION_SCENE); } doEvolution(): void { @@ -181,7 +181,7 @@ export class FormChangePhase extends EvolutionPhase { this.pokemon.findAndRemoveTags(t => t.tagType === BattlerTagType.AUTOTOMIZED); if (this.modal) { globalScene.ui.revertMode().then(() => { - if (globalScene.ui.getMode() === Mode.PARTY) { + if (globalScene.ui.getMode() === UiMode.PARTY) { const partyUiHandler = globalScene.ui.getHandler() as PartyUiHandler; partyUiHandler.clearPartySlots(); partyUiHandler.populatePartySlots(); diff --git a/src/phases/game-over-modifier-reward-phase.ts b/src/phases/game-over-modifier-reward-phase.ts index d0a39a4031a..ab6f6554c99 100644 --- a/src/phases/game-over-modifier-reward-phase.ts +++ b/src/phases/game-over-modifier-reward-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import { ModifierRewardPhase } from "./modifier-reward-phase"; @@ -10,7 +10,7 @@ export class GameOverModifierRewardPhase extends ModifierRewardPhase { globalScene.addModifier(newModifier); // Sound loaded into game as is globalScene.playSound("level_up_fanfare"); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.fadeIn(250).then(() => { globalScene.ui.showText( i18next.t("battle:rewardGain", { diff --git a/src/phases/game-over-phase.ts b/src/phases/game-over-phase.ts index 9e79eafe88b..304d876a99e 100644 --- a/src/phases/game-over-phase.ts +++ b/src/phases/game-over-phase.ts @@ -19,8 +19,8 @@ import { SummonPhase } from "#app/phases/summon-phase"; import { UnlockPhase } from "#app/phases/unlock-phase"; import { achvs, ChallengeAchv } from "#app/system/achv"; import { Unlockables } from "#app/system/unlockables"; -import { Mode } from "#app/ui/ui"; -import { isLocal, isLocalServerConnected } from "#app/utils"; +import { UiMode } from "#enums/ui-mode"; +import { isLocal, isLocalServerConnected } from "#app/utils/common"; import { PlayerGender } from "#enums/player-gender"; import { TrainerType } from "#enums/trainer-type"; import i18next from "i18next"; @@ -78,7 +78,7 @@ export class GameOverPhase extends BattlePhase { } else { globalScene.ui.showText(i18next.t("battle:retryBattle"), null, () => { globalScene.ui.setMode( - Mode.CONFIRM, + UiMode.CONFIRM, () => { globalScene.ui.fadeOut(1250).then(() => { globalScene.reset(); diff --git a/src/phases/learn-move-phase.ts b/src/phases/learn-move-phase.ts index 4107a9cf087..515ce492b92 100644 --- a/src/phases/learn-move-phase.ts +++ b/src/phases/learn-move-phase.ts @@ -8,7 +8,7 @@ import { getPokemonNameWithAffix } from "#app/messages"; import Overrides from "#app/overrides"; import EvolutionSceneHandler from "#app/ui/evolution-scene-handler"; import { SummaryUiMode } from "#app/ui/summary-ui-handler"; -import { Mode } from "#app/ui/ui"; +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"; @@ -25,7 +25,7 @@ export enum LearnMoveType { export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { private moveId: Moves; - private messageMode: Mode; + private messageMode: UiMode; private learnMoveType: LearnMoveType; private cost: number; @@ -55,7 +55,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { } this.messageMode = - globalScene.ui.getHandler() instanceof EvolutionSceneHandler ? Mode.EVOLUTION_SCENE : Mode.MESSAGE; + globalScene.ui.getHandler() instanceof EvolutionSceneHandler ? UiMode.EVOLUTION_SCENE : UiMode.MESSAGE; globalScene.ui.setMode(this.messageMode); // If the Pokemon has less than 4 moves, the new move is added to the largest empty moveset index // If it has 4 moves, the phase then checks if the player wants to replace the move itself. @@ -90,7 +90,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { await globalScene.ui.showTextPromise(preQText); await globalScene.ui.showTextPromise(shouldReplaceQ, undefined, false); await globalScene.ui.setModeWithoutClear( - Mode.CONFIRM, + UiMode.CONFIRM, () => this.forgetMoveProcess(move, pokemon), // Yes () => { // No @@ -115,7 +115,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { globalScene.ui.setMode(this.messageMode); await globalScene.ui.showTextPromise(i18next.t("battle:learnMoveForgetQuestion"), undefined, true); await globalScene.ui.setModeWithoutClear( - Mode.SUMMARY, + UiMode.SUMMARY, pokemon, SummaryUiMode.LEARN_MOVE, move, @@ -153,7 +153,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { false, ); globalScene.ui.setModeWithoutClear( - Mode.CONFIRM, + UiMode.CONFIRM, () => { globalScene.ui.setMode(this.messageMode); globalScene.ui @@ -228,7 +228,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeMoveLearnedTrigger, true); this.end(); }, - this.messageMode === Mode.EVOLUTION_SCENE ? 1000 : undefined, + this.messageMode === UiMode.EVOLUTION_SCENE ? 1000 : undefined, true, ); } diff --git a/src/phases/level-cap-phase.ts b/src/phases/level-cap-phase.ts index 567ac922124..6f3fa6fdb39 100644 --- a/src/phases/level-cap-phase.ts +++ b/src/phases/level-cap-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import { FieldPhase } from "./field-phase"; @@ -7,7 +7,7 @@ export class LevelCapPhase extends FieldPhase { start(): void { super.start(); - globalScene.ui.setMode(Mode.MESSAGE).then(() => { + globalScene.ui.setMode(UiMode.MESSAGE).then(() => { // Sound loaded into game as is globalScene.playSound("level_up_fanfare"); globalScene.ui.showText( diff --git a/src/phases/level-up-phase.ts b/src/phases/level-up-phase.ts index c6ca17d583e..8c4f4f58095 100644 --- a/src/phases/level-up-phase.ts +++ b/src/phases/level-up-phase.ts @@ -6,7 +6,7 @@ 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"; +import { NumberHolder } from "#app/utils/common"; import i18next from "i18next"; export class LevelUpPhase extends PlayerPartyMemberPokemonPhase { @@ -71,7 +71,7 @@ export class LevelUpPhase extends PlayerPartyMemberPokemonPhase { if (!this.pokemon.pauseEvolutions) { const evolution = this.pokemon.getEvolution(); if (evolution) { - this.pokemon.breakIllusion() + this.pokemon.breakIllusion(); globalScene.unshiftPhase(new EvolutionPhase(this.pokemon, evolution, this.lastLevel)); } } diff --git a/src/phases/login-phase.ts b/src/phases/login-phase.ts index 846482ff726..673b94b1148 100644 --- a/src/phases/login-phase.ts +++ b/src/phases/login-phase.ts @@ -1,11 +1,12 @@ import { updateUserInfo } from "#app/account"; -import { bypassLogin } from "#app/battle-scene"; +import { bypassLogin } from "#app/global-vars/bypass-login"; import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; import { handleTutorial, Tutorial } from "#app/tutorial"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next, { t } from "i18next"; -import { getCookie, sessionIdKey, executeIf, removeCookie } from "#app/utils"; +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"; @@ -23,7 +24,7 @@ export class LoginPhase extends Phase { const hasSession = !!getCookie(sessionIdKey); - globalScene.ui.setMode(Mode.LOADING, { buttonActions: [] }); + globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); executeIf(bypassLogin || hasSession, updateUserInfo).then(response => { const success = response ? response[0] : false; const statusCode = response ? response[1] : null; @@ -46,7 +47,7 @@ export class LoginPhase extends Phase { }); }; - globalScene.ui.setMode(Mode.LOGIN_FORM, { + globalScene.ui.setMode(UiMode.LOGIN_FORM, { buttonActions: [ () => { globalScene.ui.playSelect(); @@ -54,7 +55,7 @@ export class LoginPhase extends Phase { }, () => { globalScene.playSound("menu_open"); - globalScene.ui.setMode(Mode.REGISTRATION_FORM, { + globalScene.ui.setMode(UiMode.REGISTRATION_FORM, { buttonActions: [ () => { globalScene.ui.playSelect(); @@ -101,7 +102,7 @@ export class LoginPhase extends Phase { if (success || bypassLogin) { this.end(); } else { - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.showText(t("menu:failedToLoadSaveData")); } }); @@ -109,7 +110,7 @@ export class LoginPhase extends Phase { } end(): void { - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); if (!globalScene.gameData.gender) { globalScene.unshiftPhase(new SelectGenderPhase()); diff --git a/src/phases/money-reward-phase.ts b/src/phases/money-reward-phase.ts index ae8dc90616d..708bb3a2fa8 100644 --- a/src/phases/money-reward-phase.ts +++ b/src/phases/money-reward-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import { MoneyMultiplierModifier } from "#app/modifier/modifier"; import i18next from "i18next"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { BattlePhase } from "./battle-phase"; export class MoneyRewardPhase extends BattlePhase { diff --git a/src/phases/move-charge-phase.ts b/src/phases/move-charge-phase.ts index 26ad85bbe03..ea43f1ddb88 100644 --- a/src/phases/move-charge-phase.ts +++ b/src/phases/move-charge-phase.ts @@ -5,7 +5,7 @@ import { applyMoveChargeAttrs, MoveEffectAttr, InstantChargeAttr } from "#app/da 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"; +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"; diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index 3a4e5f32ede..c29e3fe5cda 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -61,8 +61,8 @@ import { PokemonMultiHitModifier, } from "#app/modifier/modifier"; import { PokemonPhase } from "#app/phases/pokemon-phase"; -import { BooleanHolder, isNullOrUndefined, NumberHolder } from "#app/utils"; -import type { nil } from "#app/utils"; +import { BooleanHolder, isNullOrUndefined, NumberHolder } from "#app/utils/common"; +import type { nil } from "#app/utils/common"; import { BattlerTagType } from "#enums/battler-tag-type"; import type { Moves } from "#enums/moves"; import i18next from "i18next"; diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index dc394b8a134..f42a2aefa34 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -43,7 +43,7 @@ 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"; +import { NumberHolder } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index f42290ff872..100be47e4e9 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -25,8 +25,8 @@ import { transitionMysteryEncounterIntroVisuals } from "../data/mystery-encounte import { TrainerSlot } from "#enums/trainer-slot"; import { IvScannerModifier } from "../modifier/modifier"; import { Phase } from "../phase"; -import { Mode } from "../ui/ui"; -import { isNullOrUndefined, randSeedItem } from "#app/utils"; +import { UiMode } from "#enums/ui-mode"; +import { isNullOrUndefined, randSeedItem } from "#app/utils/common"; /** * Will handle (in order): @@ -72,7 +72,7 @@ export class MysteryEncounterPhase extends Phase { } // Initiates encounter dialogue window and option select - globalScene.ui.setMode(Mode.MYSTERY_ENCOUNTER, this.optionSelectSettings); + globalScene.ui.setMode(UiMode.MYSTERY_ENCOUNTER, this.optionSelectSettings); } /** @@ -130,7 +130,7 @@ export class MysteryEncounterPhase extends Phase { const optionSelectDialogue = globalScene.currentBattle?.mysteryEncounter?.selectedOption?.dialogue; if (optionSelectDialogue?.selected && optionSelectDialogue.selected.length > 0) { // Handle intermediate dialogue (between player selection event and the onOptionSelect logic) - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); const selectedDialogue = optionSelectDialogue.selected; let i = 0; const showNextDialogue = () => { @@ -167,7 +167,7 @@ export class MysteryEncounterPhase extends Phase { * Ends phase */ end() { - globalScene.ui.setMode(Mode.MESSAGE).then(() => super.end()); + globalScene.ui.setMode(UiMode.MESSAGE).then(() => super.end()); } } @@ -629,7 +629,7 @@ export class PostMysteryEncounterPhase extends Phase { } i++; - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); if (title) { globalScene.ui.showDialogue( text ?? "", diff --git a/src/phases/obtain-status-effect-phase.ts b/src/phases/obtain-status-effect-phase.ts index 10ae195b02f..47cae2dcbf6 100644 --- a/src/phases/obtain-status-effect-phase.ts +++ b/src/phases/obtain-status-effect-phase.ts @@ -8,7 +8,7 @@ import { getPokemonNameWithAffix } from "#app/messages"; import { PokemonPhase } from "./pokemon-phase"; import { SpeciesFormChangeStatusEffectTrigger } from "#app/data/pokemon-forms"; import { applyPostSetStatusAbAttrs, PostSetStatusAbAttr } from "#app/data/abilities/ability"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; export class ObtainStatusEffectPhase extends PokemonPhase { private statusEffect?: StatusEffect; diff --git a/src/phases/party-heal-phase.ts b/src/phases/party-heal-phase.ts index 137af9f3a2d..a208ccfff92 100644 --- a/src/phases/party-heal-phase.ts +++ b/src/phases/party-heal-phase.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { fixedInt } from "#app/utils"; +import { fixedInt } from "#app/utils/common"; import { BattlePhase } from "./battle-phase"; export class PartyHealPhase extends BattlePhase { diff --git a/src/phases/pokemon-anim-phase.ts b/src/phases/pokemon-anim-phase.ts index f0693a52aaa..1889b238f05 100644 --- a/src/phases/pokemon-anim-phase.ts +++ b/src/phases/pokemon-anim-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import { SubstituteTag } from "#app/data/battler-tags"; import type Pokemon from "#app/field/pokemon"; import { BattlePhase } from "#app/phases/battle-phase"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; import { PokemonAnimType } from "#enums/pokemon-anim-type"; import { Species } from "#enums/species"; diff --git a/src/phases/pokemon-heal-phase.ts b/src/phases/pokemon-heal-phase.ts index 651c625b23a..7cb013251f6 100644 --- a/src/phases/pokemon-heal-phase.ts +++ b/src/phases/pokemon-heal-phase.ts @@ -8,7 +8,7 @@ import { getPokemonNameWithAffix } from "#app/messages"; import { HealingBoosterModifier } from "#app/modifier/modifier"; import { HealAchv } from "#app/system/achv"; import i18next from "i18next"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { CommonAnimPhase } from "./common-anim-phase"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import type { HealBlockTag } from "#app/data/battler-tags"; diff --git a/src/phases/post-turn-status-effect-phase.ts b/src/phases/post-turn-status-effect-phase.ts index af9a9ac1c29..9b530d48196 100644 --- a/src/phases/post-turn-status-effect-phase.ts +++ b/src/phases/post-turn-status-effect-phase.ts @@ -13,7 +13,7 @@ import { getStatusEffectActivationText } from "#app/data/status-effect"; import { BattleSpec } from "#app/enums/battle-spec"; import { StatusEffect } from "#app/enums/status-effect"; import { getPokemonNameWithAffix } from "#app/messages"; -import { BooleanHolder, NumberHolder } from "#app/utils"; +import { BooleanHolder, NumberHolder } from "#app/utils/common"; import { PokemonPhase } from "./pokemon-phase"; export class PostTurnStatusEffectPhase extends PokemonPhase { diff --git a/src/phases/reload-session-phase.ts b/src/phases/reload-session-phase.ts index a7ac0002b03..8cd5f67b43a 100644 --- a/src/phases/reload-session-phase.ts +++ b/src/phases/reload-session-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; -import { Mode } from "#app/ui/ui"; -import { fixedInt } from "#app/utils"; +import { UiMode } from "#enums/ui-mode"; +import { fixedInt } from "#app/utils/common"; export class ReloadSessionPhase extends Phase { private systemDataStr?: string; @@ -13,7 +13,7 @@ export class ReloadSessionPhase extends Phase { } start(): void { - globalScene.ui.setMode(Mode.SESSION_RELOAD); + globalScene.ui.setMode(UiMode.SESSION_RELOAD); let delayElapsed = false; let loaded = false; diff --git a/src/phases/revival-blessing-phase.ts b/src/phases/revival-blessing-phase.ts index f6fe4d9a3ee..2de1c616f69 100644 --- a/src/phases/revival-blessing-phase.ts +++ b/src/phases/revival-blessing-phase.ts @@ -2,9 +2,9 @@ import { SwitchType } from "#enums/switch-type"; import { globalScene } from "#app/global-scene"; import type { PartyOption } from "#app/ui/party-ui-handler"; import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; -import { toDmgValue, isNullOrUndefined } from "#app/utils"; +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"; @@ -21,7 +21,7 @@ export class RevivalBlessingPhase extends BattlePhase { public override start(): void { globalScene.ui.setMode( - Mode.PARTY, + UiMode.PARTY, PartyUiMode.REVIVAL_BLESSING, this.user.getFieldIndex(), (slotIndex: integer, _option: PartyOption) => { @@ -63,7 +63,7 @@ export class RevivalBlessingPhase extends BattlePhase { } } } - globalScene.ui.setMode(Mode.MESSAGE).then(() => this.end()); + globalScene.ui.setMode(UiMode.MESSAGE).then(() => this.end()); }, PartyUiHandler.FilterFainted, ); diff --git a/src/phases/ribbon-modifier-reward-phase.ts b/src/phases/ribbon-modifier-reward-phase.ts index 0ee38250ce1..21114ab3de9 100644 --- a/src/phases/ribbon-modifier-reward-phase.ts +++ b/src/phases/ribbon-modifier-reward-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import type PokemonSpecies from "#app/data/pokemon-species"; import type { ModifierTypeFunc } from "#app/modifier/modifier-type"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import { ModifierRewardPhase } from "./modifier-reward-phase"; @@ -19,7 +19,7 @@ export class RibbonModifierRewardPhase extends ModifierRewardPhase { const newModifier = this.modifierType.newModifier(); globalScene.addModifier(newModifier); globalScene.playSound("level_up_fanfare"); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.showText( i18next.t("battle:beatModeFirstTime", { speciesName: this.species.name, diff --git a/src/phases/scan-ivs-phase.ts b/src/phases/scan-ivs-phase.ts index aaeeb7f84f8..d79a32bd47e 100644 --- a/src/phases/scan-ivs-phase.ts +++ b/src/phases/scan-ivs-phase.ts @@ -3,7 +3,7 @@ import type { BattlerIndex } from "#app/battle"; import { PERMANENT_STATS, Stat } from "#app/enums/stat"; import { getPokemonNameWithAffix } from "#app/messages"; import { getTextColor, TextStyle } from "#app/ui/text"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import { PokemonPhase } from "./pokemon-phase"; @@ -51,9 +51,9 @@ export class ScanIvsPhase extends PokemonPhase { null, () => { globalScene.ui.setMode( - Mode.CONFIRM, + UiMode.CONFIRM, () => { - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.clearText(); globalScene.ui .getMessageHandler() @@ -61,7 +61,7 @@ export class ScanIvsPhase extends PokemonPhase { .then(() => this.end()); }, () => { - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.clearText(); this.end(); }, diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index b27e2d0e7cc..0ea2841a2d3 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -3,9 +3,9 @@ import { biomeLinks, getBiomeName } from "#app/data/balance/biomes"; import { Biome } from "#app/enums/biome"; import { MoneyInterestModifier, MapModifier } from "#app/modifier/modifier"; import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { BattlePhase } from "./battle-phase"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt } from "#app/utils/common"; import { PartyHealPhase } from "./party-heal-phase"; import { SwitchBiomePhase } from "./switch-biome-phase"; @@ -42,14 +42,14 @@ export class SelectBiomePhase extends BattlePhase { const ret: OptionSelectItem = { label: getBiomeName(b), handler: () => { - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); setNextBiome(b); return true; }, }; return ret; }); - globalScene.ui.setMode(Mode.OPTION_SELECT, { + globalScene.ui.setMode(UiMode.OPTION_SELECT, { options: biomeSelectItems, delay: 1000, }); diff --git a/src/phases/select-challenge-phase.ts b/src/phases/select-challenge-phase.ts index 5e6f20f93ee..76ac8a60c4f 100644 --- a/src/phases/select-challenge-phase.ts +++ b/src/phases/select-challenge-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; export class SelectChallengePhase extends Phase { start() { @@ -8,6 +8,6 @@ export class SelectChallengePhase extends Phase { globalScene.playBgm("menu"); - globalScene.ui.setMode(Mode.CHALLENGE_SELECT); + globalScene.ui.setMode(UiMode.CHALLENGE_SELECT); } } diff --git a/src/phases/select-gender-phase.ts b/src/phases/select-gender-phase.ts index 4da60b38aa1..a1171c1a5db 100644 --- a/src/phases/select-gender-phase.ts +++ b/src/phases/select-gender-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import { PlayerGender } from "#app/enums/player-gender"; import { Phase } from "#app/phase"; import { SettingKeys } from "#app/system/settings/settings"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; export class SelectGenderPhase extends Phase { @@ -10,7 +10,7 @@ export class SelectGenderPhase extends Phase { super.start(); globalScene.ui.showText(i18next.t("menu:boyOrGirl"), null, () => { - globalScene.ui.setMode(Mode.OPTION_SELECT, { + globalScene.ui.setMode(UiMode.OPTION_SELECT, { options: [ { label: i18next.t("settings:boy"), @@ -36,7 +36,7 @@ export class SelectGenderPhase extends Phase { } end(): void { - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); super.end(); } } diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts index 27ab7e374a2..5f11441333b 100644 --- a/src/phases/select-modifier-phase.ts +++ b/src/phases/select-modifier-phase.ts @@ -24,12 +24,12 @@ import { import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { SHOP_OPTIONS_ROW_LIMIT } from "#app/ui/modifier-select-ui-handler"; import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import { BattlePhase } from "./battle-phase"; import Overrides from "#app/overrides"; import type { CustomModifierSettings } from "#app/modifier/modifier-type"; -import { isNullOrUndefined, NumberHolder } from "#app/utils"; +import { isNullOrUndefined, NumberHolder } from "#app/utils/common"; export class SelectModifierPhase extends BattlePhase { private rerollCount: number; @@ -92,15 +92,15 @@ export class SelectModifierPhase extends BattlePhase { if (rowCursor < 0 || cursor < 0) { globalScene.ui.showText(i18next.t("battle:skipItemQuestion"), null, () => { globalScene.ui.setOverlayMode( - Mode.CONFIRM, + UiMode.CONFIRM, () => { globalScene.ui.revertMode(); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); super.end(); }, () => globalScene.ui.setMode( - Mode.MODIFIER_SELECT, + UiMode.MODIFIER_SELECT, this.isPlayer(), this.typeOptions, modifierSelectCallback, @@ -129,7 +129,7 @@ export class SelectModifierPhase extends BattlePhase { ), ); globalScene.ui.clearText(); - globalScene.ui.setMode(Mode.MESSAGE).then(() => super.end()); + globalScene.ui.setMode(UiMode.MESSAGE).then(() => super.end()); if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { globalScene.money -= rerollCost; globalScene.updateMoneyText(); @@ -139,7 +139,7 @@ export class SelectModifierPhase extends BattlePhase { break; case 1: globalScene.ui.setModeWithoutClear( - Mode.PARTY, + UiMode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: number, itemIndex: number, itemQuantity: number, toSlotIndex: number) => { @@ -168,7 +168,7 @@ export class SelectModifierPhase extends BattlePhase { ); } else { globalScene.ui.setMode( - Mode.MODIFIER_SELECT, + UiMode.MODIFIER_SELECT, this.isPlayer(), this.typeOptions, modifierSelectCallback, @@ -180,9 +180,9 @@ export class SelectModifierPhase extends BattlePhase { ); break; case 2: - globalScene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.CHECK, -1, () => { + globalScene.ui.setModeWithoutClear(UiMode.PARTY, PartyUiMode.CHECK, -1, () => { globalScene.ui.setMode( - Mode.MODIFIER_SELECT, + UiMode.MODIFIER_SELECT, this.isPlayer(), this.typeOptions, modifierSelectCallback, @@ -207,7 +207,7 @@ export class SelectModifierPhase extends BattlePhase { case 1: if (this.typeOptions.length === 0) { globalScene.ui.clearText(); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); super.end(); return true; } @@ -263,7 +263,7 @@ export class SelectModifierPhase extends BattlePhase { } } else { globalScene.ui.clearText(); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); super.end(); } }; @@ -272,7 +272,7 @@ export class SelectModifierPhase extends BattlePhase { //TODO: is the bang correct? if (modifierType instanceof FusePokemonModifierType) { globalScene.ui.setModeWithoutClear( - Mode.PARTY, + UiMode.PARTY, PartyUiMode.SPLICE, -1, (fromSlotIndex: number, spliceSlotIndex: number) => { @@ -282,13 +282,13 @@ export class SelectModifierPhase extends BattlePhase { spliceSlotIndex < 6 && fromSlotIndex !== spliceSlotIndex ) { - globalScene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer()).then(() => { + globalScene.ui.setMode(UiMode.MODIFIER_SELECT, this.isPlayer()).then(() => { const modifier = modifierType.newModifier(party[fromSlotIndex], party[spliceSlotIndex])!; //TODO: is the bang correct? applyModifier(modifier, true); }); } else { globalScene.ui.setMode( - Mode.MODIFIER_SELECT, + UiMode.MODIFIER_SELECT, this.isPlayer(), this.typeOptions, modifierSelectCallback, @@ -314,12 +314,12 @@ export class SelectModifierPhase extends BattlePhase { : PartyUiMode.MODIFIER; const tmMoveId = isTmModifier ? (modifierType as TmModifierType).moveId : undefined; globalScene.ui.setModeWithoutClear( - Mode.PARTY, + UiMode.PARTY, partyUiMode, -1, (slotIndex: number, option: PartyOption) => { if (slotIndex < 6) { - globalScene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer()).then(() => { + globalScene.ui.setMode(UiMode.MODIFIER_SELECT, this.isPlayer()).then(() => { const modifier = !isMoveModifier ? !isRememberMoveModifier ? modifierType.newModifier(party[slotIndex]) @@ -329,7 +329,7 @@ export class SelectModifierPhase extends BattlePhase { }); } else { globalScene.ui.setMode( - Mode.MODIFIER_SELECT, + UiMode.MODIFIER_SELECT, this.isPlayer(), this.typeOptions, modifierSelectCallback, @@ -352,7 +352,7 @@ export class SelectModifierPhase extends BattlePhase { return !cost!; // TODO: is the bang correct? }; globalScene.ui.setMode( - Mode.MODIFIER_SELECT, + UiMode.MODIFIER_SELECT, this.isPlayer(), this.typeOptions, modifierSelectCallback, diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts index 35511531609..0a76df31a2c 100644 --- a/src/phases/select-starter-phase.ts +++ b/src/phases/select-starter-phase.ts @@ -9,10 +9,10 @@ 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 { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import type { Species } from "#enums/species"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; export class SelectStarterPhase extends Phase { start() { @@ -20,9 +20,9 @@ export class SelectStarterPhase extends Phase { globalScene.playBgm("menu"); - globalScene.ui.setMode(Mode.STARTER_SELECT, (starters: Starter[]) => { + globalScene.ui.setMode(UiMode.STARTER_SELECT, (starters: Starter[]) => { globalScene.ui.clearText(); - globalScene.ui.setMode(Mode.SAVE_SLOT, SaveSlotUiMode.SAVE, (slotId: number) => { + globalScene.ui.setMode(UiMode.SAVE_SLOT, SaveSlotUiMode.SAVE, (slotId: number) => { if (slotId === -1) { globalScene.clearPhaseQueue(); globalScene.pushPhase(new TitlePhase()); diff --git a/src/phases/select-target-phase.ts b/src/phases/select-target-phase.ts index 035eaff41fa..c969b9ca421 100644 --- a/src/phases/select-target-phase.ts +++ b/src/phases/select-target-phase.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import type { BattlerIndex } from "#app/battle"; import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { CommandPhase } from "./command-phase"; import { PokemonPhase } from "./pokemon-phase"; import i18next from "#app/plugins/i18n"; @@ -18,8 +18,8 @@ export class SelectTargetPhase extends PokemonPhase { const turnCommand = globalScene.currentBattle.turnCommands[this.fieldIndex]; const move = turnCommand?.move?.move; - globalScene.ui.setMode(Mode.TARGET_SELECT, this.fieldIndex, move, (targets: BattlerIndex[]) => { - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.TARGET_SELECT, this.fieldIndex, move, (targets: BattlerIndex[]) => { + globalScene.ui.setMode(UiMode.MESSAGE); const fieldSide = globalScene.getField(); const user = fieldSide[this.fieldIndex]; const moveObject = allMoves[move!]; diff --git a/src/phases/show-party-exp-bar-phase.ts b/src/phases/show-party-exp-bar-phase.ts index 139f4efcc49..89bec6d8fdd 100644 --- a/src/phases/show-party-exp-bar-phase.ts +++ b/src/phases/show-party-exp-bar-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; 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"; +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"; diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index f52e4fb06a0..9d64a81bbb4 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -17,7 +17,7 @@ import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { ResetNegativeStatStageModifier } from "#app/modifier/modifier"; import { handleTutorial, Tutorial } from "#app/tutorial"; -import { NumberHolder, BooleanHolder, isNullOrUndefined } from "#app/utils"; +import { NumberHolder, BooleanHolder, isNullOrUndefined } from "#app/utils/common"; import i18next from "i18next"; import { PokemonPhase } from "./pokemon-phase"; import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat"; diff --git a/src/phases/switch-phase.ts b/src/phases/switch-phase.ts index 8562309ede5..c056b186021 100644 --- a/src/phases/switch-phase.ts +++ b/src/phases/switch-phase.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import PartyUiHandler, { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { SwitchType } from "#enums/switch-type"; import { BattlePhase } from "./battle-phase"; import { PostSummonPhase } from "./post-summon-phase"; @@ -69,7 +69,7 @@ export class SwitchPhase extends BattlePhase { : 0; globalScene.ui.setMode( - Mode.PARTY, + UiMode.PARTY, this.isModal ? PartyUiMode.FAINT_SWITCH : PartyUiMode.POST_BATTLE_SWITCH, fieldIndex, (slotIndex: number, option: PartyOption) => { @@ -80,7 +80,7 @@ export class SwitchPhase extends BattlePhase { const switchType = option === PartyOption.PASS_BATON ? SwitchType.BATON_PASS : this.switchType; globalScene.unshiftPhase(new SwitchSummonPhase(switchType, fieldIndex, slotIndex, this.doReturn)); } - globalScene.ui.setMode(Mode.MESSAGE).then(() => super.end()); + globalScene.ui.setMode(UiMode.MESSAGE).then(() => super.end()); }, PartyUiHandler.FilterNonFainted, ); diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index bc1b157e98e..56057c23372 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -17,8 +17,8 @@ import { Unlockables } from "#app/system/unlockables"; import { vouchers } from "#app/system/voucher"; import type { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler"; -import { Mode } from "#app/ui/ui"; -import { isLocal, isLocalServerConnected, isNullOrUndefined } from "#app/utils"; +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"; @@ -75,7 +75,7 @@ export class TitlePhase extends Phase { handler: () => { const setModeAndEnd = (gameMode: GameModes) => { this.gameMode = gameMode; - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.clearText(); this.end(); }; @@ -130,7 +130,7 @@ export class TitlePhase extends Phase { }, }); globalScene.ui.showText(i18next.t("menu:selectGameMode"), null, () => - globalScene.ui.setOverlayMode(Mode.OPTION_SELECT, { + globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, { options: options, }), ); @@ -140,7 +140,7 @@ export class TitlePhase extends Phase { { label: i18next.t("menu:loadGame"), handler: () => { - globalScene.ui.setOverlayMode(Mode.SAVE_SLOT, SaveSlotUiMode.LOAD, (slotId: number) => { + globalScene.ui.setOverlayMode(UiMode.SAVE_SLOT, SaveSlotUiMode.LOAD, (slotId: number) => { if (slotId === -1) { return this.showOptions(); } @@ -152,7 +152,7 @@ export class TitlePhase extends Phase { { label: i18next.t("menu:runHistory"), handler: () => { - globalScene.ui.setOverlayMode(Mode.RUN_HISTORY); + globalScene.ui.setOverlayMode(UiMode.RUN_HISTORY); return true; }, keepOpen: true, @@ -160,7 +160,7 @@ export class TitlePhase extends Phase { { label: i18next.t("menu:settings"), handler: () => { - globalScene.ui.setOverlayMode(Mode.SETTINGS); + globalScene.ui.setOverlayMode(UiMode.SETTINGS); return true; }, keepOpen: true, @@ -171,12 +171,12 @@ export class TitlePhase extends Phase { noCancel: true, yOffset: 47, }; - globalScene.ui.setMode(Mode.TITLE, config); + globalScene.ui.setMode(UiMode.TITLE, config); } loadSaveSlot(slotId: number): void { globalScene.sessionSlotId = slotId > -1 || !loggedInUser ? slotId : loggedInUser.lastSessionSlot; - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.resetModeChain(); globalScene.gameData .loadSession(slotId, slotId === -1 ? this.lastSessionData : undefined) @@ -196,7 +196,7 @@ export class TitlePhase extends Phase { initDailyRun(): void { globalScene.ui.clearText(); - globalScene.ui.setMode(Mode.SAVE_SLOT, SaveSlotUiMode.SAVE, (slotId: number) => { + globalScene.ui.setMode(UiMode.SAVE_SLOT, SaveSlotUiMode.SAVE, (slotId: number) => { globalScene.clearPhaseQueue(); if (slotId === -1) { globalScene.pushPhase(new TitlePhase()); diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts index f17071f118e..f7005b1300d 100644 --- a/src/phases/trainer-victory-phase.ts +++ b/src/phases/trainer-victory-phase.ts @@ -3,7 +3,7 @@ import { TrainerType } from "#app/enums/trainer-type"; import { modifierTypes } from "#app/modifier/modifier-type"; import { vouchers } from "#app/system/voucher"; import i18next from "i18next"; -import { randSeedItem } from "#app/utils"; +import { randSeedItem } from "#app/utils/common"; import { BattlePhase } from "./battle-phase"; import { ModifierRewardPhase } from "./modifier-reward-phase"; import { MoneyRewardPhase } from "./money-reward-phase"; diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index ba6ace2d188..622b9cdcbd1 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -6,7 +6,7 @@ import type Pokemon from "#app/field/pokemon"; 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"; +import { randSeedShuffle, BooleanHolder } from "#app/utils/common"; import { AttemptCapturePhase } from "./attempt-capture-phase"; import { AttemptRunPhase } from "./attempt-run-phase"; import { BerryPhase } from "./berry-phase"; diff --git a/src/phases/unavailable-phase.ts b/src/phases/unavailable-phase.ts index 33042739971..e5f1d899191 100644 --- a/src/phases/unavailable-phase.ts +++ b/src/phases/unavailable-phase.ts @@ -1,11 +1,11 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { LoginPhase } from "./login-phase"; export class UnavailablePhase extends Phase { start(): void { - globalScene.ui.setMode(Mode.UNAVAILABLE, () => { + globalScene.ui.setMode(UiMode.UNAVAILABLE, () => { globalScene.unshiftPhase(new LoginPhase(true)); this.end(); }); diff --git a/src/phases/unlock-phase.ts b/src/phases/unlock-phase.ts index b420a4b3a61..7a69fc207bb 100644 --- a/src/phases/unlock-phase.ts +++ b/src/phases/unlock-phase.ts @@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene"; import { Phase } from "#app/phase"; import type { Unlockables } from "#app/system/unlockables"; import { getUnlockableName } from "#app/system/unlockables"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; export class UnlockPhase extends Phase { @@ -19,7 +19,7 @@ export class UnlockPhase extends Phase { globalScene.gameData.unlocks[this.unlockable] = true; // Sound loaded into game as is globalScene.playSound("level_up_fanfare"); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.MESSAGE); globalScene.ui.showText( i18next.t("battle:unlockedSomething", { unlockedThing: getUnlockableName(this.unlockable), diff --git a/src/phases/weather-effect-phase.ts b/src/phases/weather-effect-phase.ts index b83eab43b65..d89c78e96c7 100644 --- a/src/phases/weather-effect-phase.ts +++ b/src/phases/weather-effect-phase.ts @@ -15,7 +15,7 @@ import { BattlerTagType } from "#app/enums/battler-tag-type"; import { WeatherType } from "#app/enums/weather-type"; import type Pokemon from "#app/field/pokemon"; import { HitResult } from "#app/field/pokemon"; -import { BooleanHolder, toDmgValue } from "#app/utils"; +import { BooleanHolder, toDmgValue } from "#app/utils/common"; import { CommonAnimPhase } from "./common-anim-phase"; export class WeatherEffectPhase extends CommonAnimPhase { diff --git a/src/pipelines/field-sprite.ts b/src/pipelines/field-sprite.ts index a55b6a9adb6..a6e248c9998 100644 --- a/src/pipelines/field-sprite.ts +++ b/src/pipelines/field-sprite.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { TerrainType, getTerrainColor } from "../data/terrain"; -import { getCurrentTime } from "#app/utils"; +import { getCurrentTime } from "#app/utils/common"; import fieldSpriteFragShader from "./glsl/fieldSpriteFragShader.frag?raw"; import spriteVertShader from "./glsl/spriteShader.vert?raw"; diff --git a/src/pipelines/sprite.ts b/src/pipelines/sprite.ts index 0aa9409617a..307c2cee4cc 100644 --- a/src/pipelines/sprite.ts +++ b/src/pipelines/sprite.ts @@ -3,7 +3,7 @@ import MysteryEncounterIntroVisuals from "#app/field/mystery-encounter-intro"; import Pokemon from "#app/field/pokemon"; import Trainer from "#app/field/trainer"; import { globalScene } from "#app/global-scene"; -import { rgbHexToRgba } from "#app/utils"; +import { rgbHexToRgba } from "#app/utils/common"; import FieldSpritePipeline from "./field-sprite"; import spriteFragShader from "./glsl/spriteFragShader.frag?raw"; import spriteVertShader from "./glsl/spriteShader.vert?raw"; diff --git a/src/plugins/api/api-base.ts b/src/plugins/api/api-base.ts index 6a0eca56eaa..f55ffe2d3fd 100644 --- a/src/plugins/api/api-base.ts +++ b/src/plugins/api/api-base.ts @@ -1,5 +1,5 @@ import { SESSION_ID_COOKIE_NAME } from "#app/constants"; -import { getCookie } from "#app/utils"; +import { getCookie } from "#app/utils/cookies"; type DataType = "json" | "form-urlencoded"; diff --git a/src/plugins/api/pokerogue-account-api.ts b/src/plugins/api/pokerogue-account-api.ts index bab74799677..9cd82c24430 100644 --- a/src/plugins/api/pokerogue-account-api.ts +++ b/src/plugins/api/pokerogue-account-api.ts @@ -6,7 +6,7 @@ import type { } from "#app/@types/PokerogueAccountApi"; import { SESSION_ID_COOKIE_NAME } from "#app/constants"; import { ApiBase } from "#app/plugins/api/api-base"; -import { removeCookie, setCookie } from "#app/utils"; +import { removeCookie, setCookie } from "#app/utils/cookies"; /** * A wrapper for PokéRogue account API requests. diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 5e145d08e28..ff9e54fcf50 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -1,4 +1,4 @@ -import { camelCaseToKebabCase } from "#app/utils"; +import { camelCaseToKebabCase } from "#app/utils/common"; import i18next from "i18next"; import LanguageDetector from "i18next-browser-languagedetector"; import HttpBackend from "i18next-http-backend"; diff --git a/src/sprites/variant.ts b/src/sprites/variant.ts index 7552f63b778..985068015c6 100644 --- a/src/sprites/variant.ts +++ b/src/sprites/variant.ts @@ -2,7 +2,7 @@ import { VariantTier } from "#app/enums/variant-tier"; import { hasExpSprite } from "#app/sprites/sprite-utils"; import { globalScene } from "#app/global-scene"; import type Pokemon from "#app/field/pokemon"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; export type Variant = 0 | 1 | 2; diff --git a/src/starter-colors.ts b/src/starter-colors.ts new file mode 100644 index 00000000000..6abe028be99 --- /dev/null +++ b/src/starter-colors.ts @@ -0,0 +1,4 @@ +export const starterColors: StarterColors = {}; +interface StarterColors { + [key: string]: [string, string]; +} diff --git a/src/starting-wave.ts b/src/starting-wave.ts new file mode 100644 index 00000000000..3d36dabe652 --- /dev/null +++ b/src/starting-wave.ts @@ -0,0 +1,3 @@ +import Overrides from "./overrides"; + +export const startingWave = Overrides.STARTING_WAVE_OVERRIDE || 1; diff --git a/src/system/achv.ts b/src/system/achv.ts index 62e69e6fbfe..90816ff65c3 100644 --- a/src/system/achv.ts +++ b/src/system/achv.ts @@ -2,7 +2,7 @@ import type { Modifier } from "typescript"; import { TurnHeldItemTransferModifier } from "../modifier/modifier"; import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions"; import i18next from "i18next"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { PlayerGender } from "#enums/player-gender"; import type { Challenge } from "#app/data/challenge"; import { diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 698299845a3..8b7987556ee 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -1,6 +1,6 @@ import i18next from "i18next"; import type { PokeballCounts } from "#app/battle-scene"; -import { bypassLogin } from "#app/battle-scene"; +import { bypassLogin } from "#app/global-vars/bypass-login"; import { globalScene } from "#app/global-scene"; import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; @@ -8,7 +8,7 @@ import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import type PokemonSpecies from "#app/data/pokemon-species"; import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species"; import { speciesStarterCosts } from "#app/data/balance/starters"; -import { randInt, getEnumKeys, isLocal, executeIf, fixedInt, randSeedItem, NumberHolder } from "#app/utils"; +import { randInt, getEnumKeys, isLocal, executeIf, fixedInt, randSeedItem, NumberHolder } from "#app/utils/common"; import Overrides from "#app/overrides"; import PokemonData from "#app/system/pokemon-data"; import PersistentModifierData from "#app/system/modifier-data"; @@ -24,7 +24,7 @@ import EggData from "#app/system/egg-data"; import type { Egg } from "#app/data/egg"; import { vouchers, VoucherType } from "#app/system/voucher"; import { AES, enc } from "crypto-js"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { clientSessionId, loggedInUser, updateUserInfo } from "#app/account"; import { Nature } from "#enums/nature"; import { GameStats } from "#app/system/game-stats"; @@ -1430,7 +1430,7 @@ export class GameData { const systemData = useCachedSystem ? this.parseSystemData(decrypt(localStorage.getItem(`data_${loggedInUser?.username}`)!, bypassLogin)) : this.getSystemSaveData(); // TODO: is this bang correct? - + const request = { system: systemData, session: sessionData, @@ -1604,7 +1604,7 @@ export class GameData { null, () => { globalScene.ui.setOverlayMode( - Mode.CONFIRM, + UiMode.CONFIRM, () => { localStorage.setItem(dataKey, encrypt(dataStr, bypassLogin)); diff --git a/src/system/game-speed.ts b/src/system/game-speed.ts index 3df47fafc6c..712870dfaf1 100644 --- a/src/system/game-speed.ts +++ b/src/system/game-speed.ts @@ -3,7 +3,7 @@ import type FadeIn from "phaser3-rex-plugins/plugins/audio/fade/FadeIn"; import type FadeOut from "phaser3-rex-plugins/plugins/audio/fade/FadeOut"; import type BattleScene from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; -import { FixedInt } from "#app/utils"; +import { FixedInt } from "#app/utils/common"; type FadeInType = typeof FadeIn; type FadeOutType = typeof FadeOut; diff --git a/src/system/settings/settings-gamepad.ts b/src/system/settings/settings-gamepad.ts index f4a6bd465af..12add905096 100644 --- a/src/system/settings/settings-gamepad.ts +++ b/src/system/settings/settings-gamepad.ts @@ -1,6 +1,6 @@ import type SettingsGamepadUiHandler from "../../ui/settings/settings-gamepad-ui-handler"; -import { Mode } from "../../ui/ui"; -import { truncateString } from "../../utils"; +import { UiMode } from "#enums/ui-mode"; +import { truncateString } from "../../utils/common"; import { Button } from "#enums/buttons"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; import { globalScene } from "#app/global-scene"; @@ -107,7 +107,7 @@ export function setSettingGamepad(setting: SettingGamepad, value: number): boole (globalScene.ui.getHandler() as SettingsGamepadUiHandler).updateBindings(); return success; }; - globalScene.ui.setOverlayMode(Mode.GAMEPAD_BINDING, { + globalScene.ui.setOverlayMode(UiMode.GAMEPAD_BINDING, { target: setting, cancelHandler: cancelHandler, }); @@ -133,7 +133,7 @@ export function setSettingGamepad(setting: SettingGamepad, value: number): boole cancelHandler(); return true; }; - globalScene.ui.setOverlayMode(Mode.OPTION_SELECT, { + globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, { options: [ ...gp.map((g: string) => ({ label: truncateString(g, 30), // Truncate the gamepad name for display diff --git a/src/system/settings/settings-keyboard.ts b/src/system/settings/settings-keyboard.ts index ffe8811e5d9..ec5c9ad6b0e 100644 --- a/src/system/settings/settings-keyboard.ts +++ b/src/system/settings/settings-keyboard.ts @@ -1,5 +1,5 @@ import { Button } from "#enums/buttons"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import type SettingsKeyboardUiHandler from "#app/ui/settings/settings-keyboard-ui-handler"; import i18next from "i18next"; import { globalScene } from "#app/global-scene"; @@ -174,7 +174,7 @@ export function setSettingKeyboard(setting: SettingKeyboard, value: number): boo (globalScene.ui.getHandler() as SettingsKeyboardUiHandler).updateBindings(); return success; }; - globalScene.ui.setOverlayMode(Mode.KEYBOARD_BINDING, { + globalScene.ui.setOverlayMode(UiMode.KEYBOARD_BINDING, { target: setting, cancelHandler: cancelHandler, }); diff --git a/src/system/settings/settings.ts b/src/system/settings/settings.ts index 377216291e2..31faf2b6283 100644 --- a/src/system/settings/settings.ts +++ b/src/system/settings/settings.ts @@ -1,4 +1,4 @@ -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import { globalScene } from "#app/global-scene"; import { hasTouchscreen } from "#app/touch-controls"; @@ -9,7 +9,7 @@ import { EaseType } from "#enums/ease-type"; import { MoneyFormat } from "#enums/money-format"; import { PlayerGender } from "#enums/player-gender"; import { ShopCursorTarget } from "#enums/shop-cursor-target"; -import { isLocal } from "#app/utils"; +import { isLocal } from "#app/utils/common"; const VOLUME_OPTIONS: SettingOption[] = new Array(11).fill(null).map((_, i) => i @@ -906,7 +906,7 @@ export function setSetting(setting: string, value: number): boolean { return false; } }; - globalScene.ui.setOverlayMode(Mode.OPTION_SELECT, { + globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, { options: [ { label: "English", diff --git a/src/system/version_migration/versions/v1_0_4.ts b/src/system/version_migration/versions/v1_0_4.ts index 2139352b783..9e30ccdc2a7 100644 --- a/src/system/version_migration/versions/v1_0_4.ts +++ b/src/system/version_migration/versions/v1_0_4.ts @@ -3,7 +3,7 @@ import type { SystemSaveData, SessionSaveData } from "#app/system/game-data"; import { AbilityAttr, defaultStarterSpecies, DexAttr } from "#app/system/game-data"; import { allSpecies } from "#app/data/pokemon-species"; import { CustomPokemonData } from "#app/data/custom-pokemon-data"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator"; import type { SettingsSaveMigrator } from "#app/@types/SettingsSaveMigrator"; import type { SessionSaveMigrator } from "#app/@types/SessionSaveMigrator"; diff --git a/src/system/version_migration/versions/v1_7_0.ts b/src/system/version_migration/versions/v1_7_0.ts index a1213ccf64c..dc7c0f48640 100644 --- a/src/system/version_migration/versions/v1_7_0.ts +++ b/src/system/version_migration/versions/v1_7_0.ts @@ -3,7 +3,7 @@ import type { SystemSaveMigrator } from "#app/@types/SystemSaveMigrator"; import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; import { globalScene } from "#app/global-scene"; import { DexAttr, type SessionSaveData, type SystemSaveData } from "#app/system/game-data"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; /** * If a starter is caught, but the only forms registered as caught are not starterSelectable, diff --git a/src/timed-event-manager.ts b/src/timed-event-manager.ts index 7bbd157948b..8f5a9c75428 100644 --- a/src/timed-event-manager.ts +++ b/src/timed-event-manager.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import { TextStyle, addTextObject } from "#app/ui/text"; -import type { nil } from "#app/utils"; -import { isNullOrUndefined } from "#app/utils"; +import type { nil } from "#app/utils/common"; +import { isNullOrUndefined } from "#app/utils/common"; import i18next from "i18next"; import { Species } from "#enums/species"; import type { WeatherPoolEntry } from "#app/data/weather"; diff --git a/src/tutorial.ts b/src/tutorial.ts index 82912f73979..d9ae3a03290 100644 --- a/src/tutorial.ts +++ b/src/tutorial.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import AwaitableUiHandler from "./ui/awaitable-ui-handler"; import type UiHandler from "./ui/ui-handler"; -import { Mode } from "./ui/ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import Overrides from "#app/overrides"; @@ -92,13 +92,13 @@ const tutorialHandlers = { }, [Tutorial.Select_Item]: () => { return new Promise(resolve => { - globalScene.ui.setModeWithoutClear(Mode.MESSAGE).then(() => { + globalScene.ui.setModeWithoutClear(UiMode.MESSAGE).then(() => { globalScene.ui.showText( i18next.t("tutorial:selectItem"), null, () => globalScene.ui.showText("", null, () => - globalScene.ui.setModeWithoutClear(Mode.MODIFIER_SELECT).then(() => resolve()), + globalScene.ui.setModeWithoutClear(UiMode.MODIFIER_SELECT).then(() => resolve()), ), null, true, diff --git a/src/ui-inputs.ts b/src/ui-inputs.ts index c9898f9b71e..bf4f51e5af7 100644 --- a/src/ui-inputs.ts +++ b/src/ui-inputs.ts @@ -1,5 +1,5 @@ import type Phaser from "phaser"; -import { Mode } from "./ui/ui"; +import { UiMode } from "#enums/ui-mode"; import type { InputsController } from "./inputs-controller"; import type MessageUiHandler from "./ui/message-ui-handler"; import StarterSelectUiHandler from "./ui/starter-select-ui-handler"; @@ -176,22 +176,22 @@ export class UiInputs { return; } switch (globalScene.ui?.getMode()) { - case Mode.MESSAGE: + case UiMode.MESSAGE: const messageHandler = globalScene.ui.getHandler(); if (!messageHandler.pendingPrompt || messageHandler.isTextAnimationInProgress()) { return; } - case Mode.TITLE: - case Mode.COMMAND: - case Mode.MODIFIER_SELECT: - case Mode.MYSTERY_ENCOUNTER: - globalScene.ui.setOverlayMode(Mode.MENU); + case UiMode.TITLE: + case UiMode.COMMAND: + case UiMode.MODIFIER_SELECT: + case UiMode.MYSTERY_ENCOUNTER: + globalScene.ui.setOverlayMode(UiMode.MENU); break; - case Mode.STARTER_SELECT: - case Mode.POKEDEX_PAGE: + case UiMode.STARTER_SELECT: + case UiMode.POKEDEX_PAGE: this.buttonTouch(); break; - case Mode.MENU: + case UiMode.MENU: globalScene.ui.revertMode(); globalScene.playSound("ui/select"); break; @@ -227,7 +227,7 @@ export class UiInputs { SettingKeys.Game_Speed, Setting[settingGameSpeed].options.findIndex(item => item.label === `${globalScene.gameSpeed}x`) + 1, ); - if (globalScene.ui?.getMode() === Mode.SETTINGS) { + if (globalScene.ui?.getMode() === UiMode.SETTINGS) { (globalScene.ui.getHandler() as SettingsUiHandler).show([]); } } else if (!up && globalScene.gameSpeed > 1) { @@ -238,7 +238,7 @@ export class UiInputs { 0, ), ); - if (globalScene.ui?.getMode() === Mode.SETTINGS) { + if (globalScene.ui?.getMode() === UiMode.SETTINGS) { (globalScene.ui.getHandler() as SettingsUiHandler).show([]); } } diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstact-option-select-ui-handler.ts index b360065f61d..07609648a4e 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstact-option-select-ui-handler.ts @@ -1,9 +1,9 @@ import { globalScene } from "#app/global-scene"; import { TextStyle, addBBCodeTextObject, getTextColor, getTextStyleOptions } from "./text"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; -import { rgbHexToRgba, fixedInt } from "#app/utils"; +import { rgbHexToRgba, fixedInt } from "#app/utils/common"; import { argbFromRgba } from "@material/material-color-utilities"; import { Button } from "#enums/buttons"; import BBCodeText from "phaser3-rex-plugins/plugins/gameobjects/tagtext/bbcodetext/BBCodeText"; @@ -56,7 +56,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { protected defaultTextStyle: TextStyle = TextStyle.WINDOW; protected textContent: string; - constructor(mode: Mode | null) { + constructor(mode: UiMode | null) { super(mode); } @@ -70,7 +70,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { const ui = this.getUi(); this.optionSelectContainer = globalScene.add.container(globalScene.game.canvas.width / 6 - 1, -48); - this.optionSelectContainer.setName(`option-select-${this.mode ? Mode[this.mode] : "UNKNOWN"}`); + this.optionSelectContainer.setName(`option-select-${this.mode ? UiMode[this.mode] : "UNKNOWN"}`); this.optionSelectContainer.setVisible(false); ui.add(this.optionSelectContainer); @@ -120,7 +120,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { // Setting the initial text to establish the width of the select object. We consider all options, even ones that are not displayed, // Except in the case of autocomplete, where we don't want to set up a text element with potentially hundreds of lines. - const optionsForWidth = globalScene.ui.getMode() === Mode.AUTO_COMPLETE ? optionsWithScroll : options; + const optionsForWidth = globalScene.ui.getMode() === UiMode.AUTO_COMPLETE ? optionsWithScroll : options; this.optionSelectText = addBBCodeTextObject( 0, 0, @@ -250,7 +250,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { } else { ui.playError(); } - } else if (button === Button.SUBMIT && ui.getMode() === Mode.AUTO_COMPLETE) { + } else if (button === Button.SUBMIT && ui.getMode() === UiMode.AUTO_COMPLETE) { // this is here to differentiate between a Button.SUBMIT vs Button.ACTION within the autocomplete handler // this is here because Button.ACTION is picked up as z on the keyboard, meaning if you're typing and hit z, it'll select the option you've chosen success = true; diff --git a/src/ui/achvs-ui-handler.ts b/src/ui/achvs-ui-handler.ts index 8b5a4dbd395..d0c8b716c7a 100644 --- a/src/ui/achvs-ui-handler.ts +++ b/src/ui/achvs-ui-handler.ts @@ -6,7 +6,7 @@ import type { Voucher } from "#app/system/voucher"; import { getVoucherTypeIcon, getVoucherTypeName, vouchers } from "#app/system/voucher"; import MessageUiHandler from "#app/ui/message-ui-handler"; import { addTextObject, TextStyle } from "#app/ui/text"; -import type { Mode } from "#app/ui/ui"; +import type { UiMode } from "#enums/ui-mode"; import { addWindow } from "#app/ui/ui-theme"; import { ScrollBar } from "#app/ui/scroll-bar"; import { PlayerGender } from "#enums/player-gender"; @@ -59,7 +59,7 @@ export default class AchvsUiHandler extends MessageUiHandler { private cursorObj: Phaser.GameObjects.NineSlice | null; private currentPage: Page; - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); this.achvsTotal = Object.keys(achvs).length; diff --git a/src/ui/admin-ui-handler.ts b/src/ui/admin-ui-handler.ts index 34b6e59145f..67ae3118863 100644 --- a/src/ui/admin-ui-handler.ts +++ b/src/ui/admin-ui-handler.ts @@ -1,11 +1,11 @@ import { Button } from "#app/enums/buttons"; import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; -import { formatText } from "#app/utils"; +import { formatText } from "#app/utils/common"; import type { InputFieldConfig } from "./form-modal-ui-handler"; import { FormModalUiHandler } from "./form-modal-ui-handler"; import type { ModalConfig } from "./modal-ui-handler"; import { TextStyle } from "./text"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import { globalScene } from "#app/global-scene"; type AdminUiHandlerService = "discord" | "google"; @@ -30,7 +30,7 @@ export default class AdminUiHandler extends FormModalUiHandler { return `Username and ${service} successfully ${mode.toLowerCase()}ed`; }; - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); } @@ -143,10 +143,10 @@ export default class AdminUiHandler extends FormModalUiHandler { const adminSearchResult: AdminSearchInfo = this.convertInputsToAdmin(); // this converts the input texts into a single object for use later const validFields = this.areFieldsValid(this.adminMode); if (validFields.error) { - globalScene.ui.setMode(Mode.LOADING, { buttonActions: [] }); // this is here to force a loading screen to allow the admin tool to reopen again if there's an error + globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); // this is here to force a loading screen to allow the admin tool to reopen again if there's an error return this.showMessage(validFields.errorMessage ?? "", adminSearchResult, true); } - globalScene.ui.setMode(Mode.LOADING, { buttonActions: [] }); + globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); if (this.adminMode === AdminMode.LINK) { this.adminLinkUnlink(adminSearchResult, "discord", "Link") // calls server to link discord .then(response => { @@ -174,7 +174,7 @@ export default class AdminUiHandler extends FormModalUiHandler { showMessage(message: string, adminResult: AdminSearchInfo, isError: boolean) { globalScene.ui.setMode( - Mode.ADMIN, + UiMode.ADMIN, Object.assign(this.config, { errorMessage: message?.trim() }), this.adminMode, adminResult, @@ -221,18 +221,18 @@ export default class AdminUiHandler extends FormModalUiHandler { const mode = adminResult[aR] === "" ? "Link" : "Unlink"; // this figures out if we're linking or unlinking a service const validFields = this.areFieldsValid(this.adminMode, service); if (validFields.error) { - globalScene.ui.setMode(Mode.LOADING, { buttonActions: [] }); // this is here to force a loading screen to allow the admin tool to reopen again if there's an error + globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); // this is here to force a loading screen to allow the admin tool to reopen again if there's an error return this.showMessage(validFields.errorMessage ?? "", adminResult, true); } this.adminLinkUnlink(this.convertInputsToAdmin(), service as AdminUiHandlerService, mode).then( response => { // attempts to link/unlink depending on the service if (response.error) { - globalScene.ui.setMode(Mode.LOADING, { buttonActions: [] }); + globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); return this.showMessage(response.errorType, adminResult, true); // fail } // success, reload panel with new results - globalScene.ui.setMode(Mode.LOADING, { buttonActions: [] }); + globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); this.adminSearch(adminResult).then(response => { if (response.error) { return this.showMessage(response.errorType, adminResult, true); @@ -385,7 +385,7 @@ export default class AdminUiHandler extends FormModalUiHandler { private updateAdminPanelInfo(adminSearchResult: AdminSearchInfo, mode?: AdminMode) { mode = mode ?? AdminMode.ADMIN; globalScene.ui.setMode( - Mode.ADMIN, + UiMode.ADMIN, { buttonActions: [ // we double revert here and below to go back 2 layers of menus diff --git a/src/ui/arena-flyout.ts b/src/ui/arena-flyout.ts index 1eb18a32f98..ab3bd13b47a 100644 --- a/src/ui/arena-flyout.ts +++ b/src/ui/arena-flyout.ts @@ -16,7 +16,7 @@ import type { TurnEndEvent } from "../events/battle-scene"; import { BattleSceneEventType } from "../events/battle-scene"; import { ArenaTagType } from "#enums/arena-tag-type"; import TimeOfDayWidget from "./time-of-day-widget"; -import { toCamelCaseString, formatText, fixedInt } from "#app/utils"; +import { toCamelCaseString, formatText, fixedInt } from "#app/utils/common"; import type { ParseKeys } from "i18next"; import i18next from "i18next"; diff --git a/src/ui/autocomplete-ui-handler.ts b/src/ui/autocomplete-ui-handler.ts index a170ae43f23..ba1802c8582 100644 --- a/src/ui/autocomplete-ui-handler.ts +++ b/src/ui/autocomplete-ui-handler.ts @@ -1,10 +1,10 @@ import { Button } from "#enums/buttons"; import AbstractOptionSelectUiHandler from "./abstact-option-select-ui-handler"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler { modalContainer: Phaser.GameObjects.Container; - constructor(mode: Mode = Mode.OPTION_SELECT) { + constructor(mode: UiMode = UiMode.OPTION_SELECT) { super(mode); } diff --git a/src/ui/awaitable-ui-handler.ts b/src/ui/awaitable-ui-handler.ts index 890e2884fd5..3c577fd4411 100644 --- a/src/ui/awaitable-ui-handler.ts +++ b/src/ui/awaitable-ui-handler.ts @@ -1,4 +1,4 @@ -import type { Mode } from "./ui"; +import type { UiMode } from "#enums/ui-mode"; import UiHandler from "./ui-handler"; import { Button } from "#enums/buttons"; import { globalScene } from "#app/global-scene"; @@ -9,7 +9,7 @@ export default abstract class AwaitableUiHandler extends UiHandler { public tutorialActive = false; public tutorialOverlay: Phaser.GameObjects.Rectangle; - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); } diff --git a/src/ui/ball-ui-handler.ts b/src/ui/ball-ui-handler.ts index cfa44832824..abb106a6553 100644 --- a/src/ui/ball-ui-handler.ts +++ b/src/ui/ball-ui-handler.ts @@ -1,7 +1,7 @@ import { getPokeballName } from "../data/pokeball"; import { addTextObject, getTextStyleOptions, TextStyle } from "./text"; import { Command } from "./command-ui-handler"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; import { Button } from "#enums/buttons"; @@ -18,7 +18,7 @@ export default class BallUiHandler extends UiHandler { private scale = 0.1666666667; constructor() { - super(Mode.BALL); + super(UiMode.BALL); } setup() { @@ -82,15 +82,15 @@ export default class BallUiHandler extends UiHandler { if (button === Button.ACTION && this.cursor < pokeballTypeCount) { if (globalScene.pokeballCounts[this.cursor]) { if (commandPhase.handleCommand(Command.BALL, this.cursor)) { - globalScene.ui.setMode(Mode.COMMAND, commandPhase.getFieldIndex()); - globalScene.ui.setMode(Mode.MESSAGE); + globalScene.ui.setMode(UiMode.COMMAND, commandPhase.getFieldIndex()); + globalScene.ui.setMode(UiMode.MESSAGE); success = true; } } else { ui.playError(); } } else { - ui.setMode(Mode.COMMAND, commandPhase.getFieldIndex()); + ui.setMode(UiMode.COMMAND, commandPhase.getFieldIndex()); success = true; } } else { diff --git a/src/ui/base-stats-overlay.ts b/src/ui/base-stats-overlay.ts index d0b0aff3a9d..0541ae766e5 100644 --- a/src/ui/base-stats-overlay.ts +++ b/src/ui/base-stats-overlay.ts @@ -1,7 +1,7 @@ import type { InfoToggle } from "../battle-scene"; import { TextStyle, addTextObject } from "./text"; import { addWindow } from "./ui-theme"; -import { fixedInt } from "#app/utils"; +import { fixedInt } from "#app/utils/common"; import i18next from "i18next"; import { globalScene } from "#app/global-scene"; diff --git a/src/ui/battle-flyout.ts b/src/ui/battle-flyout.ts index 854f4cc4dd9..e590bebcf5a 100644 --- a/src/ui/battle-flyout.ts +++ b/src/ui/battle-flyout.ts @@ -1,6 +1,6 @@ import type { default as Pokemon } from "../field/pokemon"; import { addTextObject, TextStyle } from "./text"; -import { fixedInt } from "#app/utils"; +import { fixedInt } from "#app/utils/common"; import { globalScene } from "#app/global-scene"; import type Move from "#app/data/moves/move"; import type { BerryUsedEvent, MoveUsedEvent } from "../events/battle-scene"; diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index 06c5f7fb3f1..4f9e59c8c89 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -1,6 +1,6 @@ import type { EnemyPokemon, default as Pokemon } from "../field/pokemon"; import { getLevelTotalExp, getLevelRelExp } from "../data/exp"; -import { getLocalizedSpriteKey, fixedInt } from "#app/utils"; +import { getLocalizedSpriteKey, fixedInt } from "#app/utils/common"; import { addTextObject, TextStyle } from "./text"; import { getGenderSymbol, getGenderColor, Gender } from "../data/gender"; import { StatusEffect } from "#enums/status-effect"; @@ -617,7 +617,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { return resolve(); } - const gender: Gender = !!pokemon.summonData?.illusion ? pokemon.summonData?.illusion.gender : pokemon.gender; + const gender: Gender = pokemon.summonData?.illusion ? pokemon.summonData?.illusion.gender : pokemon.gender; this.genderText.setText(getGenderSymbol(gender)); this.genderText.setColor(getGenderColor(gender)); @@ -794,7 +794,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { const nameSizeTest = addTextObject(0, 0, displayName, TextStyle.BATTLE_INFO); nameTextWidth = nameSizeTest.displayWidth; - const gender: Gender = !!pokemon.summonData?.illusion ? pokemon.summonData?.illusion.gender : pokemon.gender; + const gender: Gender = pokemon.summonData?.illusion ? pokemon.summonData?.illusion.gender : pokemon.gender; while ( nameTextWidth > (this.player || !this.boss ? 60 : 98) - diff --git a/src/ui/battle-message-ui-handler.ts b/src/ui/battle-message-ui-handler.ts index ccb9378c688..d1102bbe53e 100644 --- a/src/ui/battle-message-ui-handler.ts +++ b/src/ui/battle-message-ui-handler.ts @@ -1,6 +1,6 @@ import { globalScene } from "#app/global-scene"; import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "./text"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import MessageUiHandler from "./message-ui-handler"; import { addWindow } from "./ui-theme"; import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; @@ -23,7 +23,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler { public readonly wordWrapWidth: number = 1780; constructor() { - super(Mode.MESSAGE); + super(UiMode.MESSAGE); } setup(): void { diff --git a/src/ui/bgm-bar.ts b/src/ui/bgm-bar.ts index d944453ba2c..e331d82f6d9 100644 --- a/src/ui/bgm-bar.ts +++ b/src/ui/bgm-bar.ts @@ -1,6 +1,6 @@ import { addTextObject, TextStyle } from "./text"; import i18next from "i18next"; -import { formatText } from "#app/utils"; +import { formatText } from "#app/utils/common"; import { globalScene } from "#app/global-scene"; const hiddenX = -150; diff --git a/src/ui/candy-bar.ts b/src/ui/candy-bar.ts index 0cf3e0c91e9..f7a01b83093 100644 --- a/src/ui/candy-bar.ts +++ b/src/ui/candy-bar.ts @@ -1,8 +1,8 @@ -import { starterColors } from "#app/battle-scene"; +import { starterColors } from "#app/global-vars/starter-colors"; import { globalScene } from "#app/global-scene"; import { TextStyle, addTextObject } from "./text"; import { argbFromRgba } from "@material/material-color-utilities"; -import { rgbHexToRgba } from "#app/utils"; +import { rgbHexToRgba } from "#app/utils/common"; import type { Species } from "#enums/species"; export default class CandyBar extends Phaser.GameObjects.Container { diff --git a/src/ui/challenges-select-ui-handler.ts b/src/ui/challenges-select-ui-handler.ts index caffede2487..d1df16a457b 100644 --- a/src/ui/challenges-select-ui-handler.ts +++ b/src/ui/challenges-select-ui-handler.ts @@ -1,11 +1,11 @@ import { TextStyle, addTextObject } from "./text"; -import type { Mode } from "./ui"; +import type { UiMode } from "#enums/ui-mode"; import UiHandler from "./ui-handler"; import { addWindow } from "./ui-theme"; import { Button } from "#enums/buttons"; import i18next from "i18next"; import type { Challenge } from "#app/data/challenge"; -import { getLocalizedSpriteKey } from "#app/utils"; +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"; @@ -50,7 +50,7 @@ export default class GameChallengesUiHandler extends UiHandler { private readonly leftArrowGap: number = 90; // distance from the label to the left arrow private readonly arrowSpacing: number = 3; // distance between the arrows and the value area - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); } diff --git a/src/ui/char-sprite.ts b/src/ui/char-sprite.ts index f717927c107..a8451f4bb9c 100644 --- a/src/ui/char-sprite.ts +++ b/src/ui/char-sprite.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { MissingTextureKey } from "#app/utils"; +import { MissingTextureKey } from "#app/utils/common"; export default class CharSprite extends Phaser.GameObjects.Container { private sprite: Phaser.GameObjects.Sprite; diff --git a/src/ui/command-ui-handler.ts b/src/ui/command-ui-handler.ts index 55937bb8b00..57c5b5a82a2 100644 --- a/src/ui/command-ui-handler.ts +++ b/src/ui/command-ui-handler.ts @@ -1,6 +1,6 @@ import { addTextObject, TextStyle } from "./text"; import PartyUiHandler, { PartyUiMode } from "./party-ui-handler"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import UiHandler from "./ui-handler"; import i18next from "i18next"; import { Button } from "#enums/buttons"; @@ -30,7 +30,7 @@ export default class CommandUiHandler extends UiHandler { protected cursor2 = 0; constructor() { - super(Mode.COMMAND); + super(UiMode.COMMAND); } setup() { @@ -124,18 +124,18 @@ export default class CommandUiHandler extends UiHandler { switch (cursor) { // Fight case Command.FIGHT: - ui.setMode(Mode.FIGHT, (globalScene.getCurrentPhase() as CommandPhase).getFieldIndex()); + ui.setMode(UiMode.FIGHT, (globalScene.getCurrentPhase() as CommandPhase).getFieldIndex()); success = true; break; // Ball case Command.BALL: - ui.setModeWithoutClear(Mode.BALL); + ui.setModeWithoutClear(UiMode.BALL); success = true; break; // Pokemon case Command.POKEMON: ui.setMode( - Mode.PARTY, + UiMode.PARTY, PartyUiMode.SWITCH, (globalScene.getCurrentPhase() as CommandPhase).getPokemon().getFieldIndex(), null, @@ -149,7 +149,7 @@ export default class CommandUiHandler extends UiHandler { success = true; break; case Command.TERA: - ui.setMode(Mode.FIGHT, (globalScene.getCurrentPhase() as CommandPhase).getFieldIndex(), Command.TERA); + ui.setMode(UiMode.FIGHT, (globalScene.getCurrentPhase() as CommandPhase).getFieldIndex(), Command.TERA); success = true; break; } diff --git a/src/ui/confirm-ui-handler.ts b/src/ui/confirm-ui-handler.ts index eb7018051b7..7b5ca3d7e63 100644 --- a/src/ui/confirm-ui-handler.ts +++ b/src/ui/confirm-ui-handler.ts @@ -1,6 +1,6 @@ import type { OptionSelectConfig } from "./abstact-option-select-ui-handler"; import AbstractOptionSelectUiHandler from "./abstact-option-select-ui-handler"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import { Button } from "#enums/buttons"; import { globalScene } from "#app/global-scene"; @@ -12,7 +12,7 @@ export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler { private switchCheckCursor: number; constructor() { - super(Mode.CONFIRM); + super(UiMode.CONFIRM); } getWindowWidth(): number { diff --git a/src/ui/daily-run-scoreboard.ts b/src/ui/daily-run-scoreboard.ts index 896f2171676..076a782908b 100644 --- a/src/ui/daily-run-scoreboard.ts +++ b/src/ui/daily-run-scoreboard.ts @@ -1,6 +1,6 @@ import i18next from "i18next"; import { globalScene } from "#app/global-scene"; -import { getEnumKeys, executeIf } from "#app/utils"; +import { getEnumKeys, executeIf } from "#app/utils/common"; import { TextStyle, addTextObject } from "./text"; import { WindowVariant, addWindow } from "./ui-theme"; import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index 956a308448b..5377cf3d283 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -1,7 +1,7 @@ -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import { TextStyle, addTextObject, getEggTierTextTint, getTextStyleOptions } from "./text"; import MessageUiHandler from "./message-ui-handler"; -import { getEnumValues, getEnumKeys, fixedInt, randSeedShuffle } from "#app/utils"; +import { getEnumValues, getEnumKeys, fixedInt, randSeedShuffle } from "#app/utils/common"; import type { IEggOptions } from "../data/egg"; import { Egg, getLegendaryGachaSpeciesForTimestamp } from "../data/egg"; import { VoucherType, getVoucherTypeIcon } from "../system/voucher"; @@ -41,7 +41,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { private scale = 0.1666666667; constructor() { - super(Mode.EGG_GACHA); + super(UiMode.EGG_GACHA); this.gachaContainers = []; this.gachaKnobs = []; diff --git a/src/ui/egg-hatch-scene-handler.ts b/src/ui/egg-hatch-scene-handler.ts index 6ede68b7ae6..76e2c54f4b6 100644 --- a/src/ui/egg-hatch-scene-handler.ts +++ b/src/ui/egg-hatch-scene-handler.ts @@ -1,4 +1,4 @@ -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import UiHandler from "./ui-handler"; import { Button } from "#enums/buttons"; import { EggHatchPhase } from "#app/phases/egg-hatch-phase"; @@ -16,7 +16,7 @@ export default class EggHatchSceneHandler extends UiHandler { public readonly eventTarget: EventTarget = new EventTarget(); constructor() { - super(Mode.EGG_HATCH_SCENE); + super(UiMode.EGG_HATCH_SCENE); } setup() { diff --git a/src/ui/egg-list-ui-handler.ts b/src/ui/egg-list-ui-handler.ts index cf3326bec13..9f41feea8ab 100644 --- a/src/ui/egg-list-ui-handler.ts +++ b/src/ui/egg-list-ui-handler.ts @@ -1,4 +1,4 @@ -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "#app/ui/pokemon-icon-anim-handler"; import { TextStyle, addTextObject } from "#app/ui/text"; import MessageUiHandler from "#app/ui/message-ui-handler"; @@ -29,7 +29,7 @@ export default class EggListUiHandler extends MessageUiHandler { private iconAnimHandler: PokemonIconAnimHandler; constructor() { - super(Mode.EGG_LIST); + super(UiMode.EGG_LIST); } setup() { diff --git a/src/ui/egg-summary-ui-handler.ts b/src/ui/egg-summary-ui-handler.ts index f335f83d8bf..ddc536fe1ad 100644 --- a/src/ui/egg-summary-ui-handler.ts +++ b/src/ui/egg-summary-ui-handler.ts @@ -1,4 +1,4 @@ -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler"; import MessageUiHandler from "./message-ui-handler"; import { getEggTierForSpecies } from "../data/egg"; @@ -54,7 +54,7 @@ export default class EggSummaryUiHandler extends MessageUiHandler { public readonly eventTarget: EventTarget = new EventTarget(); constructor() { - super(Mode.EGG_HATCH_SUMMARY); + super(UiMode.EGG_HATCH_SUMMARY); } setup() { diff --git a/src/ui/evolution-scene-handler.ts b/src/ui/evolution-scene-handler.ts index 91f3360a3d4..cea91ce4e2c 100644 --- a/src/ui/evolution-scene-handler.ts +++ b/src/ui/evolution-scene-handler.ts @@ -1,6 +1,6 @@ import MessageUiHandler from "./message-ui-handler"; import { TextStyle, addTextObject } from "./text"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import { Button } from "#enums/buttons"; import { globalScene } from "#app/global-scene"; @@ -12,7 +12,7 @@ export default class EvolutionSceneHandler extends MessageUiHandler { public cancelled: boolean; constructor() { - super(Mode.EVOLUTION_SCENE); + super(UiMode.EVOLUTION_SCENE); } setup() { diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index 285a1dd36cc..e0a73d62934 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -4,9 +4,9 @@ import { addTextObject, TextStyle } from "./text"; import { getTypeDamageMultiplierColor } from "#app/data/type"; import { PokemonType } from "#enums/pokemon-type"; import { Command } from "./command-ui-handler"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import UiHandler from "./ui-handler"; -import { getLocalizedSpriteKey, fixedInt, padInt } from "#app/utils"; +import { getLocalizedSpriteKey, fixedInt, padInt } from "#app/utils/common"; import { MoveCategory } from "#enums/MoveCategory"; import i18next from "i18next"; import { Button } from "#enums/buttons"; @@ -37,7 +37,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { protected cursor2 = 0; constructor() { - super(Mode.FIGHT); + super(UiMode.FIGHT); } setup() { @@ -156,7 +156,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { // Cannot back out of fight menu if skipToFightInput is enabled const { battleType, mysteryEncounter } = globalScene.currentBattle; if (battleType !== BattleType.MYSTERY_ENCOUNTER || !mysteryEncounter?.skipToFightInput) { - ui.setMode(Mode.COMMAND, this.fieldIndex); + ui.setMode(UiMode.COMMAND, this.fieldIndex); success = true; } } @@ -308,7 +308,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { !opponent.battleData?.abilityRevealed, undefined, undefined, - true + true, ); if (effectiveness === undefined) { return undefined; @@ -353,7 +353,14 @@ export default class FightUiHandler extends UiHandler implements InfoToggle { const moveColors = opponents .map(opponent => - opponent.getMoveEffectiveness(pokemon, pokemonMove.getMove(), !opponent.battleData.abilityRevealed, undefined, undefined, true), + opponent.getMoveEffectiveness( + pokemon, + pokemonMove.getMove(), + !opponent.battleData.abilityRevealed, + undefined, + undefined, + true, + ), ) .sort((a, b) => b - a) .map(effectiveness => getTypeDamageMultiplierColor(effectiveness ?? 0, "offense")); diff --git a/src/ui/filter-text.ts b/src/ui/filter-text.ts index a6b01ba39e6..8b13b76db31 100644 --- a/src/ui/filter-text.ts +++ b/src/ui/filter-text.ts @@ -5,7 +5,7 @@ import { addWindow, WindowVariant } from "./ui-theme"; import i18next from "i18next"; import type AwaitableUiHandler from "./awaitable-ui-handler"; import type UI from "./ui"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import { globalScene } from "#app/global-scene"; export enum FilterTextRow { @@ -154,7 +154,7 @@ export class FilterText extends Phaser.GameObjects.Container { this.onChange; }, ]; - ui.setOverlayMode(Mode.POKEDEX_SCAN, buttonAction, prefilledText, index); + ui.setOverlayMode(UiMode.POKEDEX_SCAN, buttonAction, prefilledText, index); } setCursor(cursor: number): void { diff --git a/src/ui/form-modal-ui-handler.ts b/src/ui/form-modal-ui-handler.ts index e27b2e9ed89..e8e67d591d5 100644 --- a/src/ui/form-modal-ui-handler.ts +++ b/src/ui/form-modal-ui-handler.ts @@ -1,10 +1,10 @@ import type { ModalConfig } from "./modal-ui-handler"; import { ModalUiHandler } from "./modal-ui-handler"; -import type { Mode } from "./ui"; +import type { UiMode } from "#enums/ui-mode"; import { TextStyle, addTextInputObject, addTextObject } from "./text"; import { WindowVariant, addWindow } from "./ui-theme"; import type InputText from "phaser3-rex-plugins/plugins/inputtext"; -import { fixedInt } from "#app/utils"; +import { fixedInt } from "#app/utils/common"; import { Button } from "#enums/buttons"; import { globalScene } from "#app/global-scene"; @@ -21,7 +21,7 @@ export abstract class FormModalUiHandler extends ModalUiHandler { protected tween: Phaser.Tweens.Tween; protected formLabels: Phaser.GameObjects.Text[]; - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); this.editing = false; diff --git a/src/ui/game-stats-ui-handler.ts b/src/ui/game-stats-ui-handler.ts index 2e2112dfda4..dc184a34866 100644 --- a/src/ui/game-stats-ui-handler.ts +++ b/src/ui/game-stats-ui-handler.ts @@ -1,9 +1,9 @@ import Phaser from "phaser"; import { TextStyle, addTextObject } from "#app/ui/text"; -import type { Mode } from "#app/ui/ui"; +import type { UiMode } from "#enums/ui-mode"; import UiHandler from "#app/ui/ui-handler"; import { addWindow } from "#app/ui/ui-theme"; -import { getPlayTimeString, formatFancyLargeNumber, toReadableString } from "#app/utils"; +import { getPlayTimeString, formatFancyLargeNumber, toReadableString } from "#app/utils/common"; import type { GameData } from "#app/system/game-data"; import { DexAttr } from "#app/system/game-data"; import { speciesStarterCosts } from "#app/data/balance/starters"; @@ -223,7 +223,7 @@ export default class GameStatsUiHandler extends UiHandler { private arrowUp: Phaser.GameObjects.Sprite; private arrowDown: Phaser.GameObjects.Sprite; - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); this.statLabels = []; diff --git a/src/ui/loading-modal-ui-handler.ts b/src/ui/loading-modal-ui-handler.ts index 9626276245d..13dffe5614c 100644 --- a/src/ui/loading-modal-ui-handler.ts +++ b/src/ui/loading-modal-ui-handler.ts @@ -1,10 +1,10 @@ import i18next from "i18next"; import { ModalUiHandler } from "./modal-ui-handler"; import { addTextObject, TextStyle } from "./text"; -import type { Mode } from "./ui"; +import type { UiMode } from "#enums/ui-mode"; export default class LoadingModalUiHandler extends ModalUiHandler { - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); } diff --git a/src/ui/login-form-ui-handler.ts b/src/ui/login-form-ui-handler.ts index 5c009357443..2dfab9c0c40 100644 --- a/src/ui/login-form-ui-handler.ts +++ b/src/ui/login-form-ui-handler.ts @@ -1,8 +1,8 @@ import type { InputFieldConfig } from "./form-modal-ui-handler"; import { FormModalUiHandler } from "./form-modal-ui-handler"; import type { ModalConfig } from "./modal-ui-handler"; -import { fixedInt } from "#app/utils"; -import { Mode } from "./ui"; +import { fixedInt } from "#app/utils/common"; +import { UiMode } from "#enums/ui-mode"; import i18next from "i18next"; import { addTextObject, TextStyle } from "./text"; import { addWindow } from "./ui-theme"; @@ -34,7 +34,7 @@ export default class LoginFormUiHandler extends FormModalUiHandler { private infoContainer: Phaser.GameObjects.Container; private externalPartyBg: Phaser.GameObjects.NineSlice; private externalPartyTitle: Phaser.GameObjects.Text; - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); } @@ -146,9 +146,9 @@ export default class LoginFormUiHandler extends FormModalUiHandler { // Prevent overlapping overrides on action modification this.submitAction = originalLoginAction; this.sanitizeInputs(); - globalScene.ui.setMode(Mode.LOADING, { buttonActions: [] }); + globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); const onFail = error => { - globalScene.ui.setMode(Mode.LOGIN_FORM, Object.assign(config, { errorMessage: error?.trim() })); + globalScene.ui.setMode(UiMode.LOGIN_FORM, Object.assign(config, { errorMessage: error?.trim() })); globalScene.ui.playError(); }; if (!this.inputs[0].text) { @@ -215,8 +215,8 @@ export default class LoginFormUiHandler extends FormModalUiHandler { }); const onFail = error => { - globalScene.ui.setMode(Mode.LOADING, { buttonActions: [] }); - globalScene.ui.setModeForceTransition(Mode.LOGIN_FORM, Object.assign(config, { errorMessage: error?.trim() })); + globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); + globalScene.ui.setModeForceTransition(UiMode.LOGIN_FORM, Object.assign(config, { errorMessage: error?.trim() })); globalScene.ui.playError(); }; @@ -236,7 +236,7 @@ export default class LoginFormUiHandler extends FormModalUiHandler { }, }); } - globalScene.ui.setOverlayMode(Mode.OPTION_SELECT, { + globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, { options: options, delay: 1000, }); diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index 241ddbb91a8..7f0cd4d6a78 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -1,8 +1,10 @@ -import { bypassLogin } from "#app/battle-scene"; +import { bypassLogin } from "#app/global-vars/bypass-login"; import { globalScene } from "#app/global-scene"; import { TextStyle, addTextObject, getTextStyleOptions } from "./text"; -import { Mode } from "./ui"; -import { getEnumKeys, isLocal, isBeta, fixedInt, getCookie, sessionIdKey } from "#app/utils"; +import { UiMode } from "#enums/ui-mode"; +import { getEnumKeys, isLocal, fixedInt, sessionIdKey } from "#app/utils/common"; +import { isBeta } from "#app/utils/utility-vars"; +import { getCookie } from "#app/utils/cookies"; import { addWindow, WindowVariant } from "./ui-theme"; import MessageUiHandler from "./message-ui-handler"; import type { OptionSelectConfig, OptionSelectItem } from "./abstact-option-select-ui-handler"; @@ -64,12 +66,12 @@ export default class MenuUiHandler extends MessageUiHandler { public bgmBar: BgmBar; - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); this.excludedMenus = () => [ { - condition: [Mode.COMMAND, Mode.TITLE].includes(mode ?? Mode.TITLE), + condition: [UiMode.COMMAND, UiMode.TITLE].includes(mode ?? UiMode.TITLE), options: [MenuOptions.EGG_GACHA, MenuOptions.EGG_LIST], }, { condition: bypassLogin, options: [MenuOptions.LOG_OUT] }, @@ -234,7 +236,7 @@ export default class MenuUiHandler extends MessageUiHandler { ]), xOffset: 98, }; - ui.setOverlayMode(Mode.MENU_OPTION_SELECT, config); + ui.setOverlayMode(UiMode.MENU_OPTION_SELECT, config); }); }; @@ -377,7 +379,7 @@ export default class MenuUiHandler extends MessageUiHandler { ui.revertMode(); }, ]; - ui.setMode(Mode.TEST_DIALOGUE, buttonAction, prefilledText); + ui.setMode(UiMode.TEST_DIALOGUE, buttonAction, prefilledText); return true; }, keepOpen: true, @@ -456,7 +458,7 @@ export default class MenuUiHandler extends MessageUiHandler { handler: () => { ui.playSelect(); ui.setOverlayMode( - Mode.ADMIN, + UiMode.ADMIN, { buttonActions: [ // we double revert here and below to go back 2 layers of menus @@ -483,7 +485,7 @@ export default class MenuUiHandler extends MessageUiHandler { return true; }, }); - globalScene.ui.setOverlayMode(Mode.OPTION_SELECT, { + globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, { options: options, delay: 0, }); @@ -557,21 +559,21 @@ export default class MenuUiHandler extends MessageUiHandler { this.showText("", 0); switch (adjustedCursor) { case MenuOptions.GAME_SETTINGS: - ui.setOverlayMode(Mode.SETTINGS); + ui.setOverlayMode(UiMode.SETTINGS); success = true; break; case MenuOptions.ACHIEVEMENTS: - ui.setOverlayMode(Mode.ACHIEVEMENTS); + ui.setOverlayMode(UiMode.ACHIEVEMENTS); success = true; break; case MenuOptions.STATS: - ui.setOverlayMode(Mode.GAME_STATS); + ui.setOverlayMode(UiMode.GAME_STATS); success = true; break; case MenuOptions.EGG_LIST: if (globalScene.gameData.eggs.length) { ui.revertMode(); - ui.setOverlayMode(Mode.EGG_LIST); + ui.setOverlayMode(UiMode.EGG_LIST); success = true; } else { ui.showText(i18next.t("menuUiHandler:noEggs"), null, () => ui.showText(""), fixedInt(1500)); @@ -580,12 +582,12 @@ export default class MenuUiHandler extends MessageUiHandler { break; case MenuOptions.EGG_GACHA: ui.revertMode(); - ui.setOverlayMode(Mode.EGG_GACHA); + ui.setOverlayMode(UiMode.EGG_GACHA); success = true; break; case MenuOptions.POKEDEX: ui.revertMode(); - ui.setOverlayMode(Mode.POKEDEX); + ui.setOverlayMode(UiMode.POKEDEX); success = true; break; case MenuOptions.MANAGE_DATA: @@ -642,18 +644,18 @@ export default class MenuUiHandler extends MessageUiHandler { }, ); } - ui.setOverlayMode(Mode.MENU_OPTION_SELECT, this.manageDataConfig); + ui.setOverlayMode(UiMode.MENU_OPTION_SELECT, this.manageDataConfig); success = true; break; case MenuOptions.COMMUNITY: - ui.setOverlayMode(Mode.MENU_OPTION_SELECT, this.communityConfig); + ui.setOverlayMode(UiMode.MENU_OPTION_SELECT, this.communityConfig); success = true; break; case MenuOptions.SAVE_AND_QUIT: if (globalScene.currentBattle) { success = true; const doSaveQuit = () => { - ui.setMode(Mode.LOADING, { + ui.setMode(UiMode.LOADING, { buttonActions: [], fadeOut: () => globalScene.gameData.saveAll(true, true, true, true).then(() => { @@ -668,7 +670,7 @@ export default class MenuUiHandler extends MessageUiHandler { return; } ui.setOverlayMode( - Mode.CONFIRM, + UiMode.CONFIRM, doSaveQuit, () => { ui.revertMode(); @@ -688,7 +690,7 @@ export default class MenuUiHandler extends MessageUiHandler { case MenuOptions.LOG_OUT: success = true; const doLogout = () => { - ui.setMode(Mode.LOADING, { + ui.setMode(UiMode.LOADING, { buttonActions: [], fadeOut: () => pokerogueApi.account.logout().then(() => { @@ -703,7 +705,7 @@ export default class MenuUiHandler extends MessageUiHandler { return; } ui.setOverlayMode( - Mode.CONFIRM, + UiMode.CONFIRM, doLogout, () => { ui.revertMode(); @@ -722,7 +724,7 @@ export default class MenuUiHandler extends MessageUiHandler { success = true; ui.revertMode().then(result => { if (!result) { - ui.setMode(Mode.MESSAGE); + ui.setMode(UiMode.MESSAGE); } }); } else { diff --git a/src/ui/message-ui-handler.ts b/src/ui/message-ui-handler.ts index b57b236531c..efa53b63808 100644 --- a/src/ui/message-ui-handler.ts +++ b/src/ui/message-ui-handler.ts @@ -1,6 +1,6 @@ import AwaitableUiHandler from "./awaitable-ui-handler"; -import type { Mode } from "./ui"; -import { getFrameMs } from "#app/utils"; +import type { UiMode } from "#enums/ui-mode"; +import { getFrameMs } from "#app/utils/common"; import { globalScene } from "#app/global-scene"; export default abstract class MessageUiHandler extends AwaitableUiHandler { @@ -11,7 +11,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { public message: Phaser.GameObjects.Text; public prompt: Phaser.GameObjects.Sprite; - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); this.pendingPrompt = false; diff --git a/src/ui/modal-ui-handler.ts b/src/ui/modal-ui-handler.ts index b7dbbeb202d..a3b94296d3f 100644 --- a/src/ui/modal-ui-handler.ts +++ b/src/ui/modal-ui-handler.ts @@ -1,5 +1,5 @@ import { TextStyle, addTextObject } from "./text"; -import type { Mode } from "./ui"; +import type { UiMode } from "#enums/ui-mode"; import UiHandler from "./ui-handler"; import { WindowVariant, addWindow } from "./ui-theme"; import type { Button } from "#enums/buttons"; @@ -17,7 +17,7 @@ export abstract class ModalUiHandler extends UiHandler { protected buttonBgs: Phaser.GameObjects.NineSlice[]; protected buttonLabels: Phaser.GameObjects.Text[]; - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); this.buttonContainers = []; diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 26351d4dbf1..9ba54491175 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -4,13 +4,13 @@ import { getPlayerShopModifierTypeOptionsForWave, TmModifierType } from "../modi import { getPokeballAtlasKey } from "#app/data/pokeball"; import { addTextObject, getTextStyleOptions, getModifierTierTextTint, getTextColor, TextStyle } from "./text"; import AwaitableUiHandler from "./awaitable-ui-handler"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import { LockModifierTiersModifier, PokemonHeldItemModifier, HealShopCostModifier } from "../modifier/modifier"; import { handleTutorial, Tutorial } from "../tutorial"; import { Button } from "#enums/buttons"; import MoveInfoOverlay from "./move-info-overlay"; import { allMoves } from "../data/moves/move"; -import { formatMoney, NumberHolder } from "#app/utils"; +import { formatMoney, NumberHolder } from "#app/utils/common"; import Overrides from "#app/overrides"; import i18next from "i18next"; import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; @@ -50,7 +50,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { private cursorObj: Phaser.GameObjects.Image | null; constructor() { - super(Mode.CONFIRM); + super(UiMode.CONFIRM); this.options = []; this.shopOptionsRows = []; diff --git a/src/ui/move-info-overlay.ts b/src/ui/move-info-overlay.ts index bd9fdf00c72..2b230d609fd 100644 --- a/src/ui/move-info-overlay.ts +++ b/src/ui/move-info-overlay.ts @@ -2,7 +2,7 @@ import type { InfoToggle } from "#app/battle-scene"; import { globalScene } from "#app/global-scene"; import { TextStyle, addTextObject } from "./text"; import { addWindow } from "./ui-theme"; -import { getLocalizedSpriteKey, fixedInt } from "#app/utils"; +import { getLocalizedSpriteKey, fixedInt } from "#app/utils/common"; import type Move from "../data/moves/move"; import { MoveCategory } from "#enums/MoveCategory"; import { PokemonType } from "#enums/pokemon-type"; diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 2bf05302c55..0866ed8788e 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -1,12 +1,12 @@ import { addBBCodeTextObject, getBBCodeFrag, TextStyle } from "./text"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import UiHandler from "./ui-handler"; import { Button } from "#enums/buttons"; import { addWindow, WindowVariant } from "./ui-theme"; import type { MysteryEncounterPhase } from "../phases/mystery-encounter-phases"; import { PartyUiMode } from "./party-ui-handler"; import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; -import { fixedInt, isNullOrUndefined } from "#app/utils"; +import { fixedInt, isNullOrUndefined } from "#app/utils/common"; import { getPokeballAtlasKey } from "../data/pokeball"; import type { OptionSelectSettings } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; @@ -47,7 +47,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { protected blockInput = true; constructor() { - super(Mode.MYSTERY_ENCOUNTER); + super(UiMode.MYSTERY_ENCOUNTER); } override setup() { @@ -141,8 +141,8 @@ export default class MysteryEncounterUiHandler extends UiHandler { ...this.overrideSettings, slideInDescription: false, }; - globalScene.ui.setMode(Mode.PARTY, PartyUiMode.CHECK, -1, () => { - globalScene.ui.setMode(Mode.MYSTERY_ENCOUNTER, overrideSettings); + globalScene.ui.setMode(UiMode.PARTY, PartyUiMode.CHECK, -1, () => { + globalScene.ui.setMode(UiMode.MYSTERY_ENCOUNTER, overrideSettings); setTimeout(() => { this.setCursor(this.viewPartyIndex); this.unblockInput(); diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index ba90108c274..7c3689e757c 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -4,8 +4,8 @@ import { MoveResult } from "#app/field/pokemon"; import { addBBCodeTextObject, addTextObject, getTextColor, TextStyle } from "#app/ui/text"; import { Command } from "#app/ui/command-ui-handler"; import MessageUiHandler from "#app/ui/message-ui-handler"; -import { Mode } from "#app/ui/ui"; -import { BooleanHolder, toReadableString, randInt, getLocalizedSpriteKey } from "#app/utils"; +import { UiMode } from "#enums/ui-mode"; +import { BooleanHolder, toReadableString, randInt, getLocalizedSpriteKey } from "#app/utils/common"; import { PokemonFormChangeItemModifier, PokemonHeldItemModifier, @@ -252,7 +252,7 @@ export default class PartyUiHandler extends MessageUiHandler { ]; constructor() { - super(Mode.PARTY); + super(UiMode.PARTY); } setup() { @@ -556,7 +556,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.showText(filterResult as string, undefined, () => this.showText("", 0), undefined, true); } else if (option === PartyOption.SUMMARY) { ui.playSelect(); - ui.setModeWithoutClear(Mode.SUMMARY, pokemon).then(() => this.clearOptions()); + ui.setModeWithoutClear(UiMode.SUMMARY, pokemon).then(() => this.clearOptions()); return true; } else if (option === PartyOption.POKEDEX) { ui.playSelect(); @@ -566,7 +566,7 @@ export default class PartyUiHandler extends MessageUiHandler { form: pokemon.formIndex, female: pokemon.gender === Gender.FEMALE, }; - ui.setOverlayMode(Mode.POKEDEX_PAGE, pokemon.species, attributes).then(() => this.clearOptions()); + ui.setOverlayMode(UiMode.POKEDEX_PAGE, pokemon.species, attributes).then(() => this.clearOptions()); return true; } else if (option === PartyOption.UNPAUSE_EVOLUTION) { this.clearOptions(); @@ -593,13 +593,13 @@ export default class PartyUiHandler extends MessageUiHandler { null, () => { ui.setModeWithoutClear( - Mode.CONFIRM, + UiMode.CONFIRM, () => { const fusionName = pokemon.getName(); pokemon.unfuse().then(() => { this.clearPartySlots(); this.populatePartySlots(); - ui.setMode(Mode.PARTY); + ui.setMode(UiMode.PARTY); this.showText( i18next.t("partyUiHandler:wasReverted", { fusionName: fusionName, @@ -607,7 +607,7 @@ export default class PartyUiHandler extends MessageUiHandler { }), undefined, () => { - ui.setMode(Mode.PARTY); + ui.setMode(UiMode.PARTY); this.showText("", 0); }, null, @@ -616,7 +616,7 @@ export default class PartyUiHandler extends MessageUiHandler { }); }, () => { - ui.setMode(Mode.PARTY); + ui.setMode(UiMode.PARTY); this.showText("", 0); }, ); @@ -635,13 +635,13 @@ export default class PartyUiHandler extends MessageUiHandler { () => { this.blockInput = false; ui.setModeWithoutClear( - Mode.CONFIRM, + UiMode.CONFIRM, () => { - ui.setMode(Mode.PARTY); + ui.setMode(UiMode.PARTY); this.doRelease(this.cursor); }, () => { - ui.setMode(Mode.PARTY); + ui.setMode(UiMode.PARTY); this.showText("", 0); }, ); @@ -655,7 +655,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.clearOptions(); ui.playSelect(); ui.setModeWithoutClear( - Mode.RENAME_POKEMON, + UiMode.RENAME_POKEMON, { buttonActions: [ (nickname: string) => { @@ -664,10 +664,10 @@ export default class PartyUiHandler extends MessageUiHandler { pokemon.updateInfo(); this.clearPartySlots(); this.populatePartySlots(); - ui.setMode(Mode.PARTY); + ui.setMode(UiMode.PARTY); }, () => { - ui.setMode(Mode.PARTY); + ui.setMode(UiMode.PARTY); }, ], }, @@ -788,7 +788,7 @@ export default class PartyUiHandler extends MessageUiHandler { selectCallback(6, PartyOption.CANCEL); ui.playSelect(); } else { - ui.setMode(Mode.COMMAND, this.fieldIndex); + ui.setMode(UiMode.COMMAND, this.fieldIndex); ui.playSelect(); } } diff --git a/src/ui/pokedex-info-overlay.ts b/src/ui/pokedex-info-overlay.ts index 43e9bbc1a65..2e889f6d2a9 100644 --- a/src/ui/pokedex-info-overlay.ts +++ b/src/ui/pokedex-info-overlay.ts @@ -1,7 +1,7 @@ import type { InfoToggle } from "../battle-scene"; import { TextStyle, addTextObject } from "./text"; import { addWindow } from "./ui-theme"; -import { fixedInt } from "#app/utils"; +import { fixedInt } from "#app/utils/common"; import i18next from "i18next"; import { globalScene } from "#app/global-scene"; diff --git a/src/ui/pokedex-mon-container.ts b/src/ui/pokedex-mon-container.ts index 410effda40d..da79320850d 100644 --- a/src/ui/pokedex-mon-container.ts +++ b/src/ui/pokedex-mon-container.ts @@ -1,6 +1,6 @@ import type { Variant } from "#app/sprites/variant"; import { globalScene } from "#app/global-scene"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; import type PokemonSpecies from "../data/pokemon-species"; import { addTextObject, TextStyle } from "./text"; diff --git a/src/ui/pokedex-page-ui-handler.ts b/src/ui/pokedex-page-ui-handler.ts index 3f8959c6219..d0b85544494 100644 --- a/src/ui/pokedex-page-ui-handler.ts +++ b/src/ui/pokedex-page-ui-handler.ts @@ -4,7 +4,7 @@ import type { Variant } from "#app/sprites/variant"; import { getVariantTint, getVariantIcon } from "#app/sprites/variant"; import { argbFromRgba } from "@material/material-color-utilities"; import i18next from "i18next"; -import { starterColors } from "#app/battle-scene"; +import { starterColors } from "#app/global-vars/starter-colors"; import { allAbilities } from "#app/data/data-lists"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { GrowthRate, getGrowthRateColor } from "#app/data/exp"; @@ -26,7 +26,7 @@ import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler" import MessageUiHandler from "#app/ui/message-ui-handler"; import { StatsContainer } from "#app/ui/stats-container"; import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor, getTextStyleOptions } from "#app/ui/text"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { addWindow } from "#app/ui/ui-theme"; import { Egg } from "#app/data/egg"; import Overrides from "#app/overrides"; @@ -52,9 +52,9 @@ import { padInt, rgbHexToRgba, toReadableString, -} from "#app/utils"; +} from "#app/utils/common"; import type { Nature } from "#enums/nature"; -import { getEnumKeys } from "#app/utils"; +import { getEnumKeys } from "#app/utils/common"; import { speciesTmMoves } from "#app/data/balance/tms"; import type { BiomeTierTod } from "#app/data/balance/biomes"; import { BiomePoolTier, catchableSpecies } from "#app/data/balance/biomes"; @@ -265,7 +265,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { private exitCallback; constructor() { - super(Mode.POKEDEX_PAGE); + super(UiMode.POKEDEX_PAGE); } setup() { @@ -1140,12 +1140,12 @@ export default class PokedexPageUiHandler extends MessageUiHandler { success = true; } else if (this.previousSpecies.length > 0) { this.blockInput = true; - ui.setModeWithoutClear(Mode.OPTION_SELECT).then(() => { + ui.setModeWithoutClear(UiMode.OPTION_SELECT).then(() => { const species = this.previousSpecies.pop(); const starterAttributes = this.previousStarterAttributes.pop(); this.moveInfoOverlay.clear(); this.clearText(); - ui.setModeForceTransition(Mode.POKEDEX_PAGE, species, starterAttributes); + ui.setModeForceTransition(UiMode.POKEDEX_PAGE, species, starterAttributes); success = true; }); this.blockInput = false; @@ -1173,7 +1173,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { } else { this.blockInput = true; - ui.setMode(Mode.POKEDEX_PAGE, "refresh").then(() => { + ui.setMode(UiMode.POKEDEX_PAGE, "refresh").then(() => { ui.showText(i18next.t("pokedexUiHandler:showBaseStats"), null, () => { this.baseStatsOverlay.show(this.baseStats, this.baseTotal); @@ -1193,11 +1193,11 @@ export default class PokedexPageUiHandler extends MessageUiHandler { } else { this.blockInput = true; - ui.setMode(Mode.POKEDEX_PAGE, "refresh").then(() => { + ui.setMode(UiMode.POKEDEX_PAGE, "refresh").then(() => { ui.showText(i18next.t("pokedexUiHandler:showLevelMoves"), null, () => { this.moveInfoOverlay.show(allMoves[this.levelMoves[0][1]]); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { + ui.setModeWithoutClear(UiMode.OPTION_SELECT, { options: this.levelMoves .map(m => { const levelNumber = m[0] > 0 ? String(m[0]) : ""; @@ -1226,7 +1226,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { handler: () => { this.moveInfoOverlay.clear(); this.clearText(); - ui.setMode(Mode.POKEDEX_PAGE, "refresh"); + ui.setMode(UiMode.POKEDEX_PAGE, "refresh"); return true; }, onHover: () => { @@ -1251,7 +1251,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { } else { this.blockInput = true; - ui.setMode(Mode.POKEDEX_PAGE, "refresh").then(() => { + ui.setMode(UiMode.POKEDEX_PAGE, "refresh").then(() => { if (this.eggMoves.length === 0) { ui.showText(i18next.t("pokedexUiHandler:noEggMoves")); this.blockInput = false; @@ -1261,7 +1261,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { ui.showText(i18next.t("pokedexUiHandler:showEggMoves"), null, () => { this.moveInfoOverlay.show(allMoves[this.eggMoves[0]]); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { + ui.setModeWithoutClear(UiMode.OPTION_SELECT, { options: [ { label: i18next.t("pokedexUiHandler:common"), @@ -1294,7 +1294,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { handler: () => { this.moveInfoOverlay.clear(); this.clearText(); - ui.setMode(Mode.POKEDEX_PAGE, "refresh"); + ui.setMode(UiMode.POKEDEX_PAGE, "refresh"); return true; }, onHover: () => this.moveInfoOverlay.clear(), @@ -1321,11 +1321,11 @@ export default class PokedexPageUiHandler extends MessageUiHandler { } else { this.blockInput = true; - ui.setMode(Mode.POKEDEX_PAGE, "refresh").then(() => { + ui.setMode(UiMode.POKEDEX_PAGE, "refresh").then(() => { ui.showText(i18next.t("pokedexUiHandler:showTmMoves"), null, () => { this.moveInfoOverlay.show(allMoves[this.tmMoves[0]]); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { + ui.setModeWithoutClear(UiMode.OPTION_SELECT, { options: this.tmMoves .map(m => { const option: OptionSelectItem = { @@ -1344,7 +1344,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { handler: () => { this.moveInfoOverlay.clear(); this.clearText(); - ui.setMode(Mode.POKEDEX_PAGE, "refresh"); + ui.setMode(UiMode.POKEDEX_PAGE, "refresh"); return true; }, onHover: () => { @@ -1369,7 +1369,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { } else { this.blockInput = true; - ui.setMode(Mode.POKEDEX_PAGE, "refresh").then(() => { + ui.setMode(UiMode.POKEDEX_PAGE, "refresh").then(() => { ui.showText(i18next.t("pokedexUiHandler:showAbilities"), null, () => { this.infoOverlay.show(allAbilities[this.ability1].description); @@ -1431,13 +1431,13 @@ export default class PokedexPageUiHandler extends MessageUiHandler { handler: () => { this.infoOverlay.clear(); this.clearText(); - ui.setMode(Mode.POKEDEX_PAGE, "refresh"); + ui.setMode(UiMode.POKEDEX_PAGE, "refresh"); return true; }, onHover: () => this.infoOverlay.clear(), }); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { + ui.setModeWithoutClear(UiMode.OPTION_SELECT, { options: options, supportHover: true, maxOptions: 8, @@ -1457,7 +1457,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { } else { this.blockInput = true; - ui.setMode(Mode.POKEDEX_PAGE, "refresh").then(() => { + ui.setMode(UiMode.POKEDEX_PAGE, "refresh").then(() => { if ((!this.biomes || this.biomes?.length === 0) && (!this.preBiomes || this.preBiomes?.length === 0)) { ui.showText(i18next.t("pokedexUiHandler:noBiomes")); ui.playError(); @@ -1510,13 +1510,13 @@ export default class PokedexPageUiHandler extends MessageUiHandler { handler: () => { this.moveInfoOverlay.clear(); this.clearText(); - ui.setMode(Mode.POKEDEX_PAGE, "refresh"); + ui.setMode(UiMode.POKEDEX_PAGE, "refresh"); return true; }, onHover: () => this.moveInfoOverlay.clear(), }); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { + ui.setModeWithoutClear(UiMode.OPTION_SELECT, { options: options, supportHover: true, maxOptions: 8, @@ -1536,7 +1536,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { } else { this.blockInput = true; - ui.setMode(Mode.POKEDEX_PAGE, "refresh").then(() => { + ui.setMode(UiMode.POKEDEX_PAGE, "refresh").then(() => { const options: any[] = []; if ( @@ -1589,7 +1589,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.savedStarterAttributes.form = newFormIndex; this.moveInfoOverlay.clear(); this.clearText(); - ui.setMode(Mode.POKEDEX_PAGE, newSpecies, this.savedStarterAttributes); + ui.setMode(UiMode.POKEDEX_PAGE, newSpecies, this.savedStarterAttributes); return true; }, onHover: () => this.showText(conditionText), @@ -1631,7 +1631,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.savedStarterAttributes.form = newFormIndex; this.moveInfoOverlay.clear(); this.clearText(); - ui.setMode(Mode.POKEDEX_PAGE, evoSpecies, this.savedStarterAttributes); + ui.setMode(UiMode.POKEDEX_PAGE, evoSpecies, this.savedStarterAttributes); return true; }, onHover: () => this.showText(conditionText), @@ -1676,7 +1676,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.moveInfoOverlay.clear(); this.clearText(); ui.setMode( - Mode.POKEDEX_PAGE, + UiMode.POKEDEX_PAGE, newSpecies, this.savedStarterAttributes, this.filteredIndices, @@ -1694,13 +1694,13 @@ export default class PokedexPageUiHandler extends MessageUiHandler { handler: () => { this.moveInfoOverlay.clear(); this.clearText(); - ui.setMode(Mode.POKEDEX_PAGE, "refresh"); + ui.setMode(UiMode.POKEDEX_PAGE, "refresh"); return true; }, onHover: () => this.moveInfoOverlay.clear(), }); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { + ui.setModeWithoutClear(UiMode.OPTION_SELECT, { options: options, supportHover: true, maxOptions: 8, @@ -1719,7 +1719,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { error = true; } else { this.toggleStatsMode(); - ui.setMode(Mode.POKEDEX_PAGE, "refresh"); + ui.setMode(UiMode.POKEDEX_PAGE, "refresh"); success = true; } break; @@ -1729,10 +1729,10 @@ export default class PokedexPageUiHandler extends MessageUiHandler { error = true; } else { this.blockInput = true; - ui.setMode(Mode.POKEDEX_PAGE, "refresh").then(() => { + ui.setMode(UiMode.POKEDEX_PAGE, "refresh").then(() => { ui.showText(i18next.t("pokedexUiHandler:showNature"), null, () => { const natures = globalScene.gameData.getNaturesForAttr(this.speciesStarterDexEntry?.natureAttr); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { + ui.setModeWithoutClear(UiMode.OPTION_SELECT, { options: natures .map((n: Nature, _i: number) => { const option: OptionSelectItem = { @@ -1747,7 +1747,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { label: i18next.t("menu:cancel"), handler: () => { this.clearText(); - ui.setMode(Mode.POKEDEX_PAGE, "refresh"); + ui.setMode(UiMode.POKEDEX_PAGE, "refresh"); this.blockInput = false; return true; }, @@ -1897,7 +1897,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { }); this.setSpeciesDetails(this.species); globalScene.playSound("se/buy"); - ui.setMode(Mode.POKEDEX_PAGE, "refresh"); + ui.setMode(UiMode.POKEDEX_PAGE, "refresh"); return true; } @@ -1927,7 +1927,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { return globalScene.reset(true); } }); - ui.setMode(Mode.POKEDEX_PAGE, "refresh"); + ui.setMode(UiMode.POKEDEX_PAGE, "refresh"); globalScene.playSound("se/buy"); return true; @@ -1976,7 +1976,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { return globalScene.reset(true); } }); - ui.setMode(Mode.POKEDEX_PAGE, "refresh"); + ui.setMode(UiMode.POKEDEX_PAGE, "refresh"); globalScene.playSound("se/buy"); return true; @@ -1990,11 +1990,11 @@ export default class PokedexPageUiHandler extends MessageUiHandler { options.push({ label: i18next.t("menu:cancel"), handler: () => { - ui.setMode(Mode.POKEDEX_PAGE, "refresh"); + ui.setMode(UiMode.POKEDEX_PAGE, "refresh"); return true; }, }); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { + ui.setModeWithoutClear(UiMode.OPTION_SELECT, { options: options, yOffset: 47, }); @@ -2032,7 +2032,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { return true; } this.blockInput = true; - ui.setModeWithoutClear(Mode.OPTION_SELECT).then(() => { + ui.setModeWithoutClear(UiMode.OPTION_SELECT).then(() => { // Always go back to first selection after scrolling around if (this.previousSpecies.length === 0) { this.previousSpecies.push(this.species); @@ -2057,7 +2057,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.moveInfoOverlay.clear(); this.clearText(); ui.setModeForceTransition( - Mode.POKEDEX_PAGE, + UiMode.POKEDEX_PAGE, newSpecies, this.savedStarterAttributes, this.filteredIndices, @@ -2071,7 +2071,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.blockInput = false; return true; } - ui.setModeWithoutClear(Mode.OPTION_SELECT).then(() => { + ui.setModeWithoutClear(UiMode.OPTION_SELECT).then(() => { // Always go back to first selection after scrolling around if (this.previousSpecies.length === 0) { this.previousSpecies.push(this.species); @@ -2096,7 +2096,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler { this.moveInfoOverlay.clear(); this.clearText(); ui.setModeForceTransition( - Mode.POKEDEX_PAGE, + UiMode.POKEDEX_PAGE, newSpecies, this.savedStarterAttributes, this.filteredIndices, diff --git a/src/ui/pokedex-scan-ui-handler.ts b/src/ui/pokedex-scan-ui-handler.ts index 171040f6f12..45092d461a3 100644 --- a/src/ui/pokedex-scan-ui-handler.ts +++ b/src/ui/pokedex-scan-ui-handler.ts @@ -3,8 +3,8 @@ import { FormModalUiHandler } from "./form-modal-ui-handler"; import type { ModalConfig } from "./modal-ui-handler"; import type { PlayerPokemon } from "#app/field/pokemon"; import type { OptionSelectItem } from "./abstact-option-select-ui-handler"; -import { isNullOrUndefined } from "#app/utils"; -import { Mode } from "./ui"; +import { isNullOrUndefined } from "#app/utils/common"; +import { UiMode } from "#enums/ui-mode"; import { FilterTextRow } from "./filter-text"; import { allAbilities } from "#app/data/data-lists"; import { allMoves } from "#app/data/moves/move"; @@ -115,7 +115,7 @@ export default class PokedexScanUiHandler extends FormModalUiHandler { input.on("keydown", (inputObject, evt: KeyboardEvent) => { if ( ["escape", "space"].some(v => v === evt.key.toLowerCase() || v === evt.code.toLowerCase()) && - ui.getMode() === Mode.AUTO_COMPLETE + ui.getMode() === UiMode.AUTO_COMPLETE ) { // Delete autocomplete list and recovery focus. inputObject.on("blur", () => inputObject.node.focus(), { once: true }); @@ -125,7 +125,7 @@ export default class PokedexScanUiHandler extends FormModalUiHandler { input.on("textchange", (inputObject, evt: InputEvent) => { // Delete autocomplete. - if (ui.getMode() === Mode.AUTO_COMPLETE) { + if (ui.getMode() === UiMode.AUTO_COMPLETE) { ui.revertMode(); } @@ -154,7 +154,7 @@ export default class PokedexScanUiHandler extends FormModalUiHandler { maxOptions: 5, modalContainer: this.modalContainer, }; - ui.setOverlayMode(Mode.AUTO_COMPLETE, modalOpts); + ui.setOverlayMode(UiMode.AUTO_COMPLETE, modalOpts); } }); @@ -168,7 +168,7 @@ export default class PokedexScanUiHandler extends FormModalUiHandler { this.inputs[0].text = args[1]; } this.submitAction = _ => { - if (ui.getMode() === Mode.POKEDEX_SCAN) { + if (ui.getMode() === UiMode.POKEDEX_SCAN) { this.sanitizeInputs(); const outputName = this.reducedKeys.includes(this.inputs[0].text) ? this.inputs[0].text : ""; const sanitizedName = btoa(unescape(encodeURIComponent(outputName))); diff --git a/src/ui/pokedex-ui-handler.ts b/src/ui/pokedex-ui-handler.ts index 5fd3ca3e379..e9726031bf5 100644 --- a/src/ui/pokedex-ui-handler.ts +++ b/src/ui/pokedex-ui-handler.ts @@ -2,7 +2,7 @@ import type { Variant } from "#app/sprites/variant"; import { getVariantTint, getVariantIcon } from "#app/sprites/variant"; import { argbFromRgba } from "@material/material-color-utilities"; import i18next from "i18next"; -import { starterColors } from "#app/battle-scene"; +import { starterColors } from "#app/global-vars/starter-colors"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves"; import type { PokemonForm } from "#app/data/pokemon-species"; @@ -16,7 +16,7 @@ import { AbilityAttr, DexAttr, loadStarterPreferences } from "#app/system/game-d import MessageUiHandler from "#app/ui/message-ui-handler"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "#app/ui/pokemon-icon-anim-handler"; import { TextStyle, addTextObject } from "#app/ui/text"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; import { Passive as PassiveAttr } from "#enums/passive"; import type { Species } from "#enums/species"; @@ -31,7 +31,7 @@ import { getValueReductionCandyCounts, getSameSpeciesEggCandyCounts, } from "#app/data/balance/starters"; -import { BooleanHolder, fixedInt, getLocalizedSpriteKey, padInt, randIntRange, rgbHexToRgba } from "#app/utils"; +import { BooleanHolder, fixedInt, getLocalizedSpriteKey, padInt, randIntRange, rgbHexToRgba } from "#app/utils/common"; import type { Nature } from "#enums/nature"; import { addWindow } from "./ui-theme"; import type { OptionSelectConfig } from "./abstact-option-select-ui-handler"; @@ -231,7 +231,7 @@ export default class PokedexUiHandler extends MessageUiHandler { private filteredIndices: Species[]; constructor() { - super(Mode.POKEDEX); + super(UiMode.POKEDEX); } setup() { @@ -1133,7 +1133,7 @@ export default class PokedexUiHandler extends MessageUiHandler { } else if (this.showingTray) { if (button === Button.ACTION) { const formIndex = this.trayForms[this.trayCursor].formIndex; - ui.setOverlayMode(Mode.POKEDEX_PAGE, this.lastSpecies, { form: formIndex }, this.filteredIndices); + ui.setOverlayMode(UiMode.POKEDEX_PAGE, this.lastSpecies, { form: formIndex }, this.filteredIndices); success = true; } else { const numberOfForms = this.trayContainers.length; @@ -1182,7 +1182,7 @@ export default class PokedexUiHandler extends MessageUiHandler { } } else { if (button === Button.ACTION) { - ui.setOverlayMode(Mode.POKEDEX_PAGE, this.lastSpecies, null, this.filteredIndices); + ui.setOverlayMode(UiMode.POKEDEX_PAGE, this.lastSpecies, null, this.filteredIndices); success = true; } else { switch (button) { @@ -2268,15 +2268,15 @@ export default class PokedexUiHandler extends MessageUiHandler { const ui = this.getUi(); const cancel = () => { - ui.setMode(Mode.POKEDEX, "refresh"); + ui.setMode(UiMode.POKEDEX, "refresh"); this.clearText(); this.blockInput = false; }; ui.showText(i18next.t("pokedexUiHandler:confirmExit"), null, () => { ui.setModeWithoutClear( - Mode.CONFIRM, + UiMode.CONFIRM, () => { - ui.setMode(Mode.POKEDEX, "refresh"); + ui.setMode(UiMode.POKEDEX, "refresh"); this.clearText(); this.clear(); ui.revertMode(); diff --git a/src/ui/pokemon-hatch-info-container.ts b/src/ui/pokemon-hatch-info-container.ts index 692f0f1d374..f3095cb48bf 100644 --- a/src/ui/pokemon-hatch-info-container.ts +++ b/src/ui/pokemon-hatch-info-container.ts @@ -1,13 +1,13 @@ import PokemonInfoContainer from "#app/ui/pokemon-info-container"; import { Gender } from "#app/data/gender"; import { PokemonType } from "#enums/pokemon-type"; -import { rgbHexToRgba, padInt } from "#app/utils"; +import { rgbHexToRgba, padInt } from "#app/utils/common"; import { TextStyle, addTextObject } from "#app/ui/text"; import { speciesEggMoves } from "#app/data/balance/egg-moves"; import { allMoves } from "#app/data/moves/move"; import { Species } from "#enums/species"; import { getEggTierForSpecies } from "#app/data/egg"; -import { starterColors } from "#app/battle-scene"; +import { starterColors } from "#app/global-vars/starter-colors"; import { globalScene } from "#app/global-scene"; import { argbFromRgba } from "@material/material-color-utilities"; import type { EggHatchData } from "#app/data/egg-hatch-data"; diff --git a/src/ui/pokemon-icon-anim-handler.ts b/src/ui/pokemon-icon-anim-handler.ts index b6944c0fd84..253ccbe3623 100644 --- a/src/ui/pokemon-icon-anim-handler.ts +++ b/src/ui/pokemon-icon-anim-handler.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { fixedInt } from "#app/utils"; +import { fixedInt } from "#app/utils/common"; export enum PokemonIconAnimMode { NONE, diff --git a/src/ui/pokemon-info-container.ts b/src/ui/pokemon-info-container.ts index 0ccece46ab9..18b5d2384ef 100644 --- a/src/ui/pokemon-info-container.ts +++ b/src/ui/pokemon-info-container.ts @@ -8,7 +8,7 @@ import type Pokemon from "../field/pokemon"; import i18next from "i18next"; import type { DexEntry, StarterDataEntry } from "../system/game-data"; import { DexAttr } from "../system/game-data"; -import { fixedInt } from "#app/utils"; +import { fixedInt } from "#app/utils/common"; import ConfirmUiHandler from "./confirm-ui-handler"; import { StatsContainer } from "./stats-container"; import { TextStyle, addBBCodeTextObject, addTextObject, getTextColor } from "./text"; diff --git a/src/ui/registration-form-ui-handler.ts b/src/ui/registration-form-ui-handler.ts index 74669bc1f44..bb10efc5869 100644 --- a/src/ui/registration-form-ui-handler.ts +++ b/src/ui/registration-form-ui-handler.ts @@ -1,7 +1,7 @@ import type { InputFieldConfig } from "./form-modal-ui-handler"; import { FormModalUiHandler } from "./form-modal-ui-handler"; import type { ModalConfig } from "./modal-ui-handler"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import { TextStyle, addTextObject } from "./text"; import i18next from "i18next"; import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; @@ -101,9 +101,9 @@ export default class RegistrationFormUiHandler extends FormModalUiHandler { // Prevent overlapping overrides on action modification this.submitAction = originalRegistrationAction; this.sanitizeInputs(); - globalScene.ui.setMode(Mode.LOADING, { buttonActions: [] }); + globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); const onFail = error => { - globalScene.ui.setMode(Mode.REGISTRATION_FORM, Object.assign(config, { errorMessage: error?.trim() })); + globalScene.ui.setMode(UiMode.REGISTRATION_FORM, Object.assign(config, { errorMessage: error?.trim() })); globalScene.ui.playError(); const errorMessageFontSize = languageSettings[i18next.resolvedLanguage!]?.errorMessageFontSize; if (errorMessageFontSize) { diff --git a/src/ui/run-history-ui-handler.ts b/src/ui/run-history-ui-handler.ts index 16aad7b8634..92c5a2fde07 100644 --- a/src/ui/run-history-ui-handler.ts +++ b/src/ui/run-history-ui-handler.ts @@ -1,9 +1,9 @@ import { globalScene } from "#app/global-scene"; import { GameModes } from "../game-mode"; import { TextStyle, addTextObject } from "./text"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import { addWindow } from "./ui-theme"; -import { fixedInt, formatLargeNumber } from "#app/utils"; +import { fixedInt, formatLargeNumber } from "#app/utils/common"; import type PokemonData from "../system/pokemon-data"; import MessageUiHandler from "./message-ui-handler"; import i18next from "i18next"; @@ -40,7 +40,7 @@ export default class RunHistoryUiHandler extends MessageUiHandler { private runContainerInitialY: number; constructor() { - super(Mode.RUN_HISTORY); + super(UiMode.RUN_HISTORY); } override setup() { @@ -110,7 +110,7 @@ export default class RunHistoryUiHandler extends MessageUiHandler { if (button === Button.ACTION) { const cursor = this.cursor + this.scrollCursor; if (this.runs[cursor]) { - globalScene.ui.setOverlayMode(Mode.RUN_INFO, this.runs[cursor].entryData, RunDisplayMode.RUN_HISTORY, true); + globalScene.ui.setOverlayMode(UiMode.RUN_INFO, this.runs[cursor].entryData, RunDisplayMode.RUN_HISTORY, true); } else { return false; } diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index 60667035147..8487533f465 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -2,10 +2,10 @@ import { GameModes } from "../game-mode"; import UiHandler from "./ui-handler"; import type { SessionSaveData } from "../system/game-data"; import { TextStyle, addTextObject, addBBCodeTextObject, getTextColor } from "./text"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import { addWindow } from "./ui-theme"; import { getPokeballAtlasKey } from "#app/data/pokeball"; -import { formatLargeNumber, getPlayTimeString, formatMoney, formatFancyLargeNumber } from "#app/utils"; +import { formatLargeNumber, getPlayTimeString, formatMoney, formatFancyLargeNumber } from "#app/utils/common"; import type PokemonData from "../system/pokemon-data"; import i18next from "i18next"; import { Button } from "../enums/buttons"; @@ -69,7 +69,7 @@ export default class RunInfoUiHandler extends UiHandler { private modifiersModule: any; constructor() { - super(Mode.RUN_INFO); + super(UiMode.RUN_INFO); } override async setup() { diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index 0c16e41bbef..7b4d46203c9 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -6,10 +6,10 @@ import { GameMode } from "../game-mode"; import * as Modifier from "#app/modifier/modifier"; import type { SessionSaveData } from "../system/game-data"; import type PokemonData from "../system/pokemon-data"; -import { isNullOrUndefined, fixedInt, getPlayTimeString, formatLargeNumber } from "#app/utils"; +import { isNullOrUndefined, fixedInt, getPlayTimeString, formatLargeNumber } from "#app/utils/common"; import MessageUiHandler from "./message-ui-handler"; import { TextStyle, addTextObject } from "./text"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import { addWindow } from "./ui-theme"; import { RunDisplayMode } from "#app/ui/run-info-ui-handler"; @@ -40,7 +40,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { private sessionSlotsContainerInitialY: number; constructor() { - super(Mode.SAVE_SLOT); + super(UiMode.SAVE_SLOT); } setup() { @@ -122,13 +122,13 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { this.saveSlotSelectCallback = null; ui.revertMode(); ui.showText("", 0); - ui.setMode(Mode.MESSAGE); + ui.setMode(UiMode.MESSAGE); originalCallback?.(cursor); }; if (this.sessionSlots[cursor].hasData) { ui.showText(i18next.t("saveSlotSelectUiHandler:overwriteData"), null, () => { ui.setOverlayMode( - Mode.CONFIRM, + UiMode.CONFIRM, () => { globalScene.gameData.deleteSession(cursor).then(response => { if (response === false) { @@ -198,7 +198,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { case Button.RIGHT: if (this.sessionSlots[cursorPosition].hasData && this.sessionSlots[cursorPosition].saveData) { globalScene.ui.setOverlayMode( - Mode.RUN_INFO, + UiMode.RUN_INFO, this.sessionSlots[cursorPosition].saveData, RunDisplayMode.SESSION_PREVIEW, ); diff --git a/src/ui/saving-icon-handler.ts b/src/ui/saving-icon-handler.ts index 3db84f128a1..3b7db549a4a 100644 --- a/src/ui/saving-icon-handler.ts +++ b/src/ui/saving-icon-handler.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { fixedInt } from "#app/utils"; +import { fixedInt } from "#app/utils/common"; export default class SavingIconHandler extends Phaser.GameObjects.Container { private icon: Phaser.GameObjects.Sprite; diff --git a/src/ui/session-reload-modal-ui-handler.ts b/src/ui/session-reload-modal-ui-handler.ts index d3b88da9e63..f866783afe8 100644 --- a/src/ui/session-reload-modal-ui-handler.ts +++ b/src/ui/session-reload-modal-ui-handler.ts @@ -1,10 +1,10 @@ import type { ModalConfig } from "./modal-ui-handler"; import { ModalUiHandler } from "./modal-ui-handler"; import { addTextObject, TextStyle } from "./text"; -import type { Mode } from "./ui"; +import type { UiMode } from "#enums/ui-mode"; export default class SessionReloadModalUiHandler extends ModalUiHandler { - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); } diff --git a/src/ui/settings/abstract-binding-ui-handler.ts b/src/ui/settings/abstract-binding-ui-handler.ts index 62f78da89f5..a4707418b7c 100644 --- a/src/ui/settings/abstract-binding-ui-handler.ts +++ b/src/ui/settings/abstract-binding-ui-handler.ts @@ -1,5 +1,5 @@ import UiHandler from "../ui-handler"; -import type { Mode } from "../ui"; +import type { UiMode } from "#enums/ui-mode"; import { addWindow } from "../ui-theme"; import { addTextObject, TextStyle } from "../text"; import { Button } from "#enums/buttons"; @@ -51,7 +51,7 @@ export default abstract class AbstractBindingUiHandler extends UiHandler { * * @param mode - The UI mode. */ - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); } diff --git a/src/ui/settings/abstract-control-settings-ui-handler.ts b/src/ui/settings/abstract-control-settings-ui-handler.ts index 2c634e2c5bf..495a0f68540 100644 --- a/src/ui/settings/abstract-control-settings-ui-handler.ts +++ b/src/ui/settings/abstract-control-settings-ui-handler.ts @@ -1,5 +1,5 @@ import UiHandler from "#app/ui/ui-handler"; -import type { Mode } from "#app/ui/ui"; +import type { UiMode } from "#enums/ui-mode"; import type { InterfaceConfig } from "#app/inputs-controller"; import { addWindow } from "#app/ui/ui-theme"; import { addTextObject, TextStyle } from "#app/ui/text"; @@ -74,7 +74,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler * * @param mode - The UI mode. */ - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); this.rowsToDisplay = 8; } diff --git a/src/ui/settings/abstract-settings-ui-handler.ts b/src/ui/settings/abstract-settings-ui-handler.ts index 0c14b91251e..27ca95c25ac 100644 --- a/src/ui/settings/abstract-settings-ui-handler.ts +++ b/src/ui/settings/abstract-settings-ui-handler.ts @@ -1,5 +1,5 @@ import { TextStyle, addTextObject } from "#app/ui/text"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import MessageUiHandler from "#app/ui/message-ui-handler"; import { addWindow } from "#app/ui/ui-theme"; import { ScrollBar } from "#app/ui/scroll-bar"; @@ -42,7 +42,7 @@ export default class AbstractSettingsUiHandler extends MessageUiHandler { protected settings: Array; protected localStorageKey: string; - constructor(type: SettingType, mode: Mode | null = null) { + constructor(type: SettingType, mode: UiMode | null = null) { super(mode); this.settings = Setting.filter(s => s.type === type && !s?.isHidden?.()); this.reloadRequired = false; @@ -425,7 +425,7 @@ export default class AbstractSettingsUiHandler extends MessageUiHandler { const confirmationMessage = setting.options[cursor].confirmationMessage ?? i18next.t("settings:defaultConfirmMessage"); globalScene.ui.showText(confirmationMessage, null, () => { - globalScene.ui.setOverlayMode(Mode.CONFIRM, confirmUpdateSetting, cancelUpdateSetting, null, null, 1, 750); + globalScene.ui.setOverlayMode(UiMode.CONFIRM, confirmUpdateSetting, cancelUpdateSetting, null, null, 1, 750); }); } else { saveSetting(); diff --git a/src/ui/settings/gamepad-binding-ui-handler.ts b/src/ui/settings/gamepad-binding-ui-handler.ts index 62bc2db7825..0f226ddcafa 100644 --- a/src/ui/settings/gamepad-binding-ui-handler.ts +++ b/src/ui/settings/gamepad-binding-ui-handler.ts @@ -1,12 +1,12 @@ import AbstractBindingUiHandler from "./abstract-binding-ui-handler"; -import type { Mode } from "../ui"; +import type { UiMode } from "#enums/ui-mode"; 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"; export default class GamepadBindingUiHandler extends AbstractBindingUiHandler { - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); globalScene.input.gamepad?.on("down", this.gamepadButtonDown, this); } diff --git a/src/ui/settings/keyboard-binding-ui-handler.ts b/src/ui/settings/keyboard-binding-ui-handler.ts index 8735faeaaab..c05a31ca91e 100644 --- a/src/ui/settings/keyboard-binding-ui-handler.ts +++ b/src/ui/settings/keyboard-binding-ui-handler.ts @@ -1,12 +1,12 @@ import AbstractBindingUiHandler from "./abstract-binding-ui-handler"; -import type { Mode } from "../ui"; +import type { UiMode } from "#enums/ui-mode"; 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"; export default class KeyboardBindingUiHandler extends AbstractBindingUiHandler { - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); // Listen to gamepad button down events to initiate binding. globalScene.input.keyboard?.on("keydown", this.onKeyDown, this); diff --git a/src/ui/settings/navigationMenu.ts b/src/ui/settings/navigationMenu.ts index 1d2d71e1e20..ad3d4ccf0b5 100644 --- a/src/ui/settings/navigationMenu.ts +++ b/src/ui/settings/navigationMenu.ts @@ -1,5 +1,5 @@ import { globalScene } from "#app/global-scene"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import type { InputsIcons } from "#app/ui/settings/abstract-control-settings-ui-handler"; import { addTextObject, setTextStyle, TextStyle } from "#app/ui/text"; import { addWindow } from "#app/ui/ui-theme"; @@ -14,8 +14,8 @@ const RIGHT = "RIGHT"; */ export class NavigationManager { private static instance: NavigationManager; - public modes: Mode[]; - public selectedMode: Mode = Mode.SETTINGS; + public modes: UiMode[]; + public selectedMode: UiMode = UiMode.SETTINGS; public navigationMenus: NavigationMenu[] = new Array(); public labels: string[]; @@ -27,11 +27,11 @@ export class NavigationManager { */ constructor() { this.modes = [ - Mode.SETTINGS, - Mode.SETTINGS_DISPLAY, - Mode.SETTINGS_AUDIO, - Mode.SETTINGS_GAMEPAD, - Mode.SETTINGS_KEYBOARD, + UiMode.SETTINGS, + UiMode.SETTINGS_DISPLAY, + UiMode.SETTINGS_AUDIO, + UiMode.SETTINGS_GAMEPAD, + UiMode.SETTINGS_KEYBOARD, ]; this.labels = [ i18next.t("settings:general"), @@ -43,7 +43,7 @@ export class NavigationManager { } public reset() { - this.selectedMode = Mode.SETTINGS; + this.selectedMode = UiMode.SETTINGS; this.updateNavigationMenus(); } diff --git a/src/ui/settings/option-select-ui-handler.ts b/src/ui/settings/option-select-ui-handler.ts index b3d1735dc19..af9760814ac 100644 --- a/src/ui/settings/option-select-ui-handler.ts +++ b/src/ui/settings/option-select-ui-handler.ts @@ -1,8 +1,8 @@ import AbstractOptionSelectUiHandler from "../abstact-option-select-ui-handler"; -import { Mode } from "../ui"; +import { UiMode } from "#enums/ui-mode"; export default class OptionSelectUiHandler extends AbstractOptionSelectUiHandler { - constructor(mode: Mode = Mode.OPTION_SELECT) { + constructor(mode: UiMode = UiMode.OPTION_SELECT) { super(mode); } diff --git a/src/ui/settings/settings-audio-ui-handler.ts b/src/ui/settings/settings-audio-ui-handler.ts index f8cb4da4ba2..019d66d7428 100644 --- a/src/ui/settings/settings-audio-ui-handler.ts +++ b/src/ui/settings/settings-audio-ui-handler.ts @@ -1,4 +1,4 @@ -import type { Mode } from "../ui"; +import type { UiMode } from "#enums/ui-mode"; import AbstractSettingsUiHandler from "./abstract-settings-ui-handler"; import { SettingType } from "#app/system/settings/settings"; ("#app/inputs-controller"); @@ -9,7 +9,7 @@ export default class SettingsAudioUiHandler extends AbstractSettingsUiHandler { * * @param mode - The UI mode, optional. */ - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(SettingType.AUDIO, mode); this.title = "Audio"; this.localStorageKey = "settings"; diff --git a/src/ui/settings/settings-display-ui-handler.ts b/src/ui/settings/settings-display-ui-handler.ts index 985aa9adca2..4878bae72cb 100644 --- a/src/ui/settings/settings-display-ui-handler.ts +++ b/src/ui/settings/settings-display-ui-handler.ts @@ -1,4 +1,4 @@ -import type { Mode } from "../ui"; +import type { UiMode } from "#enums/ui-mode"; import AbstractSettingsUiHandler from "./abstract-settings-ui-handler"; import { SettingKeys, SettingType } from "#app/system/settings/settings"; ("#app/inputs-controller"); @@ -9,7 +9,7 @@ export default class SettingsDisplayUiHandler extends AbstractSettingsUiHandler * * @param mode - The UI mode, optional. */ - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(SettingType.DISPLAY, mode); this.title = "Display"; diff --git a/src/ui/settings/settings-gamepad-ui-handler.ts b/src/ui/settings/settings-gamepad-ui-handler.ts index 0b3a7241ff2..7d269deab14 100644 --- a/src/ui/settings/settings-gamepad-ui-handler.ts +++ b/src/ui/settings/settings-gamepad-ui-handler.ts @@ -1,5 +1,5 @@ import { addTextObject, TextStyle } from "../text"; -import type { Mode } from "../ui"; +import type { UiMode } from "#enums/ui-mode"; import { setSettingGamepad, SettingGamepad, @@ -13,7 +13,7 @@ import pad_unlicensedSNES from "#app/configs/inputs/pad_unlicensedSNES"; import type { InterfaceConfig } from "#app/inputs-controller"; import AbstractControlSettingsUiHandler from "#app/ui/settings/abstract-control-settings-ui-handler"; import { Device } from "#enums/devices"; -import { truncateString } from "#app/utils"; +import { truncateString } from "#app/utils/common"; import i18next from "i18next"; import { globalScene } from "#app/global-scene"; @@ -29,7 +29,7 @@ export default class SettingsGamepadUiHandler extends AbstractControlSettingsUiH * * @param mode - The UI mode, optional. */ - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); this.titleSelected = "Gamepad"; this.setting = SettingGamepad; diff --git a/src/ui/settings/settings-keyboard-ui-handler.ts b/src/ui/settings/settings-keyboard-ui-handler.ts index a7837c8961e..c334ee8f1fc 100644 --- a/src/ui/settings/settings-keyboard-ui-handler.ts +++ b/src/ui/settings/settings-keyboard-ui-handler.ts @@ -1,4 +1,4 @@ -import { Mode } from "../ui"; +import { UiMode } from "#enums/ui-mode"; import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty"; import { setSettingKeyboard, @@ -7,7 +7,7 @@ import { settingKeyboardDefaults, settingKeyboardOptions, } from "#app/system/settings/settings-keyboard"; -import { reverseValueToKeySetting, truncateString } from "#app/utils"; +import { reverseValueToKeySetting, truncateString } from "#app/utils/common"; import AbstractControlSettingsUiHandler from "#app/ui/settings/abstract-control-settings-ui-handler"; import type { InterfaceConfig } from "#app/inputs-controller"; import { addTextObject, TextStyle } from "#app/ui/text"; @@ -28,7 +28,7 @@ export default class SettingsKeyboardUiHandler extends AbstractControlSettingsUi * * @param mode - The UI mode, optional. */ - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); this.titleSelected = "Keyboard"; this.setting = SettingKeyboard; @@ -84,7 +84,7 @@ export default class SettingsKeyboardUiHandler extends AbstractControlSettingsUi * Handle the home key press event. */ onHomeDown(): void { - if (![Mode.SETTINGS_KEYBOARD, Mode.SETTINGS_GAMEPAD].includes(globalScene.ui.getMode())) { + if (![UiMode.SETTINGS_KEYBOARD, UiMode.SETTINGS_GAMEPAD].includes(globalScene.ui.getMode())) { return; } globalScene.gameData.resetMappingToFactory(); @@ -95,7 +95,7 @@ export default class SettingsKeyboardUiHandler extends AbstractControlSettingsUi * Handle the delete key press event. */ onDeleteDown(): void { - if (globalScene.ui.getMode() !== Mode.SETTINGS_KEYBOARD) { + if (globalScene.ui.getMode() !== UiMode.SETTINGS_KEYBOARD) { return; } const cursor = this.cursor + this.scrollCursor; // Calculate the absolute cursor position. diff --git a/src/ui/settings/settings-ui-handler.ts b/src/ui/settings/settings-ui-handler.ts index 22ea76d798b..8d61044ff91 100644 --- a/src/ui/settings/settings-ui-handler.ts +++ b/src/ui/settings/settings-ui-handler.ts @@ -1,5 +1,5 @@ import { SettingType } from "../../system/settings/settings"; -import type { Mode } from "../ui"; +import type { UiMode } from "#enums/ui-mode"; import AbstractSettingsUiHandler from "./abstract-settings-ui-handler"; export default class SettingsUiHandler extends AbstractSettingsUiHandler { @@ -8,7 +8,7 @@ export default class SettingsUiHandler extends AbstractSettingsUiHandler { * * @param mode - The UI mode, optional. */ - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(SettingType.GENERAL, mode); this.title = "General"; this.localStorageKey = "settings"; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 9b0009d666e..1902c691715 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -6,7 +6,7 @@ import { getVariantTint, getVariantIcon } from "#app/sprites/variant"; import { argbFromRgba } from "@material/material-color-utilities"; import i18next from "i18next"; import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; -import { starterColors } from "#app/battle-scene"; +import { starterColors } from "#app/global-vars/starter-colors"; import { globalScene } from "#app/global-scene"; import type { Ability } from "#app/data/abilities/ability-class"; import { allAbilities } from "#app/data/data-lists"; @@ -37,7 +37,7 @@ import MessageUiHandler from "#app/ui/message-ui-handler"; import PokemonIconAnimHandler, { PokemonIconAnimMode } from "#app/ui/pokemon-icon-anim-handler"; import { StatsContainer } from "#app/ui/stats-container"; import { TextStyle, addBBCodeTextObject, addTextObject } from "#app/ui/text"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { addWindow } from "#app/ui/ui-theme"; import { Egg } from "#app/data/egg"; import Overrides from "#app/overrides"; @@ -74,7 +74,7 @@ import { randIntRange, rgbHexToRgba, toReadableString, -} from "#app/utils"; +} from "#app/utils/common"; import type { Nature } from "#enums/nature"; import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; import { achvs } from "#app/system/achv"; @@ -375,7 +375,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { protected blockInput = false; constructor() { - super(Mode.STARTER_SELECT); + super(UiMode.STARTER_SELECT); } setup() { @@ -1888,7 +1888,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { { label: i18next.t("starterSelectUiHandler:addToParty"), handler: () => { - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); const isOverValueLimit = this.tryUpdateValue( globalScene.gameData.getSpeciesStarterValue(this.lastSpecies.speciesId), true, @@ -1921,7 +1921,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { label: i18next.t("starterSelectUiHandler:removeFromParty"), handler: () => { this.popStarter(removeIndex); - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); return true; }, }, @@ -1934,7 +1934,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { label: i18next.t("starterSelectUiHandler:toggleIVs"), handler: () => { this.toggleStatsMode(); - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); return true; }, }, @@ -1944,18 +1944,18 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const showSwapOptions = (moveset: StarterMoveset) => { this.blockInput = true; - ui.setMode(Mode.STARTER_SELECT).then(() => { + ui.setMode(UiMode.STARTER_SELECT).then(() => { ui.showText(i18next.t("starterSelectUiHandler:selectMoveSwapOut"), null, () => { this.moveInfoOverlay.show(allMoves[moveset[0]]); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { + ui.setModeWithoutClear(UiMode.OPTION_SELECT, { options: moveset .map((m: Moves, i: number) => { const option: OptionSelectItem = { label: allMoves[m].name, handler: () => { this.blockInput = true; - ui.setMode(Mode.STARTER_SELECT).then(() => { + ui.setMode(UiMode.STARTER_SELECT).then(() => { ui.showText( `${i18next.t("starterSelectUiHandler:selectMoveSwapWith")} ${allMoves[m].name}.`, null, @@ -1963,7 +1963,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const possibleMoves = this.speciesStarterMoves.filter((sm: Moves) => sm !== m); this.moveInfoOverlay.show(allMoves[possibleMoves[0]]); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { + ui.setModeWithoutClear(UiMode.OPTION_SELECT, { options: possibleMoves .map(sm => { // make an option for each available starter move @@ -2011,7 +2011,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { handler: () => { this.moveInfoOverlay.clear(); this.clearText(); - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); return true; }, onHover: () => { @@ -2039,10 +2039,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const showNatureOptions = () => { this.blockInput = true; - ui.setMode(Mode.STARTER_SELECT).then(() => { + ui.setMode(UiMode.STARTER_SELECT).then(() => { ui.showText(i18next.t("starterSelectUiHandler:selectNature"), null, () => { const natures = globalScene.gameData.getNaturesForAttr(this.speciesStarterDexEntry?.natureAttr); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { + ui.setModeWithoutClear(UiMode.OPTION_SELECT, { options: natures .map((n: Nature, _i: number) => { const option: OptionSelectItem = { @@ -2054,7 +2054,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } starterAttributes.nature = n; this.clearText(); - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); // set nature for starter this.setSpeciesDetails(this.lastSpecies, { natureIndex: n, @@ -2069,7 +2069,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { label: i18next.t("menu:cancel"), handler: () => { this.clearText(); - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); this.blockInput = false; return true; }, @@ -2097,7 +2097,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { label: i18next.t("starterSelectUiHandler:enablePassive"), handler: () => { starterData.passiveAttr |= PassiveAttr.ENABLED; - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); this.setSpeciesDetails(this.lastSpecies); return true; }, @@ -2107,7 +2107,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { label: i18next.t("starterSelectUiHandler:disablePassive"), handler: () => { starterData.passiveAttr ^= PassiveAttr.ENABLED; - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); this.setSpeciesDetails(this.lastSpecies); return true; }, @@ -2125,7 +2125,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (starterContainer) { starterContainer.favoriteIcon.setVisible(starterAttributes.favorite); } - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); return true; }, }); @@ -2138,7 +2138,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (starterContainer) { starterContainer.favoriteIcon.setVisible(starterAttributes.favorite); } - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); return true; }, }); @@ -2150,7 +2150,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { let nickname = starterAttributes.nickname ? String(starterAttributes.nickname) : ""; nickname = decodeURIComponent(escape(atob(nickname))); ui.setModeWithoutClear( - Mode.RENAME_POKEMON, + UiMode.RENAME_POKEMON, { buttonActions: [ (sanitizedName: string) => { @@ -2162,10 +2162,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } else { this.pokemonNameText.setText(this.lastSpecies.name); } - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); }, () => { - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); }, ], }, @@ -2197,7 +2197,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { return globalScene.reset(true); } }); - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); this.setSpeciesDetails(this.lastSpecies); globalScene.playSound("se/buy"); @@ -2238,7 +2238,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } }); this.tryUpdateValue(0); - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); globalScene.playSound("se/buy"); // update the value label and icon/animation for available upgrade @@ -2290,7 +2290,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { return globalScene.reset(true); } }); - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); globalScene.playSound("se/buy"); // update the icon/animation for available upgrade @@ -2308,11 +2308,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler { options.push({ label: i18next.t("menu:cancel"), handler: () => { - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); return true; }, }); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { + ui.setModeWithoutClear(UiMode.OPTION_SELECT, { options: options, yOffset: 47, }); @@ -2320,14 +2320,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler { options.push({ label: i18next.t("menuUiHandler:POKEDEX"), handler: () => { - ui.setMode(Mode.STARTER_SELECT).then(() => { + ui.setMode(UiMode.STARTER_SELECT).then(() => { const attributes = { shiny: starterAttributes.shiny, variant: starterAttributes.variant, form: starterAttributes.form, female: starterAttributes.female, }; - ui.setOverlayMode(Mode.POKEDEX_PAGE, this.lastSpecies, attributes); + ui.setOverlayMode(UiMode.POKEDEX_PAGE, this.lastSpecies, attributes); }); return true; }, @@ -2336,7 +2336,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { options.push({ label: i18next.t("starterSelectUiHandler:useCandies"), handler: () => { - ui.setMode(Mode.STARTER_SELECT).then(() => showUseCandies()); + ui.setMode(UiMode.STARTER_SELECT).then(() => showUseCandies()); return true; }, }); @@ -2344,11 +2344,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler { options.push({ label: i18next.t("menu:cancel"), handler: () => { - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); return true; }, }); - ui.setModeWithoutClear(Mode.OPTION_SELECT, { + ui.setModeWithoutClear(UiMode.OPTION_SELECT, { options: options, yOffset: 47, }); @@ -4281,15 +4281,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const ui = this.getUi(); const cancel = () => { - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); this.clearText(); this.blockInput = false; }; ui.showText(i18next.t("starterSelectUiHandler:confirmExit"), null, () => { ui.setModeWithoutClear( - Mode.CONFIRM, + UiMode.CONFIRM, () => { - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); globalScene.clearPhaseQueue(); if (globalScene.gameMode.isChallenge) { globalScene.pushPhase(new SelectChallengePhase()); @@ -4318,7 +4318,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const ui = this.getUi(); const cancel = () => { - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); if (!manualTrigger) { this.popStarter(this.starterSpecies.length - 1); } @@ -4330,11 +4330,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (canStart) { ui.showText(i18next.t("starterSelectUiHandler:confirmStartTeam"), null, () => { ui.setModeWithoutClear( - Mode.CONFIRM, + UiMode.CONFIRM, () => { const startRun = () => { globalScene.money = globalScene.gameMode.getStartingMoney(); - ui.setMode(Mode.STARTER_SELECT); + ui.setMode(UiMode.STARTER_SELECT); const thisObj = this; const originalStarterSelectCallback = this.starterSelectCallback; this.starterSelectCallback = null; diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 5ff4a02793d..877c342651f 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -1,6 +1,6 @@ -import { starterColors } from "#app/battle-scene"; +import { starterColors } from "#app/global-vars/starter-colors"; import { globalScene } from "#app/global-scene"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import UiHandler from "#app/ui/ui-handler"; import { getLocalizedSpriteKey, @@ -11,7 +11,7 @@ import { isNullOrUndefined, toReadableString, formatStat, -} from "#app/utils"; +} from "#app/utils/common"; import type { PlayerPokemon, PokemonMove } from "#app/field/pokemon"; import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters"; import { argbFromRgba } from "@material/material-color-utilities"; @@ -128,7 +128,7 @@ export default class SummaryUiHandler extends UiHandler { private selectCallback: Function | null; constructor() { - super(Mode.SUMMARY); + super(UiMode.SUMMARY); } setup() { @@ -510,7 +510,7 @@ export default class SummaryUiHandler extends UiHandler { } const ui = this.getUi(); - const fromPartyMode = ui.handlers[Mode.PARTY].active; + const fromPartyMode = ui.handlers[UiMode.PARTY].active; let success = false; let error = false; @@ -610,9 +610,9 @@ export default class SummaryUiHandler extends UiHandler { } if (!fromPartyMode) { - ui.setMode(Mode.MESSAGE); + ui.setMode(UiMode.MESSAGE); } else { - ui.setMode(Mode.PARTY); + ui.setMode(UiMode.PARTY); } } success = true; diff --git a/src/ui/target-select-ui-handler.ts b/src/ui/target-select-ui-handler.ts index a9f88b337f3..0db2020c25a 100644 --- a/src/ui/target-select-ui-handler.ts +++ b/src/ui/target-select-ui-handler.ts @@ -1,7 +1,7 @@ import { BattlerIndex } from "../battle"; -import { Mode } from "./ui"; +import { UiMode } from "#enums/ui-mode"; import UiHandler from "./ui-handler"; -import { isNullOrUndefined, fixedInt } from "#app/utils"; +import { isNullOrUndefined, fixedInt } from "#app/utils/common"; import { getMoveTargets } from "../data/moves/move"; import { Button } from "#enums/buttons"; import type { Moves } from "#enums/moves"; @@ -27,7 +27,7 @@ export default class TargetSelectUiHandler extends UiHandler { private targetBattleInfoMoveTween: Phaser.Tweens.Tween[] = []; constructor() { - super(Mode.TARGET_SELECT); + super(UiMode.TARGET_SELECT); this.cursor = -1; } diff --git a/src/ui/test-dialogue-ui-handler.ts b/src/ui/test-dialogue-ui-handler.ts index 9fbfc01a317..9ecf1641e7b 100644 --- a/src/ui/test-dialogue-ui-handler.ts +++ b/src/ui/test-dialogue-ui-handler.ts @@ -4,8 +4,8 @@ import type { ModalConfig } from "./modal-ui-handler"; import i18next from "i18next"; import type { PlayerPokemon } from "#app/field/pokemon"; import type { OptionSelectItem } from "./abstact-option-select-ui-handler"; -import { isNullOrUndefined } from "#app/utils"; -import { Mode } from "./ui"; +import { isNullOrUndefined } from "#app/utils/common"; +import { UiMode } from "#enums/ui-mode"; export default class TestDialogueUiHandler extends FormModalUiHandler { keys: string[]; @@ -88,7 +88,7 @@ export default class TestDialogueUiHandler extends FormModalUiHandler { input.on("keydown", (inputObject, evt: KeyboardEvent) => { if ( ["escape", "space"].some(v => v === evt.key.toLowerCase() || v === evt.code.toLowerCase()) && - ui.getMode() === Mode.AUTO_COMPLETE + ui.getMode() === UiMode.AUTO_COMPLETE ) { // Delete autocomplete list and recovery focus. inputObject.on("blur", () => inputObject.node.focus(), { once: true }); @@ -98,7 +98,7 @@ export default class TestDialogueUiHandler extends FormModalUiHandler { input.on("textchange", (inputObject, evt: InputEvent) => { // Delete autocomplete. - if (ui.getMode() === Mode.AUTO_COMPLETE) { + if (ui.getMode() === UiMode.AUTO_COMPLETE) { ui.revertMode(); } @@ -133,7 +133,7 @@ export default class TestDialogueUiHandler extends FormModalUiHandler { maxOptions: 5, modalContainer: this.modalContainer, }; - ui.setOverlayMode(Mode.AUTO_COMPLETE, modalOpts); + ui.setOverlayMode(UiMode.AUTO_COMPLETE, modalOpts); } }); @@ -147,7 +147,7 @@ export default class TestDialogueUiHandler extends FormModalUiHandler { this.inputs[0].text = args[1]; } this.submitAction = _ => { - if (ui.getMode() === Mode.TEST_DIALOGUE) { + if (ui.getMode() === UiMode.TEST_DIALOGUE) { this.sanitizeInputs(); const sanitizedName = btoa(unescape(encodeURIComponent(this.inputs[0].text))); config.buttonActions[0](sanitizedName); diff --git a/src/ui/time-of-day-widget.ts b/src/ui/time-of-day-widget.ts index 5e42e6215f8..5f5116a2da0 100644 --- a/src/ui/time-of-day-widget.ts +++ b/src/ui/time-of-day-widget.ts @@ -1,4 +1,4 @@ -import { fixedInt } from "#app/utils"; +import { fixedInt } from "#app/utils/common"; import { globalScene } from "#app/global-scene"; import { BattleSceneEventType } from "../events/battle-scene"; import { EaseType } from "#enums/ease-type"; diff --git a/src/ui/title-ui-handler.ts b/src/ui/title-ui-handler.ts index 405e3cc4a27..bed4d568481 100644 --- a/src/ui/title-ui-handler.ts +++ b/src/ui/title-ui-handler.ts @@ -1,6 +1,6 @@ import OptionSelectUiHandler from "./settings/option-select-ui-handler"; -import { Mode } from "./ui"; -import { fixedInt, randInt, randItem } from "#app/utils"; +import { UiMode } from "#enums/ui-mode"; +import { fixedInt, randInt, randItem } from "#app/utils/common"; import { TextStyle, addTextObject } from "./text"; import { getSplashMessages } from "../data/splash-messages"; import i18next from "i18next"; @@ -26,7 +26,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { private titleStatsTimer: NodeJS.Timeout | null; - constructor(mode: Mode = Mode.TITLE) { + constructor(mode: UiMode = UiMode.TITLE) { super(mode); } diff --git a/src/ui/ui-handler.ts b/src/ui/ui-handler.ts index 433f85d0f92..d3784c1225c 100644 --- a/src/ui/ui-handler.ts +++ b/src/ui/ui-handler.ts @@ -1,7 +1,7 @@ import { globalScene } from "#app/global-scene"; import type { TextStyle } from "./text"; import { getTextColor } from "./text"; -import type { Mode } from "./ui"; +import type { UiMode } from "#enums/ui-mode"; import type { Button } from "#enums/buttons"; /** @@ -15,7 +15,7 @@ export default abstract class UiHandler { /** * @param mode The mode of the UI element. These should be unique. */ - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { this.mode = mode; } diff --git a/src/ui/ui.ts b/src/ui/ui.ts index c7981cd5fba..ad496df6382 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -28,7 +28,7 @@ import { addWindow } from "./ui-theme"; import LoginFormUiHandler from "./login-form-ui-handler"; import RegistrationFormUiHandler from "./registration-form-ui-handler"; import LoadingModalUiHandler from "./loading-modal-ui-handler"; -import { executeIf } from "#app/utils"; +import { executeIf } from "#app/utils/common"; import GameStatsUiHandler from "./game-stats-ui-handler"; import AwaitableUiHandler from "./awaitable-ui-handler"; import SaveSlotSelectUiHandler from "./save-slot-select-ui-handler"; @@ -57,102 +57,55 @@ import MysteryEncounterUiHandler from "./mystery-encounter-ui-handler"; import PokedexScanUiHandler from "./pokedex-scan-ui-handler"; import PokedexPageUiHandler from "./pokedex-page-ui-handler"; import { NavigationManager } from "./settings/navigationMenu"; - -export enum Mode { - MESSAGE, - TITLE, - COMMAND, - FIGHT, - BALL, - TARGET_SELECT, - MODIFIER_SELECT, - SAVE_SLOT, - PARTY, - SUMMARY, - STARTER_SELECT, - EVOLUTION_SCENE, - EGG_HATCH_SCENE, - EGG_HATCH_SUMMARY, - CONFIRM, - OPTION_SELECT, - MENU, - MENU_OPTION_SELECT, - SETTINGS, - SETTINGS_DISPLAY, - SETTINGS_AUDIO, - SETTINGS_GAMEPAD, - GAMEPAD_BINDING, - SETTINGS_KEYBOARD, - KEYBOARD_BINDING, - ACHIEVEMENTS, - GAME_STATS, - EGG_LIST, - EGG_GACHA, - POKEDEX, - POKEDEX_SCAN, - POKEDEX_PAGE, - LOGIN_FORM, - REGISTRATION_FORM, - LOADING, - SESSION_RELOAD, - UNAVAILABLE, - CHALLENGE_SELECT, - RENAME_POKEMON, - RUN_HISTORY, - RUN_INFO, - TEST_DIALOGUE, - AUTO_COMPLETE, - ADMIN, - MYSTERY_ENCOUNTER, -} +import { UiMode } from "#enums/ui-mode"; const transitionModes = [ - Mode.SAVE_SLOT, - Mode.PARTY, - Mode.SUMMARY, - Mode.STARTER_SELECT, - Mode.EVOLUTION_SCENE, - Mode.EGG_HATCH_SCENE, - Mode.EGG_LIST, - Mode.EGG_GACHA, - Mode.POKEDEX, - Mode.POKEDEX_PAGE, - Mode.CHALLENGE_SELECT, - Mode.RUN_HISTORY, + UiMode.SAVE_SLOT, + UiMode.PARTY, + UiMode.SUMMARY, + UiMode.STARTER_SELECT, + UiMode.EVOLUTION_SCENE, + UiMode.EGG_HATCH_SCENE, + UiMode.EGG_LIST, + UiMode.EGG_GACHA, + UiMode.POKEDEX, + UiMode.POKEDEX_PAGE, + UiMode.CHALLENGE_SELECT, + UiMode.RUN_HISTORY, ]; const noTransitionModes = [ - Mode.TITLE, - Mode.CONFIRM, - Mode.OPTION_SELECT, - Mode.MENU, - Mode.MENU_OPTION_SELECT, - Mode.GAMEPAD_BINDING, - Mode.KEYBOARD_BINDING, - Mode.SETTINGS, - Mode.SETTINGS_AUDIO, - Mode.SETTINGS_DISPLAY, - Mode.SETTINGS_GAMEPAD, - Mode.SETTINGS_KEYBOARD, - Mode.ACHIEVEMENTS, - Mode.GAME_STATS, - Mode.POKEDEX_SCAN, - Mode.LOGIN_FORM, - Mode.REGISTRATION_FORM, - Mode.LOADING, - Mode.SESSION_RELOAD, - Mode.UNAVAILABLE, - Mode.RENAME_POKEMON, - Mode.TEST_DIALOGUE, - Mode.AUTO_COMPLETE, - Mode.ADMIN, - Mode.MYSTERY_ENCOUNTER, - Mode.RUN_INFO, + UiMode.TITLE, + UiMode.CONFIRM, + UiMode.OPTION_SELECT, + UiMode.MENU, + UiMode.MENU_OPTION_SELECT, + UiMode.GAMEPAD_BINDING, + UiMode.KEYBOARD_BINDING, + UiMode.SETTINGS, + UiMode.SETTINGS_AUDIO, + UiMode.SETTINGS_DISPLAY, + UiMode.SETTINGS_GAMEPAD, + UiMode.SETTINGS_KEYBOARD, + UiMode.ACHIEVEMENTS, + UiMode.GAME_STATS, + UiMode.POKEDEX_SCAN, + UiMode.LOGIN_FORM, + UiMode.REGISTRATION_FORM, + UiMode.LOADING, + UiMode.SESSION_RELOAD, + UiMode.UNAVAILABLE, + UiMode.RENAME_POKEMON, + UiMode.TEST_DIALOGUE, + UiMode.AUTO_COMPLETE, + UiMode.ADMIN, + UiMode.MYSTERY_ENCOUNTER, + UiMode.RUN_INFO, ]; export default class UI extends Phaser.GameObjects.Container { - private mode: Mode; - private modeChain: Mode[]; + private mode: UiMode; + private modeChain: UiMode[]; public handlers: UiHandler[]; private overlay: Phaser.GameObjects.Rectangle; public achvBar: AchvBar; @@ -169,7 +122,7 @@ export default class UI extends Phaser.GameObjects.Container { constructor() { super(globalScene, 0, globalScene.game.canvas.height / 6); - this.mode = Mode.MESSAGE; + this.mode = UiMode.MESSAGE; this.modeChain = []; this.handlers = [ new BattleMessageUiHandler(), @@ -189,7 +142,7 @@ export default class UI extends Phaser.GameObjects.Container { new ConfirmUiHandler(), new OptionSelectUiHandler(), new MenuUiHandler(), - new OptionSelectUiHandler(Mode.MENU_OPTION_SELECT), + new OptionSelectUiHandler(UiMode.MENU_OPTION_SELECT), // settings new SettingsUiHandler(), new SettingsDisplayUiHandler(), @@ -203,7 +156,7 @@ export default class UI extends Phaser.GameObjects.Container { new EggListUiHandler(), new EggGachaUiHandler(), new PokedexUiHandler(), - new PokedexScanUiHandler(Mode.TEST_DIALOGUE), + new PokedexScanUiHandler(UiMode.TEST_DIALOGUE), new PokedexPageUiHandler(), new LoginFormUiHandler(), new RegistrationFormUiHandler(), @@ -214,7 +167,7 @@ export default class UI extends Phaser.GameObjects.Container { new RenameFormUiHandler(), new RunHistoryUiHandler(), new RunInfoUiHandler(), - new TestDialogueUiHandler(Mode.TEST_DIALOGUE), + new TestDialogueUiHandler(UiMode.TEST_DIALOGUE), new AutoCompleteUiHandler(), new AdminUiHandler(), new MysteryEncounterUiHandler(), @@ -222,7 +175,7 @@ export default class UI extends Phaser.GameObjects.Container { } setup(): void { - this.setName(`ui-${Mode[this.mode]}`); + this.setName(`ui-${UiMode[this.mode]}`); for (const handler of this.handlers) { handler.setup(); } @@ -279,7 +232,7 @@ export default class UI extends Phaser.GameObjects.Container { } getMessageHandler(): BattleMessageUiHandler { - return this.handlers[Mode.MESSAGE] as BattleMessageUiHandler; + return this.handlers[UiMode.MESSAGE] as BattleMessageUiHandler; } processInfoButton(pressed: boolean) { @@ -287,7 +240,7 @@ export default class UI extends Phaser.GameObjects.Container { return false; } - if ([Mode.CONFIRM, Mode.COMMAND, Mode.FIGHT, Mode.MESSAGE, Mode.TARGET_SELECT].includes(this.mode)) { + if ([UiMode.CONFIRM, UiMode.COMMAND, UiMode.FIGHT, UiMode.MESSAGE, UiMode.TARGET_SELECT].includes(this.mode)) { globalScene?.processInfoButton(pressed); return true; } @@ -564,7 +517,7 @@ export default class UI extends Phaser.GameObjects.Container { } private setModeInternal( - mode: Mode, + mode: UiMode, clear: boolean, forceTransition: boolean, chainMode: boolean, @@ -587,7 +540,7 @@ export default class UI extends Phaser.GameObjects.Container { this.mode = mode; const touchControls = document?.getElementById("touchControls"); if (touchControls) { - touchControls.dataset.uiMode = Mode[mode]; + touchControls.dataset.uiMode = UiMode[mode]; } this.getHandler().show(args); } @@ -612,23 +565,23 @@ export default class UI extends Phaser.GameObjects.Container { }); } - getMode(): Mode { + getMode(): UiMode { return this.mode; } - setMode(mode: Mode, ...args: any[]): Promise { + setMode(mode: UiMode, ...args: any[]): Promise { return this.setModeInternal(mode, true, false, false, args); } - setModeForceTransition(mode: Mode, ...args: any[]): Promise { + setModeForceTransition(mode: UiMode, ...args: any[]): Promise { return this.setModeInternal(mode, true, true, false, args); } - setModeWithoutClear(mode: Mode, ...args: any[]): Promise { + setModeWithoutClear(mode: UiMode, ...args: any[]): Promise { return this.setModeInternal(mode, false, false, false, args); } - setOverlayMode(mode: Mode, ...args: any[]): Promise { + setOverlayMode(mode: UiMode, ...args: any[]): Promise { return this.setModeInternal(mode, false, false, true, args); } @@ -651,7 +604,7 @@ export default class UI extends Phaser.GameObjects.Container { globalScene.updateGameInfo(); const touchControls = document.getElementById("touchControls"); if (touchControls) { - touchControls.dataset.uiMode = Mode[this.mode]; + touchControls.dataset.uiMode = UiMode[this.mode]; } resolve(true); }; @@ -678,7 +631,7 @@ export default class UI extends Phaser.GameObjects.Container { }); } - public getModeChain(): Mode[] { + public getModeChain(): UiMode[] { return this.modeChain; } diff --git a/src/ui/unavailable-modal-ui-handler.ts b/src/ui/unavailable-modal-ui-handler.ts index 01ed850f6d0..5bed55ec24a 100644 --- a/src/ui/unavailable-modal-ui-handler.ts +++ b/src/ui/unavailable-modal-ui-handler.ts @@ -1,9 +1,10 @@ import type { ModalConfig } from "./modal-ui-handler"; import { ModalUiHandler } from "./modal-ui-handler"; import { addTextObject, TextStyle } from "./text"; -import type { Mode } from "./ui"; +import type { UiMode } from "#enums/ui-mode"; import { updateUserInfo } from "#app/account"; -import { removeCookie, sessionIdKey } from "#app/utils"; +import { sessionIdKey } from "#app/utils/common"; +import { removeCookie } from "#app/utils/cookies"; import i18next from "i18next"; import { globalScene } from "#app/global-scene"; @@ -17,7 +18,7 @@ export default class UnavailableModalUiHandler extends ModalUiHandler { private readonly randVarianceTime = 1000 * 10; - constructor(mode: Mode | null = null) { + constructor(mode: UiMode | null = null) { super(mode); this.reconnectDuration = this.minTime; } diff --git a/src/utils.ts b/src/utils/common.ts similarity index 92% rename from src/utils.ts rename to src/utils/common.ts index ce9966c0d7f..4acfabce080 100644 --- a/src/utils.ts +++ b/src/utils/common.ts @@ -276,43 +276,6 @@ export const apiUrl = localServerUrl ?? "https://api.pokerogue.net"; // used to disable api calls when isLocal is true and a server is not found export let isLocalServerConnected = true; -export const isBeta = import.meta.env.MODE === "beta"; // this checks to see if the env mode is development. Technically this gives the same value for beta AND for dev envs - -export function setCookie(cName: string, cValue: string): void { - const expiration = new Date(); - expiration.setTime(new Date().getTime() + 3600000 * 24 * 30 * 3 /*7*/); - document.cookie = `${cName}=${cValue};Secure;SameSite=Strict;Domain=${window.location.hostname};Path=/;Expires=${expiration.toUTCString()}`; -} - -export function removeCookie(cName: string): void { - if (isBeta) { - document.cookie = `${cName}=;Secure;SameSite=Strict;Domain=pokerogue.net;Path=/;Max-Age=-1`; // we need to remove the cookie from the main domain as well - } - - document.cookie = `${cName}=;Secure;SameSite=Strict;Domain=${window.location.hostname};Path=/;Max-Age=-1`; - document.cookie = `${cName}=;Secure;SameSite=Strict;Path=/;Max-Age=-1`; // legacy cookie without domain, for older cookies to prevent a login loop -} - -export function getCookie(cName: string): string { - // check if there are multiple cookies with the same name and delete them - if (document.cookie.split(";").filter(c => c.includes(cName)).length > 1) { - removeCookie(cName); - return ""; - } - const name = `${cName}=`; - const ca = document.cookie.split(";"); - for (let i = 0; i < ca.length; i++) { - let c = ca[i]; - while (c.charAt(0) === " ") { - c = c.substring(1); - } - if (c.indexOf(name) === 0) { - return c.substring(name.length, c.length); - } - } - return ""; -} - /** * When locally running the game, "pings" the local server * with a GET request to verify if a server is running, diff --git a/src/utils/cookies.ts b/src/utils/cookies.ts new file mode 100644 index 00000000000..5ed793c0451 --- /dev/null +++ b/src/utils/cookies.ts @@ -0,0 +1,36 @@ +import { isBeta } from "./utility-vars"; + +export function setCookie(cName: string, cValue: string): void { + const expiration = new Date(); + expiration.setTime(new Date().getTime() + 3600000 * 24 * 30 * 3 /*7*/); + document.cookie = `${cName}=${cValue};Secure;SameSite=Strict;Domain=${window.location.hostname};Path=/;Expires=${expiration.toUTCString()}`; +} + +export function removeCookie(cName: string): void { + if (isBeta) { + document.cookie = `${cName}=;Secure;SameSite=Strict;Domain=pokerogue.net;Path=/;Max-Age=-1`; // we need to remove the cookie from the main domain as well + } + + document.cookie = `${cName}=;Secure;SameSite=Strict;Domain=${window.location.hostname};Path=/;Max-Age=-1`; + document.cookie = `${cName}=;Secure;SameSite=Strict;Path=/;Max-Age=-1`; // legacy cookie without domain, for older cookies to prevent a login loop +} + +export function getCookie(cName: string): string { + // check if there are multiple cookies with the same name and delete them + if (document.cookie.split(";").filter(c => c.includes(cName)).length > 1) { + removeCookie(cName); + return ""; + } + const name = `${cName}=`; + const ca = document.cookie.split(";"); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + while (c.charAt(0) === " ") { + c = c.substring(1); + } + if (c.indexOf(name) === 0) { + return c.substring(name.length, c.length); + } + } + return ""; +} diff --git a/src/utils/utility-vars.ts b/src/utils/utility-vars.ts new file mode 100644 index 00000000000..081f70164c8 --- /dev/null +++ b/src/utils/utility-vars.ts @@ -0,0 +1 @@ +export const isBeta = import.meta.env.MODE === "beta"; // this checks to see if the env mode is development. Technically this gives the same value for beta AND for dev envs diff --git a/test/abilities/ability_timing.test.ts b/test/abilities/ability_timing.test.ts index 9df4fe0d1c9..6128a3e6196 100644 --- a/test/abilities/ability_timing.test.ts +++ b/test/abilities/ability_timing.test.ts @@ -2,7 +2,7 @@ import { BattleStyle } from "#app/enums/battle-style"; import { CommandPhase } from "#app/phases/command-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase"; import i18next from "#app/plugins/i18n"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { Abilities } from "#enums/abilities"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; @@ -40,9 +40,9 @@ describe("Ability Timing", () => { game.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - game.setMode(Mode.MESSAGE); + game.setMode(UiMode.MESSAGE); game.endPhase(); }, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase), diff --git a/test/abilities/analytic.test.ts b/test/abilities/analytic.test.ts index 1aadf2c0746..108c712da00 100644 --- a/test/abilities/analytic.test.ts +++ b/test/abilities/analytic.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { isBetween, toDmgValue } from "#app/utils"; +import { isBetween, toDmgValue } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/abilities/disguise.test.ts b/test/abilities/disguise.test.ts index fd8289312db..aeaf8ea2363 100644 --- a/test/abilities/disguise.test.ts +++ b/test/abilities/disguise.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { toDmgValue } from "#app/utils"; +import { toDmgValue } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/abilities/healer.test.ts b/test/abilities/healer.test.ts index d06c4680e36..d292ad0f625 100644 --- a/test/abilities/healer.test.ts +++ b/test/abilities/healer.test.ts @@ -5,7 +5,7 @@ import { StatusEffect } from "#enums/status-effect"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi, type MockInstance } from "vitest"; -import { isNullOrUndefined } from "#app/utils"; +import { isNullOrUndefined } from "#app/utils/common"; import { PostTurnResetStatusAbAttr } from "#app/data/abilities/ability"; import { allAbilities } from "#app/data/data-lists"; import type Pokemon from "#app/field/pokemon"; diff --git a/test/abilities/heatproof.test.ts b/test/abilities/heatproof.test.ts index f2fabf953d6..016237bb02f 100644 --- a/test/abilities/heatproof.test.ts +++ b/test/abilities/heatproof.test.ts @@ -1,7 +1,7 @@ import { Species } from "#app/enums/species"; import { StatusEffect } from "#app/enums/status-effect"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { toDmgValue } from "#app/utils"; +import { toDmgValue } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import GameManager from "#test/testUtils/gameManager"; diff --git a/test/abilities/intimidate.test.ts b/test/abilities/intimidate.test.ts index 2888c575b0d..8db39270dcf 100644 --- a/test/abilities/intimidate.test.ts +++ b/test/abilities/intimidate.test.ts @@ -1,7 +1,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import Phaser from "phaser"; import GameManager from "#test/testUtils/gameManager"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { Stat } from "#enums/stat"; import { getMovePosition } from "#test/testUtils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; @@ -38,9 +38,9 @@ describe("Abilities - Intimidate", () => { await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); game.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - game.setMode(Mode.MESSAGE); + game.setMode(UiMode.MESSAGE); game.endPhase(); }, () => game.isCurrentPhase("CommandPhase") || game.isCurrentPhase("TurnInitPhase"), @@ -69,9 +69,9 @@ describe("Abilities - Intimidate", () => { await game.classicMode.runToSummon([Species.MIGHTYENA, Species.POOCHYENA]); game.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - game.setMode(Mode.MESSAGE); + game.setMode(UiMode.MESSAGE); game.endPhase(); }, () => game.isCurrentPhase("CommandPhase") || game.isCurrentPhase("TurnInitPhase"), diff --git a/test/abilities/parental_bond.test.ts b/test/abilities/parental_bond.test.ts index d81486e7316..a75fea82830 100644 --- a/test/abilities/parental_bond.test.ts +++ b/test/abilities/parental_bond.test.ts @@ -1,6 +1,6 @@ import { PokemonType } from "#enums/pokemon-type"; import { BattlerTagType } from "#enums/battler-tag-type"; -import { toDmgValue } from "#app/utils"; +import { toDmgValue } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/abilities/shield_dust.test.ts b/test/abilities/shield_dust.test.ts index 4f6783eb66a..0b96640a29f 100644 --- a/test/abilities/shield_dust.test.ts +++ b/test/abilities/shield_dust.test.ts @@ -6,7 +6,7 @@ import { MoveEffectChanceMultiplierAbAttr, } from "#app/data/abilities/ability"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/abilities/stakeout.test.ts b/test/abilities/stakeout.test.ts index b3a7bdbf287..8a2231bba0b 100644 --- a/test/abilities/stakeout.test.ts +++ b/test/abilities/stakeout.test.ts @@ -1,5 +1,5 @@ import { BattlerIndex } from "#app/battle"; -import { isBetween } from "#app/utils"; +import { isBetween } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/abilities/wimp_out.test.ts b/test/abilities/wimp_out.test.ts index c46675376c1..463ec7587dc 100644 --- a/test/abilities/wimp_out.test.ts +++ b/test/abilities/wimp_out.test.ts @@ -2,7 +2,7 @@ import { BattlerIndex } from "#app/battle"; import { ArenaTagSide } from "#app/data/arena-tag"; import { allMoves } from "#app/data/moves/move"; import GameManager from "#test/testUtils/gameManager"; -import { toDmgValue } from "#app/utils"; +import { toDmgValue } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; diff --git a/test/account.test.ts b/test/account.test.ts index 3f6b9f3f80b..77368b0b64c 100644 --- a/test/account.test.ts +++ b/test/account.test.ts @@ -1,4 +1,4 @@ -import * as battleScene from "#app/battle-scene"; +import * as bypassLogin from "#app/global-vars/bypass-login"; import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; import { describe, expect, it, vi } from "vitest"; import { initLoggedInUser, loggedInUser, updateUserInfo } from "#app/account"; @@ -15,7 +15,7 @@ describe("account", () => { describe("updateUserInfo", () => { it("should set loggedInUser! to Guest if bypassLogin is true", async () => { - vi.spyOn(battleScene, "bypassLogin", "get").mockReturnValue(true); + vi.spyOn(bypassLogin, "bypassLogin", "get").mockReturnValue(true); const [success, status] = await updateUserInfo(); @@ -26,7 +26,7 @@ describe("account", () => { }); it("should fetch user info from the API if bypassLogin is false", async () => { - vi.spyOn(battleScene, "bypassLogin", "get").mockReturnValue(false); + vi.spyOn(bypassLogin, "bypassLogin", "get").mockReturnValue(false); vi.spyOn(pokerogueApi.account, "getInfo").mockResolvedValue([ { username: "test", @@ -47,7 +47,7 @@ describe("account", () => { }); it("should handle resolved API errors", async () => { - vi.spyOn(battleScene, "bypassLogin", "get").mockReturnValue(false); + vi.spyOn(bypassLogin, "bypassLogin", "get").mockReturnValue(false); vi.spyOn(pokerogueApi.account, "getInfo").mockResolvedValue([null, 401]); const [success, status] = await updateUserInfo(); @@ -57,7 +57,7 @@ describe("account", () => { }); it("should handle 500 API errors", async () => { - vi.spyOn(battleScene, "bypassLogin", "get").mockReturnValue(false); + vi.spyOn(bypassLogin, "bypassLogin", "get").mockReturnValue(false); vi.spyOn(pokerogueApi.account, "getInfo").mockResolvedValue([null, 500]); const [success, status] = await updateUserInfo(); diff --git a/test/achievements/achievement.test.ts b/test/achievements/achievement.test.ts index 5c53e38e208..0b49c4d23ab 100644 --- a/test/achievements/achievement.test.ts +++ b/test/achievements/achievement.test.ts @@ -10,7 +10,7 @@ import { RibbonAchv, achvs, } from "#app/system/achv"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/battle/battle.test.ts b/test/battle/battle.test.ts index 51304c7d5dd..e980984580e 100644 --- a/test/battle/battle.test.ts +++ b/test/battle/battle.test.ts @@ -18,7 +18,7 @@ import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { VictoryPhase } from "#app/phases/victory-phase"; import GameManager from "#test/testUtils/gameManager"; import { generateStarter } from "#test/testUtils/gameManagerUtils"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { PlayerGender } from "#enums/player-gender"; @@ -49,7 +49,7 @@ describe("Test Battle Phase", () => { it("test phase interceptor with prompt", async () => { await game.phaseInterceptor.run(LoginPhase); - game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => { + game.onNextPrompt("SelectGenderPhase", UiMode.OPTION_SELECT, () => { game.scene.gameData.gender = PlayerGender.MALE; game.endPhase(); }); @@ -57,36 +57,36 @@ describe("Test Battle Phase", () => { await game.phaseInterceptor.run(SelectGenderPhase); await game.phaseInterceptor.run(TitlePhase); - await game.waitMode(Mode.TITLE); + await game.waitMode(UiMode.TITLE); - expect(game.scene.ui?.getMode()).toBe(Mode.TITLE); + expect(game.scene.ui?.getMode()).toBe(UiMode.TITLE); expect(game.scene.gameData.gender).toBe(PlayerGender.MALE); }, 20000); it("test phase interceptor with prompt with preparation for a future prompt", async () => { await game.phaseInterceptor.run(LoginPhase); - game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => { + game.onNextPrompt("SelectGenderPhase", UiMode.OPTION_SELECT, () => { game.scene.gameData.gender = PlayerGender.MALE; game.endPhase(); }); - game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { - game.setMode(Mode.MESSAGE); + game.onNextPrompt("CheckSwitchPhase", UiMode.CONFIRM, () => { + game.setMode(UiMode.MESSAGE); game.endPhase(); }); await game.phaseInterceptor.run(SelectGenderPhase); await game.phaseInterceptor.run(TitlePhase); - await game.waitMode(Mode.TITLE); + await game.waitMode(UiMode.TITLE); - expect(game.scene.ui?.getMode()).toBe(Mode.TITLE); + expect(game.scene.ui?.getMode()).toBe(UiMode.TITLE); expect(game.scene.gameData.gender).toBe(PlayerGender.MALE); }, 20000); it("newGame one-liner", async () => { await game.startBattle(); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); @@ -156,7 +156,7 @@ describe("Test Battle Phase", () => { await game.phaseInterceptor.run(LoginPhase); game.onNextPrompt( "SelectGenderPhase", - Mode.OPTION_SELECT, + UiMode.OPTION_SELECT, () => { game.scene.gameData.gender = PlayerGender.MALE; game.endPhase(); @@ -171,7 +171,7 @@ describe("Test Battle Phase", () => { await game.phaseInterceptor.run(LoginPhase); game.onNextPrompt( "SelectGenderPhase", - Mode.OPTION_SELECT, + UiMode.OPTION_SELECT, () => { game.scene.gameData.gender = PlayerGender.MALE; game.endPhase(); @@ -185,14 +185,14 @@ describe("Test Battle Phase", () => { await game.phaseInterceptor.run(LoginPhase); game.onNextPrompt( "SelectGenderPhase", - Mode.OPTION_SELECT, + UiMode.OPTION_SELECT, () => { game.scene.gameData.gender = PlayerGender.MALE; game.endPhase(); }, () => game.isCurrentPhase(TitlePhase), ); - game.onNextPrompt("TitlePhase", Mode.TITLE, () => { + game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { game.scene.gameMode = getGameMode(GameModes.CLASSIC); const starters = generateStarter(game.scene); const selectStarterPhase = new SelectStarterPhase(); @@ -208,7 +208,7 @@ describe("Test Battle Phase", () => { game.override.enemyAbility(Abilities.HYDRATION); game.override.ability(Abilities.HYDRATION); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); @@ -218,7 +218,7 @@ describe("Test Battle Phase", () => { game.override.enemyAbility(Abilities.HYDRATION); game.override.ability(Abilities.HYDRATION); await game.startBattle([Species.BLASTOISE]); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); @@ -229,7 +229,7 @@ describe("Test Battle Phase", () => { game.override.ability(Abilities.HYDRATION); game.override.startingWave(3); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); @@ -240,7 +240,7 @@ describe("Test Battle Phase", () => { game.override.ability(Abilities.HYDRATION); game.override.startingWave(3); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD, Species.DARKRAI, Species.GABITE]); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); @@ -328,7 +328,7 @@ describe("Test Battle Phase", () => { game.onNextPrompt( "SwitchPhase", - Mode.PARTY, + UiMode.PARTY, () => { expect.fail("Switch was forced"); }, diff --git a/test/battle/special_battle.test.ts b/test/battle/special_battle.test.ts index 46dd8eaa010..163f23e488d 100644 --- a/test/battle/special_battle.test.ts +++ b/test/battle/special_battle.test.ts @@ -1,5 +1,5 @@ import { CommandPhase } from "#app/phases/command-phase"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -34,63 +34,63 @@ describe("Test Battle Phase", () => { it("startBattle 2vs1 boss", async () => { game.override.battleStyle("single").startingWave(10); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 boss", async () => { game.override.battleStyle("double").startingWave(10); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 trainer", async () => { game.override.battleStyle("double").startingWave(5); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs1 trainer", async () => { game.override.battleStyle("single").startingWave(5); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs1 rival", async () => { game.override.battleStyle("single").startingWave(8); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 rival", async () => { game.override.battleStyle("double").startingWave(8); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 1vs1 trainer", async () => { game.override.battleStyle("single").startingWave(5); await game.startBattle([Species.BLASTOISE]); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 trainer", async () => { game.override.battleStyle("double").startingWave(5); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 4vs2 trainer", async () => { game.override.battleStyle("double").startingWave(5); await game.startBattle([Species.BLASTOISE, Species.CHARIZARD, Species.DARKRAI, Species.GABITE]); - expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); + expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); }); diff --git a/test/boss-pokemon.test.ts b/test/boss-pokemon.test.ts index 9df69da09b7..ef95ae9bcc2 100644 --- a/test/boss-pokemon.test.ts +++ b/test/boss-pokemon.test.ts @@ -6,7 +6,7 @@ import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { EFFECTIVE_STATS } from "#app/enums/stat"; import type { EnemyPokemon } from "#app/field/pokemon"; -import { toDmgValue } from "#app/utils"; +import { toDmgValue } from "#app/utils/common"; describe("Boss Pokemon / Shields", () => { let phaserGame: Phaser.Game; diff --git a/test/daily_mode.test.ts b/test/daily_mode.test.ts index 6b95543fb3b..a7f5784087a 100644 --- a/test/daily_mode.test.ts +++ b/test/daily_mode.test.ts @@ -4,7 +4,7 @@ import { MapModifier } from "#app/modifier/modifier"; import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { Species } from "#enums/species"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import GameManager from "#test/testUtils/gameManager"; @@ -76,7 +76,7 @@ describe("Shop modifications", async () => { game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to("BattleEndPhase"); - game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { + game.onNextPrompt("SelectModifierPhase", UiMode.MODIFIER_SELECT, () => { expect(game.scene.ui.getHandler()).toBeInstanceOf(ModifierSelectUiHandler); game.modifiers.testCheck("EVIOLITE", false).testCheck("MINI_BLACK_HOLE", false); }); @@ -87,7 +87,7 @@ describe("Shop modifications", async () => { game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to("BattleEndPhase"); - game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { + game.onNextPrompt("SelectModifierPhase", UiMode.MODIFIER_SELECT, () => { expect(game.scene.ui.getHandler()).toBeInstanceOf(ModifierSelectUiHandler); game.modifiers.testCheck("EVIOLITE", true).testCheck("MINI_BLACK_HOLE", true); }); diff --git a/test/eggs/egg.test.ts b/test/eggs/egg.test.ts index 8875300780b..0110aa5fdaf 100644 --- a/test/eggs/egg.test.ts +++ b/test/eggs/egg.test.ts @@ -5,7 +5,7 @@ import { EggSourceType } from "#app/enums/egg-source-types"; import { EggTier } from "#app/enums/egg-type"; import { VariantTier } from "#app/enums/variant-tier"; import EggData from "#app/system/egg-data"; -import * as Utils from "#app/utils"; +import * as Utils from "#app/utils/common"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; diff --git a/test/enemy_command.test.ts b/test/enemy_command.test.ts index 6d5cc2698a3..ae1f2918798 100644 --- a/test/enemy_command.test.ts +++ b/test/enemy_command.test.ts @@ -6,7 +6,7 @@ import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import type { EnemyPokemon } from "#app/field/pokemon"; import { AiType } from "#app/field/pokemon"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt } from "#app/utils/common"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/escape-calculations.test.ts b/test/escape-calculations.test.ts index d591bdec9fc..56333432cee 100644 --- a/test/escape-calculations.test.ts +++ b/test/escape-calculations.test.ts @@ -1,7 +1,7 @@ import { AttemptRunPhase } from "#app/phases/attempt-run-phase"; import type { CommandPhase } from "#app/phases/command-phase"; import { Command } from "#app/ui/command-ui-handler"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; diff --git a/test/evolution.test.ts b/test/evolution.test.ts index 68d02402eac..4f91cd99382 100644 --- a/test/evolution.test.ts +++ b/test/evolution.test.ts @@ -6,7 +6,7 @@ import { import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; -import * as Utils from "#app/utils"; +import * as Utils from "#app/utils/common"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; diff --git a/test/game-mode.test.ts b/test/game-mode.test.ts index a2da7d1690a..0483d18e492 100644 --- a/test/game-mode.test.ts +++ b/test/game-mode.test.ts @@ -1,7 +1,7 @@ import type { GameMode } from "#app/game-mode"; import { GameModes, getGameMode } from "#app/game-mode"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import * as Utils from "#app/utils"; +import * as Utils from "#app/utils/common"; import GameManager from "#test/testUtils/gameManager"; describe("game-mode", () => { diff --git a/test/items/dire_hit.test.ts b/test/items/dire_hit.test.ts index f6197e097c2..b409b2ac7cb 100644 --- a/test/items/dire_hit.test.ts +++ b/test/items/dire_hit.test.ts @@ -6,7 +6,7 @@ import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { BattleEndPhase } from "#app/phases/battle-end-phase"; import { TempCritBoosterModifier } from "#app/modifier/modifier"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { Button } from "#app/enums/buttons"; import { CommandPhase } from "#app/phases/command-phase"; @@ -71,7 +71,7 @@ describe("Items - Dire Hit", () => { // Forced DIRE_HIT to spawn in the first slot with override game.onNextPrompt( "SelectModifierPhase", - Mode.MODIFIER_SELECT, + UiMode.MODIFIER_SELECT, () => { const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler; // Traverse to first modifier slot diff --git a/test/items/double_battle_chance_booster.test.ts b/test/items/double_battle_chance_booster.test.ts index b4818e7e7ba..68a29ef823e 100644 --- a/test/items/double_battle_chance_booster.test.ts +++ b/test/items/double_battle_chance_booster.test.ts @@ -5,7 +5,7 @@ import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { Button } from "#app/enums/buttons"; @@ -69,7 +69,7 @@ describe("Items - Double Battle Chance Boosters", () => { // Forced LURE to spawn in the first slot with override game.onNextPrompt( "SelectModifierPhase", - Mode.MODIFIER_SELECT, + UiMode.MODIFIER_SELECT, () => { const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler; // Traverse to first modifier slot diff --git a/test/items/eviolite.test.ts b/test/items/eviolite.test.ts index 43fd6a795bb..fafc0f4a10c 100644 --- a/test/items/eviolite.test.ts +++ b/test/items/eviolite.test.ts @@ -1,5 +1,5 @@ import { StatBoosterModifier } from "#app/modifier/modifier"; -import { NumberHolder, randItem } from "#app/utils"; +import { NumberHolder, randItem } from "#app/utils/common"; import { Species } from "#enums/species"; import { Stat } from "#enums/stat"; import GameManager from "#test/testUtils/gameManager"; diff --git a/test/items/exp_booster.test.ts b/test/items/exp_booster.test.ts index 3fe31e5c202..ec7528c3b23 100644 --- a/test/items/exp_booster.test.ts +++ b/test/items/exp_booster.test.ts @@ -1,6 +1,6 @@ import { Abilities } from "#app/enums/abilities"; import { PokemonExpBoosterModifier } from "#app/modifier/modifier"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import GameManager from "#test/testUtils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; diff --git a/test/items/leek.test.ts b/test/items/leek.test.ts index 5f9be882bc1..7589b89bc15 100644 --- a/test/items/leek.test.ts +++ b/test/items/leek.test.ts @@ -1,5 +1,5 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { randInt } from "#app/utils"; +import { randInt } from "#app/utils/common"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; diff --git a/test/items/light_ball.test.ts b/test/items/light_ball.test.ts index e85fb1b602b..91195d0b1e5 100644 --- a/test/items/light_ball.test.ts +++ b/test/items/light_ball.test.ts @@ -2,7 +2,7 @@ import { Stat } from "#enums/stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phase from "phaser"; diff --git a/test/items/lock_capsule.test.ts b/test/items/lock_capsule.test.ts index 9cc6046307e..19829578d87 100644 --- a/test/items/lock_capsule.test.ts +++ b/test/items/lock_capsule.test.ts @@ -2,7 +2,7 @@ import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import GameManager from "#test/testUtils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -41,7 +41,7 @@ describe("Items - Lock Capsule", () => { }), ); - game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { + game.onNextPrompt("SelectModifierPhase", UiMode.MODIFIER_SELECT, () => { const selectModifierPhase = game.scene.getCurrentPhase() as SelectModifierPhase; const rerollCost = selectModifierPhase.getRerollCost(true); expect(rerollCost).toBe(150); diff --git a/test/items/metal_powder.test.ts b/test/items/metal_powder.test.ts index 37686710848..6be7655ec70 100644 --- a/test/items/metal_powder.test.ts +++ b/test/items/metal_powder.test.ts @@ -2,7 +2,7 @@ import { Stat } from "#enums/stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phase from "phaser"; diff --git a/test/items/quick_powder.test.ts b/test/items/quick_powder.test.ts index 6937d6093f3..d77f981f04d 100644 --- a/test/items/quick_powder.test.ts +++ b/test/items/quick_powder.test.ts @@ -2,7 +2,7 @@ import { Stat } from "#enums/stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phase from "phaser"; diff --git a/test/items/temp_stat_stage_booster.test.ts b/test/items/temp_stat_stage_booster.test.ts index ccbabf01ccb..a3cfc3256bb 100644 --- a/test/items/temp_stat_stage_booster.test.ts +++ b/test/items/temp_stat_stage_booster.test.ts @@ -7,7 +7,7 @@ import { Moves } from "#app/enums/moves"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#app/enums/abilities"; import { TempStatStageBoosterModifier } from "#app/modifier/modifier"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { Button } from "#app/enums/buttons"; import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; @@ -137,7 +137,7 @@ describe("Items - Temporary Stat Stage Boosters", () => { // Forced X_ATTACK to spawn in the first slot with override game.onNextPrompt( "SelectModifierPhase", - Mode.MODIFIER_SELECT, + UiMode.MODIFIER_SELECT, () => { const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler; // Traverse to first modifier slot diff --git a/test/items/thick_club.test.ts b/test/items/thick_club.test.ts index 9e9cd2e2ec8..2a63a60a0e6 100644 --- a/test/items/thick_club.test.ts +++ b/test/items/thick_club.test.ts @@ -2,7 +2,7 @@ import { Stat } from "#enums/stat"; import { SpeciesStatBoosterModifier } from "#app/modifier/modifier"; import { modifierTypes } from "#app/modifier/modifier-type"; import i18next from "#app/plugins/i18n"; -import { NumberHolder, randInt } from "#app/utils"; +import { NumberHolder, randInt } from "#app/utils/common"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phase from "phaser"; diff --git a/test/moves/aurora_veil.test.ts b/test/moves/aurora_veil.test.ts index ef53b69b4e4..e9ab66d4203 100644 --- a/test/moves/aurora_veil.test.ts +++ b/test/moves/aurora_veil.test.ts @@ -5,7 +5,7 @@ import { allMoves, CritOnlyAttr } from "#app/data/moves/move"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import type Pokemon from "#app/field/pokemon"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/moves/belly_drum.test.ts b/test/moves/belly_drum.test.ts index f01a50f8a79..8ee1026bf20 100644 --- a/test/moves/belly_drum.test.ts +++ b/test/moves/belly_drum.test.ts @@ -1,5 +1,5 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { toDmgValue } from "#app/utils"; +import { toDmgValue } from "#app/utils/common"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { Stat } from "#enums/stat"; diff --git a/test/moves/fillet_away.test.ts b/test/moves/fillet_away.test.ts index cc462b3746a..477cdf76fc7 100644 --- a/test/moves/fillet_away.test.ts +++ b/test/moves/fillet_away.test.ts @@ -1,5 +1,5 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { toDmgValue } from "#app/utils"; +import { toDmgValue } from "#app/utils/common"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { Stat } from "#enums/stat"; diff --git a/test/moves/light_screen.test.ts b/test/moves/light_screen.test.ts index 12aeb29577a..cea26f29542 100644 --- a/test/moves/light_screen.test.ts +++ b/test/moves/light_screen.test.ts @@ -6,7 +6,7 @@ import { Abilities } from "#app/enums/abilities"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import type Pokemon from "#app/field/pokemon"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; diff --git a/test/moves/multi_target.test.ts b/test/moves/multi_target.test.ts index fccf650416c..ad47d540a14 100644 --- a/test/moves/multi_target.test.ts +++ b/test/moves/multi_target.test.ts @@ -1,7 +1,7 @@ import { BattlerIndex } from "#app/battle"; import { Abilities } from "#app/enums/abilities"; import { Species } from "#app/enums/species"; -import { toDmgValue } from "#app/utils"; +import { toDmgValue } from "#app/utils/common"; import { Moves } from "#enums/moves"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; diff --git a/test/moves/pledge_moves.test.ts b/test/moves/pledge_moves.test.ts index b3d13a27b83..2bfd408e5fb 100644 --- a/test/moves/pledge_moves.test.ts +++ b/test/moves/pledge_moves.test.ts @@ -5,7 +5,7 @@ import { allMoves, FlinchAttr } from "#app/data/moves/move"; import { PokemonType } from "#enums/pokemon-type"; import { ArenaTagType } from "#enums/arena-tag-type"; import { Stat } from "#enums/stat"; -import { toDmgValue } from "#app/utils"; +import { toDmgValue } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/moves/reflect.test.ts b/test/moves/reflect.test.ts index 473b46bf097..b8338cea8cf 100644 --- a/test/moves/reflect.test.ts +++ b/test/moves/reflect.test.ts @@ -6,7 +6,7 @@ import { Abilities } from "#app/enums/abilities"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import type Pokemon from "#app/field/pokemon"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { NumberHolder } from "#app/utils"; +import { NumberHolder } from "#app/utils/common"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; diff --git a/test/moves/revival_blessing.test.ts b/test/moves/revival_blessing.test.ts index 860ce5524d4..b36cd43eb83 100644 --- a/test/moves/revival_blessing.test.ts +++ b/test/moves/revival_blessing.test.ts @@ -1,6 +1,6 @@ import { BattlerIndex } from "#app/battle"; import { MoveResult } from "#app/field/pokemon"; -import { toDmgValue } from "#app/utils"; +import { toDmgValue } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; diff --git a/test/moves/substitute.test.ts b/test/moves/substitute.test.ts index 2e82078418b..7f4a2e69f9e 100644 --- a/test/moves/substitute.test.ts +++ b/test/moves/substitute.test.ts @@ -6,7 +6,7 @@ import { MoveResult } from "#app/field/pokemon"; import type { CommandPhase } from "#app/phases/command-phase"; import GameManager from "#test/testUtils/gameManager"; import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { Abilities } from "#enums/abilities"; import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; @@ -398,7 +398,7 @@ describe("Moves - Substitute", () => { leadPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, Moves.NONE, leadPokemon.id); // Simulate a Baton switch for the player this turn - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { (game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.POKEMON, 1, true); }); diff --git a/test/mystery-encounter/encounter-test-utils.ts b/test/mystery-encounter/encounter-test-utils.ts index 93629778e0a..977f40bc90e 100644 --- a/test/mystery-encounter/encounter-test-utils.ts +++ b/test/mystery-encounter/encounter-test-utils.ts @@ -14,8 +14,8 @@ import type MessageUiHandler from "#app/ui/message-ui-handler"; import type MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; import type PartyUiHandler from "#app/ui/party-ui-handler"; import type OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; -import { Mode } from "#app/ui/ui"; -import { isNullOrUndefined } from "#app/utils"; +import { UiMode } from "#enums/ui-mode"; +import { isNullOrUndefined } from "#app/utils/common"; import { Button } from "#enums/buttons"; import { StatusEffect } from "#enums/status-effect"; import type GameManager from "#test/testUtils/gameManager"; @@ -40,7 +40,7 @@ export async function runMysteryEncounterToEnd( // run the selected options phase game.onNextPrompt( "MysteryEncounterOptionSelectedPhase", - Mode.MESSAGE, + UiMode.MESSAGE, () => { const uiHandler = game.scene.ui.getHandler(); uiHandler.processInput(Button.ACTION); @@ -51,9 +51,9 @@ export async function runMysteryEncounterToEnd( if (isBattle) { game.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - game.setMode(Mode.MESSAGE); + game.setMode(UiMode.MESSAGE); game.endPhase(); }, () => game.isCurrentPhase(CommandPhase), @@ -61,16 +61,16 @@ export async function runMysteryEncounterToEnd( game.onNextPrompt( "CheckSwitchPhase", - Mode.MESSAGE, + UiMode.MESSAGE, () => { - game.setMode(Mode.MESSAGE); + game.setMode(UiMode.MESSAGE); game.endPhase(); }, () => game.isCurrentPhase(CommandPhase), ); // If a battle is started, fast forward to end of the battle - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { game.scene.clearPhaseQueue(); game.scene.clearPhaseQueueSplice(); game.scene.unshiftPhase(new VictoryPhase(0)); @@ -78,13 +78,13 @@ export async function runMysteryEncounterToEnd( }); // Handle end of battle trainer messages - game.onNextPrompt("TrainerVictoryPhase", Mode.MESSAGE, () => { + game.onNextPrompt("TrainerVictoryPhase", UiMode.MESSAGE, () => { const uiHandler = game.scene.ui.getHandler(); uiHandler.processInput(Button.ACTION); }); // Handle egg hatch dialogue - game.onNextPrompt("EggLapsePhase", Mode.MESSAGE, () => { + game.onNextPrompt("EggLapsePhase", UiMode.MESSAGE, () => { const uiHandler = game.scene.ui.getHandler(); uiHandler.processInput(Button.ACTION); }); @@ -103,7 +103,7 @@ export async function runSelectMysteryEncounterOption( // Handle any eventual queued messages (e.g. weather phase, etc.) game.onNextPrompt( "MessagePhase", - Mode.MESSAGE, + UiMode.MESSAGE, () => { const uiHandler = game.scene.ui.getHandler(); uiHandler.processInput(Button.ACTION); @@ -118,7 +118,7 @@ export async function runSelectMysteryEncounterOption( // dispose of intro messages game.onNextPrompt( "MysteryEncounterPhase", - Mode.MESSAGE, + UiMode.MESSAGE, () => { const uiHandler = game.scene.ui.getHandler(); uiHandler.processInput(Button.ACTION); @@ -157,7 +157,7 @@ export async function runSelectMysteryEncounterOption( async function handleSecondaryOptionSelect(game: GameManager, pokemonNo: number, optionNo?: number) { // Handle secondary option selections - const partyUiHandler = game.scene.ui.handlers[Mode.PARTY] as PartyUiHandler; + const partyUiHandler = game.scene.ui.handlers[UiMode.PARTY] as PartyUiHandler; vi.spyOn(partyUiHandler, "show"); const encounterUiHandler = game.scene.ui.getHandler(); @@ -177,7 +177,7 @@ async function handleSecondaryOptionSelect(game: GameManager, pokemonNo: number, // If there is a second choice to make after selecting a Pokemon if (!isNullOrUndefined(optionNo)) { // Wait for Summary menu to close and second options to spawn - const secondOptionUiHandler = game.scene.ui.handlers[Mode.OPTION_SELECT] as OptionSelectUiHandler; + const secondOptionUiHandler = game.scene.ui.handlers[UiMode.OPTION_SELECT] as OptionSelectUiHandler; vi.spyOn(secondOptionUiHandler, "show"); await vi.waitFor(() => expect(secondOptionUiHandler.show).toHaveBeenCalled()); @@ -206,6 +206,6 @@ export async function skipBattleRunMysteryEncounterRewardsPhase(game: GameManage }); game.scene.pushPhase(new VictoryPhase(0)); game.phaseInterceptor.superEndPhase(); - game.setMode(Mode.MESSAGE); + 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 43d582c5b70..a4c043ad13f 100644 --- a/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts +++ b/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts @@ -117,10 +117,8 @@ describe("A Trainer's Test - Mystery Encounter", () => { i18next.t("trainerNames:marley"), i18next.t("trainerNames:mira"), i18next.t("trainerNames:riley"), - ] - .map(name => name.toLowerCase()) - .includes(scene.currentBattle.trainer!.config.name), - ).toBeTruthy(); + ].map(name => name.toLowerCase()), + ).toContain(scene.currentBattle.trainer!.config.name.toLowerCase()); expect(enemyField[0]).toBeDefined(); }); diff --git a/test/mystery-encounter/encounters/berries-abound-encounter.test.ts b/test/mystery-encounter/encounters/berries-abound-encounter.test.ts index e19726f49fd..3f85b0b89d9 100644 --- a/test/mystery-encounter/encounters/berries-abound-encounter.test.ts +++ b/test/mystery-encounter/encounters/berries-abound-encounter.test.ts @@ -9,7 +9,7 @@ import { skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; import type BattleScene from "#app/battle-scene"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { BerryModifier } from "#app/modifier/modifier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -153,7 +153,7 @@ describe("Berries Abound - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -238,7 +238,7 @@ describe("Berries Abound - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; 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 9befe77e688..fc208ed7180 100644 --- a/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts +++ b/test/mystery-encounter/encounters/bug-type-superfan-encounter.test.ts @@ -12,7 +12,7 @@ import { import { Moves } from "#enums/moves"; import type BattleScene from "#app/battle-scene"; import { PokemonMove } from "#app/field/pokemon"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; @@ -364,7 +364,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterRewardsPhase.name); game.phaseInterceptor["prompts"] = []; // Clear out prompt handlers - game.onNextPrompt("MysteryEncounterRewardsPhase", Mode.OPTION_SELECT, () => { + game.onNextPrompt("MysteryEncounterRewardsPhase", UiMode.OPTION_SELECT, () => { game.phaseInterceptor.superEndPhase(); }); await game.phaseInterceptor.run(MysteryEncounterRewardsPhase); @@ -416,7 +416,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -432,7 +432,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -454,7 +454,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -478,7 +478,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -554,7 +554,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; diff --git a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts index 4bbe76e5c72..afc4a83e9bf 100644 --- a/test/mystery-encounter/encounters/clowning-around-encounter.test.ts +++ b/test/mystery-encounter/encounters/clowning-around-encounter.test.ts @@ -16,7 +16,7 @@ import { Moves } from "#enums/moves"; import type BattleScene from "#app/battle-scene"; import type Pokemon from "#app/field/pokemon"; import { PokemonMove } from "#app/field/pokemon"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; @@ -206,14 +206,14 @@ describe("Clowning Around - Mystery Encounter", () => { await game.phaseInterceptor.run(SelectModifierPhase); const abilityToTrain = scene.currentBattle.mysteryEncounter?.misc.ability; - game.onNextPrompt("PostMysteryEncounterPhase", Mode.MESSAGE, () => { + game.onNextPrompt("PostMysteryEncounterPhase", UiMode.MESSAGE, () => { game.scene.ui.getHandler().processInput(Button.ACTION); }); // Run to ability train option selection - const optionSelectUiHandler = game.scene.ui.handlers[Mode.OPTION_SELECT] as OptionSelectUiHandler; + const optionSelectUiHandler = game.scene.ui.handlers[UiMode.OPTION_SELECT] as OptionSelectUiHandler; vi.spyOn(optionSelectUiHandler, "show"); - const partyUiHandler = game.scene.ui.handlers[Mode.PARTY] as PartyUiHandler; + const partyUiHandler = game.scene.ui.handlers[UiMode.PARTY] as PartyUiHandler; vi.spyOn(partyUiHandler, "show"); game.endPhase(); await game.phaseInterceptor.to(PostMysteryEncounterPhase); diff --git a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts index 77cd65e51b9..873bed2f213 100644 --- a/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts +++ b/test/mystery-encounter/encounters/dancing-lessons-encounter.test.ts @@ -15,7 +15,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; import { Moves } from "#enums/moves"; import { DancingLessonsEncounter } from "#app/data/mystery-encounters/encounters/dancing-lessons-encounter"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { PokemonMove } from "#app/field/pokemon"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; @@ -132,7 +132,7 @@ describe("Dancing Lessons - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; 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 d4b0de30535..2488d12dad1 100644 --- a/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts +++ b/test/mystery-encounter/encounters/department-store-sale-encounter.test.ts @@ -7,7 +7,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 { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { DepartmentStoreSaleEncounter } from "#app/data/mystery-encounters/encounters/department-store-sale-encounter"; import { CIVILIZATION_ENCOUNTER_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; @@ -98,7 +98,7 @@ describe("Department Store Sale - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -135,7 +135,7 @@ describe("Department Store Sale - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -175,7 +175,7 @@ describe("Department Store Sale - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -215,7 +215,7 @@ describe("Department Store Sale - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; diff --git a/test/mystery-encounter/encounters/field-trip-encounter.test.ts b/test/mystery-encounter/encounters/field-trip-encounter.test.ts index 8bd35d6013f..75a6fe77492 100644 --- a/test/mystery-encounter/encounters/field-trip-encounter.test.ts +++ b/test/mystery-encounter/encounters/field-trip-encounter.test.ts @@ -12,7 +12,7 @@ import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encount import { FieldTripEncounter } from "#app/data/mystery-encounters/encounters/field-trip-encounter"; import { Moves } from "#enums/moves"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import i18next from "i18next"; @@ -88,7 +88,7 @@ describe("Field Trip - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1, optionNo: 2 }); await game.phaseInterceptor.to(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -100,7 +100,7 @@ describe("Field Trip - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1, optionNo: 1 }); await game.phaseInterceptor.to(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -149,7 +149,7 @@ describe("Field Trip - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 1 }); await game.phaseInterceptor.to(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -161,7 +161,7 @@ describe("Field Trip - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1, optionNo: 2 }); await game.phaseInterceptor.to(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -210,7 +210,7 @@ describe("Field Trip - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 }); await game.phaseInterceptor.to(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -223,7 +223,7 @@ describe("Field Trip - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 3 }); await game.phaseInterceptor.to(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; 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 d233e72932a..d47266268ee 100644 --- a/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts +++ b/test/mystery-encounter/encounters/fight-or-flight-encounter.test.ts @@ -12,7 +12,7 @@ import { import { Moves } from "#enums/moves"; import type BattleScene from "#app/battle-scene"; import { PokemonMove } from "#app/field/pokemon"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; @@ -126,7 +126,7 @@ describe("Fight or Flight - Mystery Encounter", () => { await game.phaseInterceptor.to(SelectModifierPhase, false); expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, @@ -186,7 +186,7 @@ describe("Fight or Flight - Mystery Encounter", () => { await game.phaseInterceptor.to(SelectModifierPhase, false); expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, 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 4bb44c4d19e..f8375c1aa78 100644 --- a/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts +++ b/test/mystery-encounter/encounters/fun-and-games-encounter.test.ts @@ -10,7 +10,7 @@ import { runSelectMysteryEncounterOption, } from "#test/mystery-encounter/encounter-test-utils"; import type BattleScene from "#app/battle-scene"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; @@ -147,7 +147,7 @@ describe("Fun And Games! - Mystery Encounter", () => { expect(scene.getEnemyPokemon()?.ivs).toEqual([0, 0, 0, 0, 0, 0]); expect(scene.getEnemyPokemon()?.nature).toBe(Nature.MILD); - game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { + game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.endPhase(); }); @@ -173,7 +173,7 @@ describe("Fun And Games! - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); - game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { + game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.endPhase(); }); @@ -186,7 +186,7 @@ describe("Fun And Games! - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -200,7 +200,7 @@ describe("Fun And Games! - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); - game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { + game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.endPhase(); }); @@ -215,7 +215,7 @@ describe("Fun And Games! - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -230,7 +230,7 @@ describe("Fun And Games! - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); - game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { + game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.endPhase(); }); @@ -245,7 +245,7 @@ describe("Fun And Games! - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -260,7 +260,7 @@ describe("Fun And Games! - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); - game.onNextPrompt("MessagePhase", Mode.MESSAGE, () => { + game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.endPhase(); }); @@ -275,7 +275,7 @@ describe("Fun And Games! - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; 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 f68561c2286..576e99c4e18 100644 --- a/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts +++ b/test/mystery-encounter/encounters/global-trade-system-encounter.test.ts @@ -15,10 +15,10 @@ import { modifierTypes } from "#app/modifier/modifier-type"; import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter"; import { CIVILIZATION_ENCOUNTER_BIOMES } from "#app/data/mystery-encounters/mystery-encounters"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { ModifierTier } from "#app/modifier/modifier-tier"; -import * as Utils from "#app/utils"; +import * as Utils from "#app/utils/common"; const namespace = "mysteryEncounters/globalTradeSystem"; const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA]; @@ -231,7 +231,7 @@ describe("Global Trade System - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; diff --git a/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts b/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts index f620cbd6c36..2c61d03b29d 100644 --- a/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts +++ b/test/mystery-encounter/encounters/mysterious-challengers-encounter.test.ts @@ -10,7 +10,7 @@ import { skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; import type BattleScene from "#app/battle-scene"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; @@ -166,7 +166,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -210,7 +210,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -267,7 +267,7 @@ describe("Mysterious Challengers - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; diff --git a/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts b/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts index 85c823038e8..4ff94c5a9bd 100644 --- a/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts +++ b/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts @@ -10,7 +10,7 @@ import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import GameManager from "#test/testUtils/gameManager"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { @@ -301,7 +301,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; 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 a9e6a339d36..e3440aee9e0 100644 --- a/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-strong-stuff-encounter.test.ts @@ -18,7 +18,7 @@ import { Nature } from "#enums/nature"; import { BerryType } from "#enums/berry-type"; import { BattlerTagType } from "#enums/battler-tag-type"; import { PokemonMove } from "#app/field/pokemon"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { BerryModifier, PokemonBaseStatTotalModifier } from "#app/modifier/modifier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; @@ -236,7 +236,7 @@ describe("The Strong Stuff - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; 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 94c8141aa1e..4cb712ce779 100644 --- a/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts +++ b/test/mystery-encounter/encounters/the-winstrate-challenge-encounter.test.ts @@ -7,7 +7,7 @@ import GameManager from "#test/testUtils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils"; import type BattleScene from "#app/battle-scene"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils"; @@ -299,7 +299,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -341,7 +341,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -359,7 +359,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => { async function skipBattleToNextBattle(game: GameManager, isFinalBattle = false) { game.scene.clearPhaseQueue(); game.scene.clearPhaseQueueSplice(); - const commandUiHandler = game.scene.ui.handlers[Mode.COMMAND]; + const commandUiHandler = game.scene.ui.handlers[UiMode.COMMAND]; commandUiHandler.clear(); game.scene.getEnemyParty().forEach(p => { p.hp = 0; 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 df7bbb9f424..2f910a9250f 100644 --- a/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts +++ b/test/mystery-encounter/encounters/trash-to-treasure-encounter.test.ts @@ -20,8 +20,8 @@ import { CommandPhase } from "#app/phases/command-phase"; import { MovePhase } from "#app/phases/move-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { Mode } from "#app/ui/ui"; -import * as Utils from "#app/utils"; +import { UiMode } from "#enums/ui-mode"; +import * as Utils from "#app/utils/common"; import { Moves } from "#enums/moves"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; @@ -246,7 +246,7 @@ describe("Trash to Treasure - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; diff --git a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts index fbb88e346a8..f51ab45e4d4 100644 --- a/test/mystery-encounter/encounters/weird-dream-encounter.test.ts +++ b/test/mystery-encounter/encounters/weird-dream-encounter.test.ts @@ -10,7 +10,7 @@ import { skipBattleRunMysteryEncounterRewardsPhase, } from "#test/mystery-encounter/encounter-test-utils"; import type BattleScene from "#app/battle-scene"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; @@ -144,7 +144,7 @@ describe("Weird Dream - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -200,7 +200,7 @@ describe("Weird Dream - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; diff --git a/test/phases/learn-move-phase.test.ts b/test/phases/learn-move-phase.test.ts index 55b9d8b79d4..019b833d386 100644 --- a/test/phases/learn-move-phase.test.ts +++ b/test/phases/learn-move-phase.test.ts @@ -4,7 +4,7 @@ import GameManager from "#test/testUtils/gameManager"; import { Species } from "#enums/species"; import { Moves } from "#enums/moves"; import { LearnMovePhase } from "#app/phases/learn-move-phase"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { Button } from "#app/enums/buttons"; describe("Learn Move Phase", () => { @@ -52,10 +52,10 @@ describe("Learn Move Phase", () => { await game.doKillOpponents(); // queue up inputs to confirm dialog boxes - game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => { + game.onNextPrompt("LearnMovePhase", UiMode.CONFIRM, () => { game.scene.ui.processInput(Button.ACTION); }); - game.onNextPrompt("LearnMovePhase", Mode.SUMMARY, () => { + game.onNextPrompt("LearnMovePhase", UiMode.SUMMARY, () => { for (let x = 0; x < moveSlotNum; x++) { game.scene.ui.processInput(Button.DOWN); } @@ -84,16 +84,16 @@ describe("Learn Move Phase", () => { await game.doKillOpponents(); // queue up inputs to confirm dialog boxes - game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => { + game.onNextPrompt("LearnMovePhase", UiMode.CONFIRM, () => { game.scene.ui.processInput(Button.ACTION); }); - game.onNextPrompt("LearnMovePhase", Mode.SUMMARY, () => { + game.onNextPrompt("LearnMovePhase", UiMode.SUMMARY, () => { for (let x = 0; x < 4; x++) { game.scene.ui.processInput(Button.DOWN); // moves down 4 times to the 5th move slot } game.scene.ui.processInput(Button.ACTION); }); - game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => { + game.onNextPrompt("LearnMovePhase", UiMode.CONFIRM, () => { game.scene.ui.processInput(Button.ACTION); }); await game.phaseInterceptor.to(LearnMovePhase); diff --git a/test/phases/mystery-encounter-phase.test.ts b/test/phases/mystery-encounter-phase.test.ts index f903932d2cb..34078b65039 100644 --- a/test/phases/mystery-encounter-phase.test.ts +++ b/test/phases/mystery-encounter-phase.test.ts @@ -3,7 +3,7 @@ import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { Species } from "#enums/species"; import { MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { Button } from "#enums/buttons"; import type MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; @@ -50,7 +50,7 @@ describe("Mystery Encounter Phases", () => { Species.VOLCARONA, ]); - game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => { + game.onNextPrompt("MysteryEncounterPhase", UiMode.MYSTERY_ENCOUNTER, () => { // End phase early for test game.phaseInterceptor.superEndPhase(); }); @@ -61,7 +61,7 @@ describe("Mystery Encounter Phases", () => { MysteryEncounterType.MYSTERIOUS_CHALLENGERS, ); expect(game.scene.mysteryEncounterSaveData.encounteredEvents[0].tier).toEqual(MysteryEncounterTier.GREAT); - expect(game.scene.ui.getMode()).toBe(Mode.MYSTERY_ENCOUNTER); + expect(game.scene.ui.getMode()).toBe(UiMode.MYSTERY_ENCOUNTER); }); it("Selects an option for MysteryEncounterPhase", async () => { @@ -73,7 +73,7 @@ describe("Mystery Encounter Phases", () => { Species.VOLCARONA, ]); - game.onNextPrompt("MysteryEncounterPhase", Mode.MESSAGE, () => { + game.onNextPrompt("MysteryEncounterPhase", UiMode.MESSAGE, () => { const handler = game.scene.ui.getHandler() as MessageUiHandler; handler.processInput(Button.ACTION); }); @@ -89,7 +89,7 @@ describe("Mystery Encounter Phases", () => { await vi.waitFor(() => expect(game.scene.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterOptionSelectedPhase.name), ); - expect(ui.getMode()).toBe(Mode.MESSAGE); + expect(ui.getMode()).toBe(UiMode.MESSAGE); expect(ui.showDialogue).toHaveBeenCalledTimes(1); expect(ui.showText).toHaveBeenCalledTimes(2); expect(ui.showDialogue).toHaveBeenCalledWith( diff --git a/test/phases/phases.test.ts b/test/phases/phases.test.ts index 96225c9151c..2483cfb317f 100644 --- a/test/phases/phases.test.ts +++ b/test/phases/phases.test.ts @@ -2,7 +2,7 @@ import type BattleScene from "#app/battle-scene"; import { LoginPhase } from "#app/phases/login-phase"; import { TitlePhase } from "#app/phases/title-phase"; import { UnavailablePhase } from "#app/phases/unavailable-phase"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -32,7 +32,7 @@ describe("Phases", () => { const loginPhase = new LoginPhase(); scene.unshiftPhase(loginPhase); await game.phaseInterceptor.to(LoginPhase); - expect(scene.ui.getMode()).to.equal(Mode.MESSAGE); + expect(scene.ui.getMode()).to.equal(UiMode.MESSAGE); }); }); @@ -41,7 +41,7 @@ describe("Phases", () => { const titlePhase = new TitlePhase(); scene.unshiftPhase(titlePhase); await game.phaseInterceptor.to(TitlePhase); - expect(scene.ui.getMode()).to.equal(Mode.TITLE); + expect(scene.ui.getMode()).to.equal(UiMode.TITLE); }); }); @@ -50,7 +50,7 @@ describe("Phases", () => { const unavailablePhase = new UnavailablePhase(); scene.unshiftPhase(unavailablePhase); await game.phaseInterceptor.to(UnavailablePhase); - expect(scene.ui.getMode()).to.equal(Mode.UNAVAILABLE); + 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 d352acea77a..85f8b472c4a 100644 --- a/test/phases/select-modifier-phase.test.ts +++ b/test/phases/select-modifier-phase.test.ts @@ -6,8 +6,8 @@ import type { CustomModifierSettings } from "#app/modifier/modifier-type"; import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { Mode } from "#app/ui/ui"; -import { shiftCharCodes } from "#app/utils"; +import { UiMode } from "#enums/ui-mode"; +import { shiftCharCodes } from "#app/utils/common"; import { Abilities } from "#enums/abilities"; import { Button } from "#enums/buttons"; import { Moves } from "#enums/moves"; @@ -51,7 +51,7 @@ describe("SelectModifierPhase", () => { scene.unshiftPhase(selectModifierPhase); await game.phaseInterceptor.to(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); }); it("should generate random modifiers", async () => { @@ -59,7 +59,7 @@ describe("SelectModifierPhase", () => { game.move.select(Moves.FISSURE); await game.phaseInterceptor.to("SelectModifierPhase"); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -97,7 +97,7 @@ describe("SelectModifierPhase", () => { // TODO: nagivate the ui to reroll somehow //const smphase = scene.getCurrentPhase() as SelectModifierPhase; - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -106,7 +106,7 @@ describe("SelectModifierPhase", () => { modifierSelectHandler.processInput(Button.ACTION); expect(scene.money).toBe(1000000 - 250); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(modifierSelectHandler.options.length).toEqual(3); }); @@ -125,7 +125,7 @@ describe("SelectModifierPhase", () => { game.move.select(Moves.FISSURE); await game.phaseInterceptor.to("SelectModifierPhase"); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -134,7 +134,7 @@ describe("SelectModifierPhase", () => { // TODO: nagivate ui to reroll with lock capsule enabled - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(modifierSelectHandler.options.length).toEqual(3); // Reroll with lock can still upgrade expect( @@ -168,7 +168,7 @@ describe("SelectModifierPhase", () => { game.move.select(Moves.SPLASH); await game.phaseInterceptor.to("SelectModifierPhase"); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -205,7 +205,7 @@ describe("SelectModifierPhase", () => { game.move.select(Moves.SPLASH); await game.phaseInterceptor.to("SelectModifierPhase"); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -244,7 +244,7 @@ describe("SelectModifierPhase", () => { game.move.select(Moves.SPLASH); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; @@ -268,7 +268,7 @@ describe("SelectModifierPhase", () => { game.move.select(Moves.SPLASH); await game.phaseInterceptor.run(SelectModifierPhase); - expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); + expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find( h => h instanceof ModifierSelectUiHandler, ) as ModifierSelectUiHandler; diff --git a/test/plugins/api/pokerogue-account-api.test.ts b/test/plugins/api/pokerogue-account-api.test.ts index e7e1b2d52b0..3c37451960a 100644 --- a/test/plugins/api/pokerogue-account-api.test.ts +++ b/test/plugins/api/pokerogue-account-api.test.ts @@ -2,7 +2,8 @@ import type { AccountInfoResponse } from "#app/@types/PokerogueAccountApi"; import { SESSION_ID_COOKIE_NAME } from "#app/constants"; import { PokerogueAccountApi } from "#app/plugins/api/pokerogue-account-api"; import { getApiBaseUrl } from "#test/testUtils/testUtils"; -import * as Utils from "#app/utils"; +import * as CookieUtils from "#app/utils/cookies"; +import * as cookies from "#app/utils/cookies"; import { http, HttpResponse } from "msw"; import { beforeAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { initServerForApiTests } from "#test/testUtils/testFileInitialization"; @@ -98,13 +99,13 @@ describe("Pokerogue Account API", () => { const loginParams = { username: "test", password: "test" }; it("should return null and set the cookie on SUCCESS", async () => { - vi.spyOn(Utils, "setCookie"); + vi.spyOn(CookieUtils, "setCookie"); server.use(http.post(`${apiBase}/account/login`, () => HttpResponse.json({ token: "abctest" }))); const error = await accountApi.login(loginParams); expect(error).toBeNull(); - expect(Utils.setCookie).toHaveBeenCalledWith(SESSION_ID_COOKIE_NAME, "abctest"); + expect(cookies.setCookie).toHaveBeenCalledWith(SESSION_ID_COOKIE_NAME, "abctest"); }); it("should return error message and report a warning on FAILURE", async () => { @@ -130,16 +131,16 @@ describe("Pokerogue Account API", () => { describe("Logout", () => { beforeEach(() => { - vi.spyOn(Utils, "removeCookie"); + vi.spyOn(CookieUtils, "removeCookie"); }); it("should remove cookie on success", async () => { - vi.spyOn(Utils, "setCookie"); + vi.spyOn(CookieUtils, "setCookie"); server.use(http.get(`${apiBase}/account/logout`, () => new HttpResponse("", { status: 200 }))); await accountApi.logout(); - expect(Utils.removeCookie).toHaveBeenCalledWith(SESSION_ID_COOKIE_NAME); + expect(cookies.removeCookie).toHaveBeenCalledWith(SESSION_ID_COOKIE_NAME); }); it("should report a warning on and remove cookie on FAILURE", async () => { @@ -147,7 +148,7 @@ describe("Pokerogue Account API", () => { await accountApi.logout(); - expect(Utils.removeCookie).toHaveBeenCalledWith(SESSION_ID_COOKIE_NAME); + expect(cookies.removeCookie).toHaveBeenCalledWith(SESSION_ID_COOKIE_NAME); expect(console.warn).toHaveBeenCalledWith("Log out failed!", expect.any(Error)); }); @@ -156,7 +157,7 @@ describe("Pokerogue Account API", () => { await accountApi.logout(); - expect(Utils.removeCookie).toHaveBeenCalledWith(SESSION_ID_COOKIE_NAME); + expect(cookies.removeCookie).toHaveBeenCalledWith(SESSION_ID_COOKIE_NAME); expect(console.warn).toHaveBeenCalledWith("Log out failed!", expect.any(Error)); }); }); diff --git a/test/reload.test.ts b/test/reload.test.ts index c69c0f9f484..93823e06cce 100644 --- a/test/reload.test.ts +++ b/test/reload.test.ts @@ -1,7 +1,7 @@ import { GameModes } from "#app/game-mode"; import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; import type OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { Biome } from "#enums/biome"; import { Button } from "#enums/buttons"; import { Moves } from "#enums/moves"; @@ -58,7 +58,7 @@ describe("Reload", () => { // Transition from Wave 10 to Wave 11 in order to trigger biome switch game.move.select(Moves.SPLASH); await game.doKillOpponents(); - game.onNextPrompt("SelectBiomePhase", Mode.OPTION_SELECT, () => { + game.onNextPrompt("SelectBiomePhase", UiMode.OPTION_SELECT, () => { (game.scene.time as MockClock).overrideDelay = null; const optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; game.scene.time.delayedCall(1010, () => optionSelectUiHandler.processInput(Button.ACTION)); diff --git a/test/settingMenu/rebinding_setting.test.ts b/test/settingMenu/rebinding_setting.test.ts index 28b5d73d7cc..45c647248c4 100644 --- a/test/settingMenu/rebinding_setting.test.ts +++ b/test/settingMenu/rebinding_setting.test.ts @@ -2,7 +2,7 @@ import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty"; import { getKeyWithKeycode, getKeyWithSettingName } from "#app/configs/inputs/configHandler"; import type { InterfaceConfig } from "#app/inputs-controller"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; -import { deepCopy } from "#app/utils"; +import { deepCopy } from "#app/utils/common"; import { Button } from "#enums/buttons"; import { Device } from "#enums/devices"; import { InGameManip } from "#test/settingMenu/helpers/inGameManip"; diff --git a/test/system/game_data.test.ts b/test/system/game_data.test.ts index 94e82949fe6..900fb672320 100644 --- a/test/system/game_data.test.ts +++ b/test/system/game_data.test.ts @@ -1,4 +1,4 @@ -import * as BattleScene from "#app/battle-scene"; +import * as bypassLoginModule from "#app/global-vars/bypass-login"; import { pokerogueApi } from "#app/plugins/api/pokerogue-api"; import type { SessionSaveData } from "#app/system/game-data"; import { Abilities } from "#enums/abilities"; @@ -33,13 +33,13 @@ describe("System - Game Data", () => { describe("tryClearSession", () => { beforeEach(() => { - vi.spyOn(BattleScene, "bypassLogin", "get").mockReturnValue(false); + vi.spyOn(bypassLoginModule, "bypassLogin", "get").mockReturnValue(false); vi.spyOn(game.scene.gameData, "getSessionSaveData").mockReturnValue({} as SessionSaveData); vi.spyOn(account, "updateUserInfo").mockImplementation(async () => [true, 1]); }); it("should return [true, true] if bypassLogin is true", async () => { - vi.spyOn(BattleScene, "bypassLogin", "get").mockReturnValue(true); + vi.spyOn(bypassLoginModule, "bypassLogin", "get").mockReturnValue(true); const result = await game.scene.gameData.tryClearSession(0); diff --git a/test/testUtils/gameManager.ts b/test/testUtils/gameManager.ts index 390e71af126..874d8f786b8 100644 --- a/test/testUtils/gameManager.ts +++ b/test/testUtils/gameManager.ts @@ -30,8 +30,8 @@ import type CommandUiHandler from "#app/ui/command-ui-handler"; import type ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import type PartyUiHandler from "#app/ui/party-ui-handler"; import type TargetSelectUiHandler from "#app/ui/target-select-ui-handler"; -import { Mode } from "#app/ui/ui"; -import { isNullOrUndefined } from "#app/utils"; +import { UiMode } from "#enums/ui-mode"; +import { isNullOrUndefined } from "#app/utils/common"; import { BattleStyle } from "#enums/battle-style"; import { Button } from "#enums/buttons"; import { ExpGainsSpeed } from "#enums/exp-gains-speed"; @@ -102,7 +102,7 @@ export default class GameManager { if (!firstTimeScene) { this.scene.reset(false, true); - (this.scene.ui.handlers[Mode.STARTER_SELECT] as StarterSelectUiHandler).clearStarterPreferences(); + (this.scene.ui.handlers[UiMode.STARTER_SELECT] as StarterSelectUiHandler).clearStarterPreferences(); this.scene.clearAllPhases(); // Must be run after phase interceptor has been initialized. @@ -135,7 +135,7 @@ export default class GameManager { * Sets the game mode. * @param mode - The mode to set. */ - setMode(mode: Mode) { + setMode(mode: UiMode) { this.scene.ui?.setMode(mode); } @@ -144,7 +144,7 @@ export default class GameManager { * @param mode - The mode to wait for. * @returns A promise that resolves when the mode is set. */ - waitMode(mode: Mode): Promise { + waitMode(mode: UiMode): Promise { return new Promise(async resolve => { await waitUntil(() => this.scene.ui?.getMode() === mode); return resolve(); @@ -168,7 +168,7 @@ export default class GameManager { */ onNextPrompt( phaseTarget: string, - mode: Mode, + mode: UiMode, callback: () => void, expireFn?: () => void, awaitingActionInput = false, @@ -208,7 +208,7 @@ export default class GameManager { console.log("===to final boss encounter==="); await this.runToTitle(); - this.onNextPrompt("TitlePhase", Mode.TITLE, () => { + this.onNextPrompt("TitlePhase", UiMode.TITLE, () => { this.scene.gameMode = getGameMode(mode); const starters = generateStarter(this.scene, species); const selectStarterPhase = new SelectStarterPhase(); @@ -243,7 +243,7 @@ export default class GameManager { this.onNextPrompt( "TitlePhase", - Mode.TITLE, + UiMode.TITLE, () => { this.scene.gameMode = getGameMode(GameModes.CLASSIC); const starters = generateStarter(this.scene, species); @@ -256,7 +256,7 @@ export default class GameManager { this.onNextPrompt( "EncounterPhase", - Mode.MESSAGE, + UiMode.MESSAGE, () => { const handler = this.scene.ui.getHandler() as BattleMessageUiHandler; handler.processInput(Button.ACTION); @@ -284,9 +284,9 @@ export default class GameManager { if (this.scene.battleStyle === BattleStyle.SWITCH) { this.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - this.setMode(Mode.MESSAGE); + this.setMode(UiMode.MESSAGE); this.endPhase(); }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(TurnInitPhase), @@ -294,9 +294,9 @@ export default class GameManager { this.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - this.setMode(Mode.MESSAGE); + this.setMode(UiMode.MESSAGE); this.endPhase(); }, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(TurnInitPhase), @@ -316,7 +316,7 @@ export default class GameManager { selectTarget(movePosition: number, targetIndex?: BattlerIndex) { this.onNextPrompt( "SelectTargetPhase", - Mode.TARGET_SELECT, + UiMode.TARGET_SELECT, () => { const handler = this.scene.ui.getHandler() as TargetSelectUiHandler; const move = (this.scene.getCurrentPhase() as SelectTargetPhase) @@ -351,7 +351,7 @@ export default class GameManager { doSelectModifier() { this.onNextPrompt( "SelectModifierPhase", - Mode.MODIFIER_SELECT, + UiMode.MODIFIER_SELECT, () => { const handler = this.scene.ui.getHandler() as ModifierSelectUiHandler; handler.processInput(Button.CANCEL); @@ -365,7 +365,7 @@ export default class GameManager { this.onNextPrompt( "SelectModifierPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { const handler = this.scene.ui.getHandler() as ModifierSelectUiHandler; handler.processInput(Button.ACTION); @@ -427,9 +427,9 @@ export default class GameManager { this.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - this.setMode(Mode.MESSAGE); + this.setMode(UiMode.MESSAGE); this.endPhase(); }, () => this.isCurrentPhase(TurnInitPhase), @@ -461,7 +461,7 @@ export default class GameManager { * @param mode - The target mode. * @returns True if the current mode matches the target mode, otherwise false. */ - isCurrentMode(mode: Mode) { + isCurrentMode(mode: UiMode) { return this.scene.ui?.getMode() === mode; } @@ -516,7 +516,7 @@ export default class GameManager { * @param pokemonIndex the index of the pokemon in your party to switch to */ doSwitchPokemon(pokemonIndex: number) { - this.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + this.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { (this.scene.ui.getHandler() as CommandUiHandler).setCursor(2); (this.scene.ui.getHandler() as CommandUiHandler).processInput(Button.ACTION); }); @@ -545,7 +545,7 @@ export default class GameManager { * non-command switch actions happen in SwitchPhase. */ doSelectPartyPokemon(slot: number, inPhase = "SwitchPhase") { - this.onNextPrompt(inPhase, Mode.PARTY, () => { + this.onNextPrompt(inPhase, UiMode.PARTY, () => { const partyHandler = this.scene.ui.getHandler() as PartyUiHandler; partyHandler.setCursor(slot); @@ -560,12 +560,12 @@ export default class GameManager { * @param ballIndex the index of the pokeball to throw */ public doThrowPokeball(ballIndex: number) { - this.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + this.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { (this.scene.ui.getHandler() as CommandUiHandler).setCursor(1); (this.scene.ui.getHandler() as CommandUiHandler).processInput(Button.ACTION); }); - this.onNextPrompt("CommandPhase", Mode.BALL, () => { + this.onNextPrompt("CommandPhase", UiMode.BALL, () => { const ballHandler = this.scene.ui.getHandler() as BallUiHandler; ballHandler.setCursor(ballIndex); ballHandler.processInput(Button.ACTION); // select ball and throw diff --git a/test/testUtils/gameWrapper.ts b/test/testUtils/gameWrapper.ts index 02865701ed0..050e9f13257 100644 --- a/test/testUtils/gameWrapper.ts +++ b/test/testUtils/gameWrapper.ts @@ -1,8 +1,9 @@ // @ts-nocheck - TODO: remove this -import BattleScene, * as battleScene from "#app/battle-scene"; +import BattleScene from "#app/battle-scene"; import { MoveAnim } from "#app/data/battle-anims"; import Pokemon from "#app/field/pokemon"; -import { setCookie, sessionIdKey } from "#app/utils"; +import { sessionIdKey } from "#app/utils/common"; +import { setCookie } from "#app/utils/cookies"; import { blobToString } from "#test/testUtils/gameManagerUtils"; import { MockClock } from "#test/testUtils/mocks/mockClock"; import { MockFetch } from "#test/testUtils/mocks/mockFetch"; @@ -20,6 +21,8 @@ import KeyboardPlugin = Phaser.Input.Keyboard.KeyboardPlugin; import GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin; import EventEmitter = Phaser.Events.EventEmitter; import UpdateList = Phaser.GameObjects.UpdateList; +// biome-ignore lint/style/noNamespaceImport: Necessary in order to mock the var +import * as bypassLoginModule from "#app/global-vars/bypass-login"; window.URL.createObjectURL = (blob: Blob) => { blobToString(blob).then((data: string) => { @@ -43,7 +46,7 @@ export default class GameWrapper { Phaser.Math.RND.sow(["test"]); // vi.spyOn(Utils, "apiFetch", "get").mockReturnValue(fetch); if (bypassLogin) { - vi.spyOn(battleScene, "bypassLogin", "get").mockReturnValue(true); + vi.spyOn(bypassLoginModule, "bypassLogin", "get").mockReturnValue(true); } this.game = phaserGame; MoveAnim.prototype.getAnim = () => ({ diff --git a/test/testUtils/helpers/challengeModeHelper.ts b/test/testUtils/helpers/challengeModeHelper.ts index 0b7826eda7e..3a4f2adcd09 100644 --- a/test/testUtils/helpers/challengeModeHelper.ts +++ b/test/testUtils/helpers/challengeModeHelper.ts @@ -3,7 +3,7 @@ import type { Species } from "#app/enums/species"; import overrides from "#app/overrides"; import { EncounterPhase } from "#app/phases/encounter-phase"; import { SelectStarterPhase } from "#app/phases/select-starter-phase"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { generateStarter } from "../gameManagerUtils"; import { GameManagerHelper } from "./gameManagerHelper"; import type { Challenge } from "#app/data/challenge"; @@ -41,7 +41,7 @@ export class ChallengeModeHelper extends GameManagerHelper { this.game.override.shiny(false).enemyShiny(false); } - this.game.onNextPrompt("TitlePhase", Mode.TITLE, () => { + this.game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { this.game.scene.gameMode.challenges = this.challenges; const starters = generateStarter(this.game.scene, species); const selectStarterPhase = new SelectStarterPhase(); @@ -66,9 +66,9 @@ export class ChallengeModeHelper extends GameManagerHelper { if (this.game.scene.battleStyle === BattleStyle.SWITCH) { this.game.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - this.game.setMode(Mode.MESSAGE); + this.game.setMode(UiMode.MESSAGE); this.game.endPhase(); }, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase), @@ -76,9 +76,9 @@ export class ChallengeModeHelper extends GameManagerHelper { this.game.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - this.game.setMode(Mode.MESSAGE); + this.game.setMode(UiMode.MESSAGE); this.game.endPhase(); }, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase), diff --git a/test/testUtils/helpers/classicModeHelper.ts b/test/testUtils/helpers/classicModeHelper.ts index 5b6a38f5747..8e1ac95c733 100644 --- a/test/testUtils/helpers/classicModeHelper.ts +++ b/test/testUtils/helpers/classicModeHelper.ts @@ -6,7 +6,7 @@ import { CommandPhase } from "#app/phases/command-phase"; import { EncounterPhase } from "#app/phases/encounter-phase"; import { SelectStarterPhase } from "#app/phases/select-starter-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { generateStarter } from "../gameManagerUtils"; import { GameManagerHelper } from "./gameManagerHelper"; @@ -26,7 +26,7 @@ export class ClassicModeHelper extends GameManagerHelper { this.game.override.shiny(false).enemyShiny(false); } - this.game.onNextPrompt("TitlePhase", Mode.TITLE, () => { + this.game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { this.game.scene.gameMode = getGameMode(GameModes.CLASSIC); const starters = generateStarter(this.game.scene, species); const selectStarterPhase = new SelectStarterPhase(); @@ -51,9 +51,9 @@ export class ClassicModeHelper extends GameManagerHelper { if (this.game.scene.battleStyle === BattleStyle.SWITCH) { this.game.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - this.game.setMode(Mode.MESSAGE); + this.game.setMode(UiMode.MESSAGE); this.game.endPhase(); }, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase), @@ -61,9 +61,9 @@ export class ClassicModeHelper extends GameManagerHelper { this.game.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - this.game.setMode(Mode.MESSAGE); + this.game.setMode(UiMode.MESSAGE); this.game.endPhase(); }, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase), diff --git a/test/testUtils/helpers/dailyModeHelper.ts b/test/testUtils/helpers/dailyModeHelper.ts index 0f5bc84df68..8ee03ce5f89 100644 --- a/test/testUtils/helpers/dailyModeHelper.ts +++ b/test/testUtils/helpers/dailyModeHelper.ts @@ -6,7 +6,7 @@ import { EncounterPhase } from "#app/phases/encounter-phase"; import { TitlePhase } from "#app/phases/title-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase"; import type SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { GameManagerHelper } from "./gameManagerHelper"; /** @@ -24,12 +24,12 @@ export class DailyModeHelper extends GameManagerHelper { this.game.override.shiny(false).enemyShiny(false); } - this.game.onNextPrompt("TitlePhase", Mode.TITLE, () => { + this.game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { const titlePhase = new TitlePhase(); titlePhase.initDailyRun(); }); - this.game.onNextPrompt("TitlePhase", Mode.SAVE_SLOT, () => { + this.game.onNextPrompt("TitlePhase", UiMode.SAVE_SLOT, () => { const uihandler = this.game.scene.ui.getHandler(); uihandler.processInput(Button.ACTION); // select first slot. that's fine }); @@ -51,9 +51,9 @@ export class DailyModeHelper extends GameManagerHelper { if (this.game.scene.battleStyle === BattleStyle.SWITCH) { this.game.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - this.game.setMode(Mode.MESSAGE); + this.game.setMode(UiMode.MESSAGE); this.game.endPhase(); }, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase), @@ -61,9 +61,9 @@ export class DailyModeHelper extends GameManagerHelper { this.game.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - this.game.setMode(Mode.MESSAGE); + this.game.setMode(UiMode.MESSAGE); this.game.endPhase(); }, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase), diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index a54028ebca0..edade109966 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -7,7 +7,7 @@ import type { CommandPhase } from "#app/phases/command-phase"; import { LearnMovePhase } from "#app/phases/learn-move-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Command } from "#app/ui/command-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { Moves } from "#enums/moves"; import { getMovePosition } from "#test/testUtils/gameManagerUtils"; import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; @@ -53,10 +53,10 @@ export class MoveHelper extends GameManagerHelper { public select(move: Moves, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null) { const movePosition = getMovePosition(this.game.scene, pkmIndex, move); - this.game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { - this.game.scene.ui.setMode(Mode.FIGHT, (this.game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); + this.game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { + this.game.scene.ui.setMode(UiMode.FIGHT, (this.game.scene.getCurrentPhase() as CommandPhase).getFieldIndex()); }); - this.game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { + this.game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => { (this.game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false); }); @@ -76,14 +76,14 @@ export class MoveHelper extends GameManagerHelper { const movePosition = getMovePosition(this.game.scene, pkmIndex, move); this.game.scene.getPlayerParty()[pkmIndex].isTerastallized = false; - this.game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + this.game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { this.game.scene.ui.setMode( - Mode.FIGHT, + UiMode.FIGHT, (this.game.scene.getCurrentPhase() as CommandPhase).getFieldIndex(), Command.TERA, ); }); - this.game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { + this.game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => { (this.game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.TERA, movePosition, false); }); @@ -135,16 +135,16 @@ export class MoveHelper extends GameManagerHelper { // if slots are full, queue up inputs to replace existing moves if (this.game.scene.getPlayerParty()[partyIndex].moveset.filter(m => m).length === 4) { - this.game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => { + this.game.onNextPrompt("LearnMovePhase", UiMode.CONFIRM, () => { this.game.scene.ui.processInput(Button.ACTION); // "Should a move be forgotten and replaced with XXX?" }); - this.game.onNextPrompt("LearnMovePhase", Mode.SUMMARY, () => { + this.game.onNextPrompt("LearnMovePhase", UiMode.SUMMARY, () => { for (let x = 0; x < (moveSlotIndex ?? 0); x++) { this.game.scene.ui.processInput(Button.DOWN); // Scrolling in summary pane to move position } this.game.scene.ui.processInput(Button.ACTION); if (moveSlotIndex === 4) { - this.game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => { + this.game.onNextPrompt("LearnMovePhase", UiMode.CONFIRM, () => { this.game.scene.ui.processInput(Button.ACTION); // "Give up on learning XXX?" }); } diff --git a/test/testUtils/helpers/overridesHelper.ts b/test/testUtils/helpers/overridesHelper.ts index d570a1a4195..6aa382ef59a 100644 --- a/test/testUtils/helpers/overridesHelper.ts +++ b/test/testUtils/helpers/overridesHelper.ts @@ -14,7 +14,7 @@ import { StatusEffect } from "#enums/status-effect"; import type { WeatherType } from "#enums/weather-type"; import { expect, vi } from "vitest"; import { GameManagerHelper } from "./gameManagerHelper"; -import { shiftCharCodes } from "#app/utils"; +import { shiftCharCodes } from "#app/utils/common"; import type { RandomTrainerOverride } from "#app/overrides"; import type { BattleType } from "#enums/battle-type"; diff --git a/test/testUtils/helpers/reloadHelper.ts b/test/testUtils/helpers/reloadHelper.ts index 842cd88b95c..4867a146aaf 100644 --- a/test/testUtils/helpers/reloadHelper.ts +++ b/test/testUtils/helpers/reloadHelper.ts @@ -1,6 +1,6 @@ import { GameManagerHelper } from "./gameManagerHelper"; import { TitlePhase } from "#app/phases/title-phase"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { vi } from "vitest"; import { BattleStyle } from "#app/enums/battle-style"; import { CommandPhase } from "#app/phases/command-phase"; @@ -53,9 +53,9 @@ export class ReloadHelper extends GameManagerHelper { if (this.game.scene.battleStyle === BattleStyle.SWITCH) { this.game.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - this.game.setMode(Mode.MESSAGE); + this.game.setMode(UiMode.MESSAGE); this.game.endPhase(); }, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase), @@ -63,9 +63,9 @@ export class ReloadHelper extends GameManagerHelper { this.game.onNextPrompt( "CheckSwitchPhase", - Mode.CONFIRM, + UiMode.CONFIRM, () => { - this.game.setMode(Mode.MESSAGE); + this.game.setMode(UiMode.MESSAGE); this.game.endPhase(); }, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase), diff --git a/test/testUtils/phaseInterceptor.ts b/test/testUtils/phaseInterceptor.ts index 742a6bc8441..3d56c513c00 100644 --- a/test/testUtils/phaseInterceptor.ts +++ b/test/testUtils/phaseInterceptor.ts @@ -43,7 +43,8 @@ import { TurnStartPhase } from "#app/phases/turn-start-phase"; import { UnavailablePhase } from "#app/phases/unavailable-phase"; import { VictoryPhase } from "#app/phases/victory-phase"; import { PartyHealPhase } from "#app/phases/party-heal-phase"; -import UI, { Mode } from "#app/ui/ui"; +import UI from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { SelectBiomePhase } from "#app/phases/select-biome-phase"; import { MysteryEncounterBattlePhase, @@ -64,7 +65,7 @@ import { RevivalBlessingPhase } from "#app/phases/revival-blessing-phase"; export interface PromptHandler { phaseTarget?: string; - mode?: Mode; + mode?: UiMode; callback?: () => void; expireFn?: () => void; awaitingActionInput?: boolean; @@ -487,13 +488,13 @@ export default class PhaseInterceptor { /** * m2m to set mode. - * @param mode - The {@linkcode Mode} to set. + * @param mode - The {@linkcode UiMode} to set. * @param args - Additional arguments to pass to the original method. */ - setMode(mode: Mode, ...args: unknown[]): Promise { + setMode(mode: UiMode, ...args: unknown[]): Promise { const currentPhase = this.scene.getCurrentPhase(); const instance = this.scene.ui; - console.log("setMode", `${Mode[mode]} (=${mode})`, args); + console.log("setMode", `${UiMode[mode]} (=${mode})`, args); const ret = this.originalSetMode.apply(instance, [mode, ...args]); if (!this.phases[currentPhase.constructor.name]) { throw new Error( @@ -546,7 +547,7 @@ export default class PhaseInterceptor { */ addToNextPrompt( phaseTarget: string, - mode: Mode, + mode: UiMode, callback: () => void, expireFn?: () => void, awaitingActionInput = false, diff --git a/test/testUtils/testFileInitialization.ts b/test/testUtils/testFileInitialization.ts index cb2cd57044d..414e34e024b 100644 --- a/test/testUtils/testFileInitialization.ts +++ b/test/testUtils/testFileInitialization.ts @@ -11,7 +11,7 @@ import { initSpecies } from "#app/data/pokemon-species"; import { initAchievements } from "#app/system/achv"; import { initVouchers } from "#app/system/voucher"; import { initStatsKeys } from "#app/ui/game-stats-ui-handler"; -import { setCookie } from "#app/utils"; +import { setCookie } from "#app/utils/cookies"; import { blobToString } from "#test/testUtils/gameManagerUtils"; import { MockConsoleLog } from "#test/testUtils/mocks/mockConsoleLog"; import { mockContext } from "#test/testUtils/mocks/mockContextCanvas"; @@ -21,6 +21,7 @@ import Phaser from "phaser"; import InputText from "phaser3-rex-plugins/plugins/inputtext"; import BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; import { manageListeners } from "./listenersManager"; +import { initI18n } from "#app/plugins/i18n"; let wasInitialized = false; /** @@ -87,6 +88,7 @@ export function initTestFile() { // initSpecies(); if (!wasInitialized) { wasInitialized = true; + initI18n(); initVouchers(); initAchievements(); initStatsKeys(); diff --git a/test/ui/starter-select.test.ts b/test/ui/starter-select.test.ts index 1d523c3bbd5..b402e02e2d7 100644 --- a/test/ui/starter-select.test.ts +++ b/test/ui/starter-select.test.ts @@ -9,7 +9,7 @@ import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler" import type SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler"; import type OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler"; import type StarterSelectUiHandler from "#app/ui/starter-select-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import { Abilities } from "#enums/abilities"; import { Button } from "#enums/buttons"; import { Species } from "#enums/species"; @@ -44,12 +44,12 @@ describe("UI - Starter select", () => { }).length; expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); - game.onNextPrompt("TitlePhase", Mode.TITLE, () => { + game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { const currentPhase = game.scene.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); @@ -60,7 +60,7 @@ describe("UI - Starter select", () => { let options: OptionSelectItem[] = []; let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; options = optionSelectUiHandler.getOptionsWithScroll(); resolve(); @@ -74,15 +74,15 @@ describe("UI - Starter select", () => { optionSelectUiHandler?.processInput(Button.ACTION); await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.SUBMIT); }); - game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.CONFIRM, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.ACTION); }); - game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.SAVE_SLOT, () => { const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler; saveSlotSelectUiHandler.processInput(Button.ACTION); resolve(); @@ -104,12 +104,12 @@ describe("UI - Starter select", () => { }).length; expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); - game.onNextPrompt("TitlePhase", Mode.TITLE, () => { + game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { const currentPhase = game.scene.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); @@ -121,7 +121,7 @@ describe("UI - Starter select", () => { let options: OptionSelectItem[] = []; let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; options = optionSelectUiHandler.getOptionsWithScroll(); resolve(); @@ -135,15 +135,15 @@ describe("UI - Starter select", () => { optionSelectUiHandler?.processInput(Button.ACTION); await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.SUBMIT); }); - game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.CONFIRM, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.ACTION); }); - game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.SAVE_SLOT, () => { const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler; saveSlotSelectUiHandler.processInput(Button.ACTION); resolve(); @@ -166,12 +166,12 @@ describe("UI - Starter select", () => { }).length; expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); - game.onNextPrompt("TitlePhase", Mode.TITLE, () => { + game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { const currentPhase = game.scene.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); @@ -185,7 +185,7 @@ describe("UI - Starter select", () => { let options: OptionSelectItem[] = []; let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; options = optionSelectUiHandler.getOptionsWithScroll(); resolve(); @@ -199,15 +199,15 @@ describe("UI - Starter select", () => { optionSelectUiHandler?.processInput(Button.ACTION); await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.SUBMIT); }); - game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.CONFIRM, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.ACTION); }); - game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.SAVE_SLOT, () => { const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler; saveSlotSelectUiHandler.processInput(Button.ACTION); resolve(); @@ -231,12 +231,12 @@ describe("UI - Starter select", () => { }).length; expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); - game.onNextPrompt("TitlePhase", Mode.TITLE, () => { + game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { const currentPhase = game.scene.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); @@ -248,7 +248,7 @@ describe("UI - Starter select", () => { let options: OptionSelectItem[] = []; let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; options = optionSelectUiHandler.getOptionsWithScroll(); resolve(); @@ -262,15 +262,15 @@ describe("UI - Starter select", () => { optionSelectUiHandler?.processInput(Button.ACTION); await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.SUBMIT); }); - game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.CONFIRM, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.ACTION); }); - game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.SAVE_SLOT, () => { const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler; saveSlotSelectUiHandler.processInput(Button.ACTION); resolve(); @@ -292,12 +292,12 @@ describe("UI - Starter select", () => { }).length; expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); - game.onNextPrompt("TitlePhase", Mode.TITLE, () => { + game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { const currentPhase = game.scene.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); @@ -309,7 +309,7 @@ describe("UI - Starter select", () => { let options: OptionSelectItem[] = []; let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; options = optionSelectUiHandler.getOptionsWithScroll(); resolve(); @@ -323,15 +323,15 @@ describe("UI - Starter select", () => { optionSelectUiHandler?.processInput(Button.ACTION); await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.SUBMIT); }); - game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.CONFIRM, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.ACTION); }); - game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.SAVE_SLOT, () => { const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler; saveSlotSelectUiHandler.processInput(Button.ACTION); resolve(); @@ -352,12 +352,12 @@ describe("UI - Starter select", () => { }).length; expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); - game.onNextPrompt("TitlePhase", Mode.TITLE, () => { + game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { const currentPhase = game.scene.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); @@ -371,7 +371,7 @@ describe("UI - Starter select", () => { let options: OptionSelectItem[] = []; let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; options = optionSelectUiHandler.getOptionsWithScroll(); resolve(); @@ -385,15 +385,15 @@ describe("UI - Starter select", () => { optionSelectUiHandler?.processInput(Button.ACTION); await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.SUBMIT); }); - game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.CONFIRM, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.ACTION); }); - game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.SAVE_SLOT, () => { const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler; saveSlotSelectUiHandler.processInput(Button.ACTION); resolve(); @@ -414,12 +414,12 @@ describe("UI - Starter select", () => { }).length; expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); - game.onNextPrompt("TitlePhase", Mode.TITLE, () => { + game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { const currentPhase = game.scene.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); @@ -432,7 +432,7 @@ describe("UI - Starter select", () => { let options: OptionSelectItem[] = []; let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; options = optionSelectUiHandler.getOptionsWithScroll(); resolve(); @@ -446,15 +446,15 @@ describe("UI - Starter select", () => { optionSelectUiHandler?.processInput(Button.ACTION); await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.SUBMIT); }); - game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.CONFIRM, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.ACTION); }); - game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.SAVE_SLOT, () => { const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler; saveSlotSelectUiHandler.processInput(Button.ACTION); resolve(); @@ -475,12 +475,12 @@ describe("UI - Starter select", () => { }).length; expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); - game.onNextPrompt("TitlePhase", Mode.TITLE, () => { + game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { const currentPhase = game.scene.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.RIGHT); @@ -492,7 +492,7 @@ describe("UI - Starter select", () => { let options: OptionSelectItem[] = []; let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; options = optionSelectUiHandler.getOptionsWithScroll(); resolve(); @@ -507,7 +507,7 @@ describe("UI - Starter select", () => { let starterSelectUiHandler: StarterSelectUiHandler; await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { starterSelectUiHandler = game.scene.ui.getHandler() as StarterSelectUiHandler; starterSelectUiHandler.processInput(Button.SUBMIT); resolve(); @@ -519,11 +519,11 @@ describe("UI - Starter select", () => { // expect(starterSelectUiHandler.cursorObj.x).toBe(132 + 4 * 18); // expect(starterSelectUiHandler.cursorObj.y).toBe(10); - game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.CONFIRM, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.ACTION); }); - game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.SAVE_SLOT, () => { const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler; saveSlotSelectUiHandler.processInput(Button.ACTION); }); @@ -539,12 +539,12 @@ describe("UI - Starter select", () => { }).length; expect(caughtCount).toBe(Object.keys(allSpecies).length); await game.runToTitle(); - game.onNextPrompt("TitlePhase", Mode.TITLE, () => { + game.onNextPrompt("TitlePhase", UiMode.TITLE, () => { const currentPhase = game.scene.getCurrentPhase() as TitlePhase; currentPhase.gameMode = GameModes.CLASSIC; currentPhase.end(); }); - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.RIGHT); @@ -557,7 +557,7 @@ describe("UI - Starter select", () => { let options: OptionSelectItem[] = []; let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; options = optionSelectUiHandler.getOptionsWithScroll(); resolve(); @@ -572,7 +572,7 @@ describe("UI - Starter select", () => { let starterSelectUiHandler: StarterSelectUiHandler | undefined; await new Promise(resolve => { - game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.STARTER_SELECT, () => { starterSelectUiHandler = game.scene.ui.getHandler() as StarterSelectUiHandler; starterSelectUiHandler.processInput(Button.SUBMIT); resolve(); @@ -585,11 +585,11 @@ describe("UI - Starter select", () => { expect(starterSelectUiHandler?.cursorObj.x).toBe(53); expect(starterSelectUiHandler?.cursorObj.y).toBe(31); - game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.CONFIRM, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.ACTION); }); - game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => { + game.onNextPrompt("SelectStarterPhase", UiMode.SAVE_SLOT, () => { const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler; saveSlotSelectUiHandler.processInput(Button.ACTION); }); diff --git a/test/ui/transfer-item.test.ts b/test/ui/transfer-item.test.ts index cbbdc1d50ee..f0ea8f84005 100644 --- a/test/ui/transfer-item.test.ts +++ b/test/ui/transfer-item.test.ts @@ -4,7 +4,7 @@ import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; import PartyUiHandler, { PartyUiMode } from "#app/ui/party-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import type BBCodeText from "phaser3-rex-plugins/plugins/bbcodetext"; @@ -42,21 +42,21 @@ describe("UI - Transfer Items", () => { game.move.select(Moves.DRAGON_CLAW); - game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { + game.onNextPrompt("SelectModifierPhase", UiMode.MODIFIER_SELECT, () => { expect(game.scene.ui.getHandler()).toBeInstanceOf(ModifierSelectUiHandler); const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler; handler.setCursor(1); handler.processInput(Button.ACTION); - void game.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER); + void game.scene.ui.setModeWithoutClear(UiMode.PARTY, PartyUiMode.MODIFIER_TRANSFER); }); await game.phaseInterceptor.to("BattleEndPhase"); }); it("check red tint for held item limit in transfer menu", async () => { - game.onNextPrompt("SelectModifierPhase", Mode.PARTY, () => { + game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, () => { expect(game.scene.ui.getHandler()).toBeInstanceOf(PartyUiHandler); const handler = game.scene.ui.getHandler() as PartyUiHandler; @@ -79,7 +79,7 @@ describe("UI - Transfer Items", () => { }, 20000); it("check transfer option for pokemon to transfer to", async () => { - game.onNextPrompt("SelectModifierPhase", Mode.PARTY, () => { + game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, () => { expect(game.scene.ui.getHandler()).toBeInstanceOf(PartyUiHandler); const handler = game.scene.ui.getHandler() as PartyUiHandler; diff --git a/test/ui/type-hints.test.ts b/test/ui/type-hints.test.ts index fcb71186448..08d9806ec7f 100644 --- a/test/ui/type-hints.test.ts +++ b/test/ui/type-hints.test.ts @@ -3,7 +3,7 @@ import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; import { CommandPhase } from "#app/phases/command-phase"; import FightUiHandler from "#app/ui/fight-ui-handler"; -import { Mode } from "#app/ui/ui"; +import { UiMode } from "#enums/ui-mode"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -42,14 +42,14 @@ describe("UI - Type Hints", () => { await game.startBattle([Species.RAYQUAZA]); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { const { ui } = game.scene; const handler = ui.getHandler(); handler.processInput(Button.ACTION); // select "Fight" game.phaseInterceptor.unlock(); }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { + game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => { const { ui } = game.scene; const movesContainer = ui.getByName(FightUiHandler.MOVES_CONTAINER_NAME); const dragonClawText = movesContainer @@ -67,14 +67,14 @@ describe("UI - Type Hints", () => { await game.startBattle([Species.RAYQUAZA]); - game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { const { ui } = game.scene; const handler = ui.getHandler(); handler.processInput(Button.ACTION); // select "Fight" game.phaseInterceptor.unlock(); }); - game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { + game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => { const { ui } = game.scene; const movesContainer = ui.getByName(FightUiHandler.MOVES_CONTAINER_NAME); const growlText = movesContainer diff --git a/src/utils.test.ts b/test/utils.test.ts similarity index 95% rename from src/utils.test.ts rename to test/utils.test.ts index cc3f2bb1a04..33f7906738c 100644 --- a/src/utils.test.ts +++ b/test/utils.test.ts @@ -1,5 +1,5 @@ import { expect, describe, it, beforeAll } from "vitest"; -import { randomString, padInt } from "./utils"; +import { randomString, padInt } from "#app/utils/common"; import Phaser from "phaser"; From 65294f408e1c8b26e13fa7ff01d6f42039ed0cfa Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Sat, 19 Apr 2025 10:04:19 -0500 Subject: [PATCH 37/52] [Bug][UI/UX] Fix type hint after enemy disappears (#5677) * Fix type hint after enemy disappears * Add automated test for type hint bugfix * Make onField default to true * Replace reference to Mode with UiMode and battleType with BattleStyle --- src/data/moves/move.ts | 4 ++-- src/data/terrain.ts | 2 +- src/field/pokemon.ts | 9 +++++++-- src/phases/move-phase.ts | 2 +- test/ui/type-hints.test.ts | 41 ++++++++++++++++++++++++++++++++++++-- 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 513ab3f6a74..26654fee18f 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -8183,7 +8183,7 @@ export type MoveTargetSet = { export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveTarget): MoveTargetSet { const variableTarget = new NumberHolder(0); - user.getOpponents().forEach(p => applyMoveAttrs(VariableTargetAttr, user, p, allMoves[move], variableTarget)); + user.getOpponents(false).forEach(p => applyMoveAttrs(VariableTargetAttr, user, p, allMoves[move], variableTarget)); let moveTarget: MoveTarget | undefined; if (allMoves[move].hasAttr(VariableTargetAttr)) { @@ -8195,7 +8195,7 @@ export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveT } else if (move === undefined) { moveTarget = MoveTarget.NEAR_ENEMY; } - const opponents = user.getOpponents(); + const opponents = user.getOpponents(false); let set: Pokemon[] = []; let multiple = false; diff --git a/src/data/terrain.ts b/src/data/terrain.ts index 894fb8a7955..5b6063cee68 100644 --- a/src/data/terrain.ts +++ b/src/data/terrain.ts @@ -59,7 +59,7 @@ export class Terrain { // Cancels move if the move has positive priority and targets a Pokemon grounded on the Psychic Terrain return ( move.getPriority(user) > 0 && - user.getOpponents().some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded()) + user.getOpponents(true).some(o => targets.includes(o.getBattlerIndex()) && o.isGrounded()) ); } } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 0242820dcde..f2e5fd4c2b6 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -3852,12 +3852,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return null; } - getOpponents(): Pokemon[] { + /** + * Returns the pokemon that oppose this one and are active + * + * @param onField - whether to also check if the pokemon is currently on the field (defaults to true) + */ + getOpponents(onField = true): Pokemon[] { return ( (this.isPlayer() ? globalScene.getEnemyField() : globalScene.getPlayerField()) as Pokemon[] - ).filter(p => p.isActive()); + ).filter(p => p.isActive(onField)); } getOpponentDescriptor(): string { diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index f42a2aefa34..7d2848a5d70 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -654,7 +654,7 @@ export class MovePhase extends BattlePhase { }), 500, ); - applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents()[0], this.move.getMove()); + applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents(false)[0], this.move.getMove()); } public showFailedText(failedText: string = i18next.t("battle:attackFailed")): void { diff --git a/test/ui/type-hints.test.ts b/test/ui/type-hints.test.ts index 08d9806ec7f..2051af76754 100644 --- a/test/ui/type-hints.test.ts +++ b/test/ui/type-hints.test.ts @@ -40,7 +40,7 @@ describe("UI - Type Hints", () => { .moveset([Moves.DRAGON_CLAW]); game.settings.typeHints(true); //activate type hints - await game.startBattle([Species.RAYQUAZA]); + await game.classicMode.startBattle([Species.RAYQUAZA]); game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { const { ui } = game.scene; @@ -65,7 +65,7 @@ describe("UI - Type Hints", () => { it("check status move color", async () => { game.override.enemySpecies(Species.FLORGES).moveset([Moves.GROWL]); - await game.startBattle([Species.RAYQUAZA]); + await game.classicMode.startBattle([Species.RAYQUAZA]); game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { const { ui } = game.scene; @@ -86,4 +86,41 @@ describe("UI - Type Hints", () => { }); await game.phaseInterceptor.to(CommandPhase); }); + + it("should show the proper hint for a move in doubles after one of the enemy pokemon flees", async () => { + game.override + .enemySpecies(Species.ABRA) + .moveset([Moves.SPLASH, Moves.SHADOW_BALL, Moves.SOAK]) + .enemyMoveset([Moves.SPLASH, Moves.TELEPORT]) + .battleStyle("double"); + + await game.classicMode.startBattle([Species.MAGIKARP, Species.MAGIKARP]); + game.move.select(Moves.SPLASH); + // Use soak to change type of remaining abra to water + game.move.select(Moves.SOAK, 1); + + await game.forceEnemyMove(Moves.SPLASH); + await game.forceEnemyMove(Moves.TELEPORT); + await game.toNextTurn(); + + game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { + const { ui } = game.scene; + const handler = ui.getHandler(); + handler.processInput(Button.ACTION); // select "Fight" + game.phaseInterceptor.unlock(); + }); + + game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => { + const { ui } = game.scene; + const movesContainer = ui.getByName(FightUiHandler.MOVES_CONTAINER_NAME); + const shadowBallText = movesContainer + .getAll() + .find(text => text.text === i18next.t("move:shadowBall.name"))! as unknown as MockText; + expect.soft(shadowBallText).toBeDefined(); + + expect.soft(shadowBallText.color).toBe(undefined); + ui.getHandler().processInput(Button.ACTION); + }); + await game.phaseInterceptor.to(CommandPhase); + }); }); From bda286cebb7285400925c5eefa3b3f4811549cda Mon Sep 17 00:00:00 2001 From: Chris <75648912+ChrisLolz@users.noreply.github.com> Date: Sat, 19 Apr 2025 17:00:12 -0400 Subject: [PATCH 38/52] [Bug] Fix Login Screen Buttons can be Pressed While Animating (#5170) * destroy containers when processing external containers * make form buttons uninteractible until tweens finished instead * fix holding enter spam * fix conflicts --- src/ui/form-modal-ui-handler.ts | 2 +- src/ui/login-form-ui-handler.ts | 132 +++++++++++++------------ src/ui/modal-ui-handler.ts | 6 +- src/ui/registration-form-ui-handler.ts | 86 ++++++++-------- 4 files changed, 120 insertions(+), 106 deletions(-) diff --git a/src/ui/form-modal-ui-handler.ts b/src/ui/form-modal-ui-handler.ts index e8e67d591d5..8c30b4e0bc4 100644 --- a/src/ui/form-modal-ui-handler.ts +++ b/src/ui/form-modal-ui-handler.ts @@ -124,7 +124,7 @@ export abstract class FormModalUiHandler extends ModalUiHandler { if (this.buttonBgs.length) { this.buttonBgs[0].off("pointerdown"); this.buttonBgs[0].on("pointerdown", () => { - if (this.submitAction) { + if (this.submitAction && globalScene.tweens.getTweensOf(this.modalContainer).length === 0) { this.submitAction(); } }); diff --git a/src/ui/login-form-ui-handler.ts b/src/ui/login-form-ui-handler.ts index 2dfab9c0c40..714a9b39771 100644 --- a/src/ui/login-form-ui-handler.ts +++ b/src/ui/login-form-ui-handler.ts @@ -40,25 +40,9 @@ export default class LoginFormUiHandler extends FormModalUiHandler { setup(): void { super.setup(); + this.buildExternalPartyContainer(); - - this.infoContainer = globalScene.add.container(0, 0); - - this.usernameInfoImage = this.buildInteractableImage("settings_icon", "username-info-icon", { - x: 20, - scale: 0.5, - }); - - this.saveDownloadImage = this.buildInteractableImage("saving_icon", "save-download-icon", { - x: 0, - scale: 0.75, - }); - - this.infoContainer.add(this.usernameInfoImage); - this.infoContainer.add(this.saveDownloadImage); - this.getUi().add(this.infoContainer); - this.infoContainer.setVisible(false); - this.infoContainer.disableInteractive(); + this.buildInfoContainer(); } private buildExternalPartyContainer() { @@ -84,6 +68,26 @@ export default class LoginFormUiHandler extends FormModalUiHandler { this.externalPartyContainer.setVisible(false); } + private buildInfoContainer() { + this.infoContainer = globalScene.add.container(0, 0); + + this.usernameInfoImage = this.buildInteractableImage("settings_icon", "username-info-icon", { + x: 20, + scale: 0.5, + }); + + this.saveDownloadImage = this.buildInteractableImage("saving_icon", "save-download-icon", { + x: 0, + scale: 0.75, + }); + + this.infoContainer.add(this.usernameInfoImage); + this.infoContainer.add(this.saveDownloadImage); + this.getUi().add(this.infoContainer); + this.infoContainer.setVisible(false); + this.infoContainer.disableInteractive(); + } + override getModalTitle(_config?: ModalConfig): string { let key = "menu:login"; if (import.meta.env.VITE_SERVER_URL === "https://apibeta.pokerogue.net") { @@ -143,27 +147,29 @@ export default class LoginFormUiHandler extends FormModalUiHandler { this.processExternalProvider(config); const originalLoginAction = this.submitAction; this.submitAction = _ => { - // Prevent overlapping overrides on action modification - this.submitAction = originalLoginAction; - this.sanitizeInputs(); + if (globalScene.tweens.getTweensOf(this.modalContainer).length === 0) { + // Prevent overlapping overrides on action modification + this.submitAction = originalLoginAction; + this.sanitizeInputs(); globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); - const onFail = error => { + const onFail = error => { globalScene.ui.setMode(UiMode.LOGIN_FORM, Object.assign(config, { errorMessage: error?.trim() })); - globalScene.ui.playError(); - }; - if (!this.inputs[0].text) { - return onFail(i18next.t("menu:emptyUsername")); - } - - const [usernameInput, passwordInput] = this.inputs; - - pokerogueApi.account.login({ username: usernameInput.text, password: passwordInput.text }).then(error => { - if (!error && originalLoginAction) { - originalLoginAction(); - } else { - onFail(error); + globalScene.ui.playError(); + }; + if (!this.inputs[0].text) { + return onFail(i18next.t("menu:emptyUsername")); } - }); + + const [usernameInput, passwordInput] = this.inputs; + + pokerogueApi.account.login({ username: usernameInput.text, password: passwordInput.text }).then(error => { + if (!error && originalLoginAction) { + originalLoginAction(); + } else { + onFail(error); + } + }); + } }; return true; @@ -221,34 +227,36 @@ export default class LoginFormUiHandler extends FormModalUiHandler { }; this.usernameInfoImage.on("pointerdown", () => { - const localStorageKeys = Object.keys(localStorage); // this gets the keys for localStorage - const keyToFind = "data_"; - const dataKeys = localStorageKeys.filter(ls => ls.indexOf(keyToFind) >= 0); - if (dataKeys.length > 0 && dataKeys.length <= 2) { - const options: OptionSelectItem[] = []; - for (let i = 0; i < dataKeys.length; i++) { - options.push({ - label: dataKeys[i].replace(keyToFind, ""), - handler: () => { - globalScene.ui.revertMode(); - this.infoContainer.disableInteractive(); - return true; - }, - }); - } + if (globalScene.tweens.getTweensOf(this.infoContainer).length === 0) { + const localStorageKeys = Object.keys(localStorage); // this gets the keys for localStorage + const keyToFind = "data_"; + const dataKeys = localStorageKeys.filter(ls => ls.indexOf(keyToFind) >= 0); + if (dataKeys.length > 0 && dataKeys.length <= 2) { + const options: OptionSelectItem[] = []; + for (let i = 0; i < dataKeys.length; i++) { + options.push({ + label: dataKeys[i].replace(keyToFind, ""), + handler: () => { + globalScene.ui.revertMode(); + this.infoContainer.disableInteractive(); + return true; + }, + }); + } globalScene.ui.setOverlayMode(UiMode.OPTION_SELECT, { - options: options, - delay: 1000, - }); - this.infoContainer.setInteractive( - new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width, globalScene.game.canvas.height), - Phaser.Geom.Rectangle.Contains, - ); - } else { - if (dataKeys.length > 2) { - return onFail(this.ERR_TOO_MANY_SAVES); + options: options, + delay: 1000, + }); + this.infoContainer.setInteractive( + new Phaser.Geom.Rectangle(0, 0, globalScene.game.canvas.width, globalScene.game.canvas.height), + Phaser.Geom.Rectangle.Contains, + ); + } else { + if (dataKeys.length > 2) { + return onFail(this.ERR_TOO_MANY_SAVES); + } + return onFail(this.ERR_NO_SAVES); } - return onFail(this.ERR_NO_SAVES); } }); diff --git a/src/ui/modal-ui-handler.ts b/src/ui/modal-ui-handler.ts index a3b94296d3f..56c1c2c3fcf 100644 --- a/src/ui/modal-ui-handler.ts +++ b/src/ui/modal-ui-handler.ts @@ -134,7 +134,11 @@ export abstract class ModalUiHandler extends UiHandler { for (let a = 0; a < this.buttonBgs.length; a++) { if (a < this.buttonBgs.length) { - this.buttonBgs[a].on("pointerdown", _ => config.buttonActions[a]()); + this.buttonBgs[a].on("pointerdown", _ => { + if (globalScene.tweens.getTweensOf(this.modalContainer).length === 0) { + config.buttonActions[a](); + } + }); } } diff --git a/src/ui/registration-form-ui-handler.ts b/src/ui/registration-form-ui-handler.ts index bb10efc5869..3d4613c21d6 100644 --- a/src/ui/registration-form-ui-handler.ts +++ b/src/ui/registration-form-ui-handler.ts @@ -98,51 +98,53 @@ export default class RegistrationFormUiHandler extends FormModalUiHandler { const originalRegistrationAction = this.submitAction; this.submitAction = _ => { - // Prevent overlapping overrides on action modification - this.submitAction = originalRegistrationAction; - this.sanitizeInputs(); + if (globalScene.tweens.getTweensOf(this.modalContainer).length === 0) { + // Prevent overlapping overrides on action modification + this.submitAction = originalRegistrationAction; + this.sanitizeInputs(); globalScene.ui.setMode(UiMode.LOADING, { buttonActions: [] }); - const onFail = error => { + const onFail = error => { globalScene.ui.setMode(UiMode.REGISTRATION_FORM, Object.assign(config, { errorMessage: error?.trim() })); - globalScene.ui.playError(); - const errorMessageFontSize = languageSettings[i18next.resolvedLanguage!]?.errorMessageFontSize; - if (errorMessageFontSize) { - this.errorMessage.setFontSize(errorMessageFontSize); - } - }; - if (!this.inputs[0].text) { - return onFail(i18next.t("menu:emptyUsername")); - } - if (!this.inputs[1].text) { - return onFail(this.getReadableErrorMessage("invalid password")); - } - if (this.inputs[1].text !== this.inputs[2].text) { - return onFail(i18next.t("menu:passwordNotMatchingConfirmPassword")); - } - const [usernameInput, passwordInput] = this.inputs; - pokerogueApi.account - .register({ - username: usernameInput.text, - password: passwordInput.text, - }) - .then(registerError => { - if (!registerError) { - pokerogueApi.account - .login({ - username: usernameInput.text, - password: passwordInput.text, - }) - .then(loginError => { - if (!loginError) { - originalRegistrationAction?.(); - } else { - onFail(loginError); - } - }); - } else { - onFail(registerError); + globalScene.ui.playError(); + const errorMessageFontSize = languageSettings[i18next.resolvedLanguage!]?.errorMessageFontSize; + if (errorMessageFontSize) { + this.errorMessage.setFontSize(errorMessageFontSize); } - }); + }; + if (!this.inputs[0].text) { + return onFail(i18next.t("menu:emptyUsername")); + } + if (!this.inputs[1].text) { + return onFail(this.getReadableErrorMessage("invalid password")); + } + if (this.inputs[1].text !== this.inputs[2].text) { + return onFail(i18next.t("menu:passwordNotMatchingConfirmPassword")); + } + const [usernameInput, passwordInput] = this.inputs; + pokerogueApi.account + .register({ + username: usernameInput.text, + password: passwordInput.text, + }) + .then(registerError => { + if (!registerError) { + pokerogueApi.account + .login({ + username: usernameInput.text, + password: passwordInput.text, + }) + .then(loginError => { + if (!loginError) { + originalRegistrationAction?.(); + } else { + onFail(loginError); + } + }); + } else { + onFail(registerError); + } + }); + } }; return true; From 8515cadd7735c0046a9185dec936f208f07c2281 Mon Sep 17 00:00:00 2001 From: Blitzy <118096277+Blitz425@users.noreply.github.com> Date: Sun, 20 Apr 2025 00:20:07 -0500 Subject: [PATCH 39/52] [Balance] Update Gym Leader Teams and Teras (#5670) * Update Gym Leader Teams * Set Tera slots for Gym Leaders * Change Giovanni's Specialty Type to Ground --- Co-authored-by: damocleas --- src/data/balance/signature-species.ts | 134 +++++++++++++------------- src/data/trainers/trainer-config.ts | 132 ++++++++++++------------- 2 files changed, 133 insertions(+), 133 deletions(-) diff --git a/src/data/balance/signature-species.ts b/src/data/balance/signature-species.ts index a1b73af40cd..e2fecaa12ff 100644 --- a/src/data/balance/signature-species.ts +++ b/src/data/balance/signature-species.ts @@ -11,87 +11,87 @@ export type SignatureSpecies = { */ export const signatureSpecies: SignatureSpecies = { // Gym Leaders- Kanto - BROCK: [Species.GEODUDE, Species.ONIX], - MISTY: [Species.STARYU, Species.PSYDUCK], - LT_SURGE: [Species.VOLTORB, Species.PIKACHU, Species.ELECTABUZZ], + BROCK: [Species.ONIX, Species.GEODUDE, [Species.OMANYTE, Species.KABUTO], Species.AERODACTYL], + MISTY: [Species.STARYU, Species.PSYDUCK, Species.WOOPER, Species.LAPRAS], + LT_SURGE: [Species.PICHU, Species.VOLTORB, Species.ELEKID, Species.JOLTEON], ERIKA: [Species.ODDISH, Species.BELLSPROUT, Species.TANGELA, Species.HOPPIP], - JANINE: [Species.VENONAT, Species.SPINARAK, Species.ZUBAT], - SABRINA: [Species.ABRA, Species.MR_MIME, Species.ESPEON], - BLAINE: [Species.GROWLITHE, Species.PONYTA, Species.MAGMAR], - GIOVANNI: [Species.SANDILE, Species.MURKROW, Species.NIDORAN_M, Species.NIDORAN_F], + JANINE: [Species.VENONAT, Species.SPINARAK, Species.ZUBAT, Species.KOFFING], + SABRINA: [Species.ABRA, Species.MR_MIME, Species.SMOOCHUM, Species.ESPEON], + BLAINE: [Species.GROWLITHE, Species.PONYTA, Species.MAGBY, Species.VULPIX], + GIOVANNI: [Species.RHYHORN, Species.MEOWTH, [Species.NIDORAN_F, Species.NIDORAN_M], Species.DIGLETT], // Tera Ground Meowth // Gym Leaders- Johto - FALKNER: [Species.PIDGEY, Species.HOOTHOOT, Species.DODUO], - BUGSY: [Species.SCYTHER, Species.HERACROSS, Species.SHUCKLE, Species.PINSIR], - WHITNEY: [Species.JIGGLYPUFF, Species.MILTANK, Species.AIPOM, Species.GIRAFARIG], - MORTY: [Species.GASTLY, Species.MISDREAVUS, Species.SABLEYE], - CHUCK: [Species.POLIWRATH, Species.MANKEY], - JASMINE: [Species.MAGNEMITE, Species.STEELIX], - PRYCE: [Species.SEEL, Species.SWINUB], - CLAIR: [Species.DRATINI, Species.HORSEA, Species.GYARADOS], + FALKNER: [Species.PIDGEY, Species.HOOTHOOT, Species.NATU, Species.MURKROW], + BUGSY: [Species.SCYTHER, Species.SHUCKLE, Species.YANMA, [Species.PINSIR, Species.HERACROSS]], + WHITNEY: [Species.MILTANK, Species.AIPOM, Species.IGGLYBUFF, [Species.GIRAFARIG, Species.STANTLER]], + MORTY: [Species.GASTLY, Species.MISDREAVUS, Species.DUSKULL, Species.SABLEYE], + CHUCK: [Species.POLIWRATH, Species.MANKEY, Species.TYROGUE, Species.MACHOP], + JASMINE: [Species.STEELIX, Species.MAGNEMITE, Species.PINECO, Species.SKARMORY], + PRYCE: [Species.SWINUB, Species.SEEL, Species.SHELLDER, Species.SNEASEL], + CLAIR: [Species.HORSEA, Species.DRATINI, Species.MAGIKARP, Species.DRUDDIGON], // Tera Dragon Magikarp // Gym Leaders- Hoenn - ROXANNE: [Species.GEODUDE, Species.NOSEPASS], - BRAWLY: [Species.MACHOP, Species.MAKUHITA], - WATTSON: [Species.MAGNEMITE, Species.VOLTORB, Species.ELECTRIKE], - FLANNERY: [Species.SLUGMA, Species.TORKOAL, Species.NUMEL], - NORMAN: [Species.SLAKOTH, Species.SPINDA, Species.ZIGZAGOON, Species.KECLEON], + ROXANNE: [Species.NOSEPASS, Species.GEODUDE, [Species.LILEEP, Species.ANORITH], Species.ARON], + BRAWLY: [Species.MAKUHITA, Species.MACHOP, Species.MEDITITE, Species.SHROOMISH], + WATTSON: [Species.ELECTRIKE, Species.VOLTORB, Species.MAGNEMITE, [Species.PLUSLE, Species.MINUN]], + FLANNERY: [Species.TORKOAL, Species.SLUGMA, Species.NUMEL, Species.HOUNDOUR], + NORMAN: [Species.SLAKOTH, Species.KECLEON, Species.WHISMUR, Species.ZANGOOSE], WINONA: [Species.SWABLU, Species.WINGULL, Species.TROPIUS, Species.SKARMORY], - TATE: [Species.SOLROCK, Species.NATU, Species.CHIMECHO, Species.GALLADE], - LIZA: [Species.LUNATONE, Species.SPOINK, Species.BALTOY, Species.GARDEVOIR], - JUAN: [Species.HORSEA, Species.BARBOACH, Species.SPHEAL, Species.RELICANTH], + TATE: [Species.SOLROCK, Species.NATU, Species.CHINGLING, Species.GALLADE], + LIZA: [Species.LUNATONE, Species.BALTOY, Species.SPOINK, Species.GARDEVOIR], + JUAN: [Species.HORSEA, Species.SPHEAL, Species.BARBOACH, Species.CORPHISH], // Gym Leaders- Sinnoh - ROARK: [Species.CRANIDOS, Species.LARVITAR, Species.GEODUDE], - GARDENIA: [Species.ROSELIA, Species.TANGELA, Species.TURTWIG], - MAYLENE: [Species.LUCARIO, Species.MEDITITE, Species.CHIMCHAR], + ROARK: [Species.CRANIDOS, Species.GEODUDE, Species.NOSEPASS, Species.LARVITAR], + GARDENIA: [Species.BUDEW, Species.CHERUBI, Species.TURTWIG, Species.LEAFEON], + MAYLENE: [Species.RIOLU, Species.MEDITITE, Species.CHIMCHAR, Species.CROAGUNK], CRASHER_WAKE: [Species.BUIZEL, Species.WOOPER, Species.PIPLUP, Species.MAGIKARP], - FANTINA: [Species.MISDREAVUS, Species.DRIFLOON, Species.SPIRITOMB], - BYRON: [Species.SHIELDON, Species.BRONZOR, Species.AGGRON], - CANDICE: [Species.SNEASEL, Species.SNOVER, Species.SNORUNT], - VOLKNER: [Species.SHINX, Species.CHINCHOU, Species.ROTOM], + FANTINA: [Species.MISDREAVUS, Species.DRIFLOON, Species.DUSKULL, Species.SPIRITOMB], + BYRON: [Species.SHIELDON, Species.BRONZOR, Species.ARON, Species.SKARMORY], + CANDICE: [Species.FROSLASS, Species.SNOVER, Species.SNEASEL, Species.GLACEON], + VOLKNER: [Species.ELEKID, Species.SHINX, Species.CHINCHOU, Species.ROTOM], // Gym Leaders- Unova - CILAN: [Species.PANSAGE, Species.FOONGUS, Species.PETILIL], - CHILI: [Species.PANSEAR, Species.DARUMAKA, Species.NUMEL], - CRESS: [Species.PANPOUR, Species.TYMPOLE, Species.SLOWPOKE], - CHEREN: [Species.LILLIPUP, Species.MINCCINO, Species.PIDOVE], - LENORA: [Species.PATRAT, Species.DEERLING, Species.AUDINO], - ROXIE: [Species.VENIPEDE, Species.TRUBBISH, Species.SKORUPI], - BURGH: [Species.SEWADDLE, Species.SHELMET, Species.KARRABLAST], - ELESA: [Species.EMOLGA, Species.BLITZLE, Species.JOLTIK], - CLAY: [Species.DRILBUR, Species.SANDILE, Species.GOLETT], - SKYLA: [Species.DUCKLETT, Species.WOOBAT, Species.RUFFLET], - BRYCEN: [Species.CRYOGONAL, Species.VANILLITE, Species.CUBCHOO], - DRAYDEN: [Species.DRUDDIGON, Species.AXEW, Species.DEINO], - MARLON: [Species.WAILMER, Species.FRILLISH, Species.TIRTOUGA], + CILAN: [Species.PANSAGE, Species.SNIVY, Species.MARACTUS, Species.FERROSEED], + CHILI: [Species.PANSEAR, Species.TEPIG, Species.HEATMOR, Species.DARUMAKA], + CRESS: [Species.PANPOUR, Species.OSHAWOTT, Species.BASCULIN, Species.TYMPOLE], + CHEREN: [Species.LILLIPUP, Species.MINCCINO, Species.PIDOVE, Species.BOUFFALANT], + LENORA: [Species.PATRAT, Species.DEERLING, Species.AUDINO, Species.BRAVIARY], + ROXIE: [Species.VENIPEDE, Species.KOFFING, Species.TRUBBISH, Species.TOXEL], + BURGH: [Species.SEWADDLE, Species.DWEBBLE, [Species.KARRABLAST, Species.SHELMET], Species.DURANT], + ELESA: [Species.BLITZLE, Species.EMOLGA, Species.JOLTIK, Species.TYNAMO], + CLAY: [Species.DRILBUR, Species.SANDILE, Species.TYMPOLE, Species.GOLETT], + SKYLA: [Species.DUCKLETT, Species.WOOBAT, [Species.RUFFLET, Species.VULLABY], Species.ARCHEN], + BRYCEN: [Species.CRYOGONAL, Species.VANILLITE, Species.CUBCHOO, Species.GALAR_DARUMAKA], + DRAYDEN: [Species.AXEW, Species.DRUDDIGON, Species.TRAPINCH, Species.DEINO], + MARLON: [Species.FRILLISH, Species.TIRTOUGA, Species.WAILMER, Species.MANTYKE], // Gym Leaders- Kalos - VIOLA: [Species.SURSKIT, Species.SCATTERBUG], - GRANT: [Species.AMAURA, Species.TYRUNT], - KORRINA: [Species.HAWLUCHA, Species.LUCARIO, Species.MIENFOO], - RAMOS: [Species.SKIDDO, Species.HOPPIP, Species.BELLSPROUT], - CLEMONT: [Species.HELIOPTILE, Species.MAGNEMITE, Species.EMOLGA], - VALERIE: [Species.SYLVEON, Species.MAWILE, Species.MR_MIME], - OLYMPIA: [Species.ESPURR, Species.SIGILYPH, Species.SLOWKING], - WULFRIC: [Species.BERGMITE, Species.SNOVER, Species.CRYOGONAL], + VIOLA: [Species.SCATTERBUG, Species.SURSKIT, Species.CUTIEFLY, Species.BLIPBUG], + GRANT: [Species.TYRUNT, Species.AMAURA, Species.BINACLE, Species.DWEBBLE], + KORRINA: [Species.RIOLU, Species.MIENFOO, Species.HAWLUCHA, Species.PANCHAM], + RAMOS: [Species.SKIDDO, Species.HOPPIP, Species.BELLSPROUT, [Species.PHANTUMP, Species.PUMPKABOO]], + CLEMONT: [Species.HELIOPTILE, Species.MAGNEMITE, Species.DEDENNE, Species.ROTOM], + VALERIE: [Species.SYLVEON, Species.MAWILE, Species.MR_MIME, [Species.SPRITZEE, Species.SWIRLIX]], + OLYMPIA: [Species.ESPURR, Species.SIGILYPH, Species.INKAY, Species.SLOWKING], + WULFRIC: [Species.BERGMITE, Species.SNOVER, Species.CRYOGONAL, Species.SWINUB], // Gym Leaders- Galar - MILO: [Species.GOSSIFLEUR, Species.APPLIN, Species.BOUNSWEET], - NESSA: [Species.CHEWTLE, Species.ARROKUDA, Species.WIMPOD], - KABU: [Species.SIZZLIPEDE, Species.VULPIX, Species.TORKOAL], - BEA: [Species.GALAR_FARFETCHD, Species.MACHOP, Species.CLOBBOPUS], - ALLISTER: [Species.GALAR_YAMASK, Species.GALAR_CORSOLA, Species.GASTLY], - OPAL: [Species.MILCERY, Species.TOGETIC, Species.GALAR_WEEZING], - BEDE: [Species.HATENNA, Species.GALAR_PONYTA, Species.GARDEVOIR], - GORDIE: [Species.ROLYCOLY, Species.STONJOURNER, Species.BINACLE], - MELONY: [Species.SNOM, Species.GALAR_DARUMAKA, Species.GALAR_MR_MIME], - PIERS: [Species.GALAR_ZIGZAGOON, Species.SCRAGGY, Species.INKAY], - MARNIE: [Species.IMPIDIMP, Species.PURRLOIN, Species.MORPEKO], - RAIHAN: [Species.DURALUDON, Species.TURTONATOR, Species.GOOMY], + MILO: [Species.GOSSIFLEUR, Species.SEEDOT, Species.APPLIN, Species.LOTAD], + NESSA: [Species.CHEWTLE, Species.WIMPOD, Species.ARROKUDA, Species.MAREANIE], + KABU: [Species.SIZZLIPEDE, Species.VULPIX, Species.GROWLITHE, Species.TORKOAL], + BEA: [Species.MACHOP, Species.GALAR_FARFETCHD, Species.CLOBBOPUS, Species.FALINKS], + ALLISTER: [Species.GASTLY, Species.GALAR_YAMASK, Species.GALAR_CORSOLA, Species.SINISTEA], + OPAL: [Species.MILCERY, Species.GALAR_WEEZING, Species.TOGEPI, Species.MAWILE], + BEDE: [Species.HATENNA, Species.GALAR_PONYTA, Species.GARDEVOIR, Species.SYLVEON], + GORDIE: [Species.ROLYCOLY, [Species.SHUCKLE, Species.BINACLE], Species.STONJOURNER, Species.LARVITAR], + MELONY: [Species.LAPRAS, Species.SNOM, Species.EISCUE, [Species.GALAR_MR_MIME, Species.GALAR_DARUMAKA]], + PIERS: [Species.GALAR_ZIGZAGOON, Species.SCRAGGY, Species.TOXEL, Species.INKAY], // Tera Dark Toxel + MARNIE: [Species.IMPIDIMP, Species.MORPEKO, Species.PURRLOIN, Species.CROAGUNK], // Tera Dark Croagunk + RAIHAN: [Species.DURALUDON, Species.TRAPINCH, Species.GOOMY, Species.TURTONATOR], // Gym Leaders- Paldea; First slot is Tera - KATY: [Species.TEDDIURSA, Species.NYMBLE, Species.TAROUNTULA], // Tera Bug Teddiursa - BRASSIUS: [Species.SUDOWOODO, Species.BRAMBLIN, Species.SMOLIV], // Tera Grass Sudowoodo - IONO: [Species.MISDREAVUS, Species.TADBULB, Species.WATTREL], // Tera Ghost Misdreavus + KATY: [Species.TEDDIURSA, Species.NYMBLE, Species.TAROUNTULA, Species.RELLOR], // Tera Bug Teddiursa + BRASSIUS: [Species.BONSLY, Species.SMOLIV, Species.BRAMBLIN, Species.SUNKERN], // Tera Grass Bonsly + IONO: [Species.MISDREAVUS, Species.TADBULB, Species.WATTREL, Species.MAGNEMITE], // Tera Ghost Misdreavus KOFU: [Species.CRABRAWLER, Species.VELUZA, Species.WIGLETT, Species.WINGULL], // Tera Water Crabrawler LARRY: [Species.STARLY, Species.DUNSPARCE, Species.LECHONK, Species.KOMALA], // Tera Normal Starly RYME: [Species.TOXEL, Species.GREAVARD, Species.SHUPPET, Species.MIMIKYU], // Tera Ghost Toxel TULIP: [Species.FLABEBE, Species.FLITTLE, Species.RALTS, Species.GIRAFARIG], // Tera Psychic Flabebe - GRUSHA: [Species.SWABLU, Species.CETODDLE, Species.CUBCHOO, Species.ALOLA_VULPIX], // Tera Ice Swablu + GRUSHA: [Species.SWABLU, Species.CETODDLE, Species.SNOM, Species.CUBCHOO], // Tera Ice Swablu // Elite Four- Kanto LORELEI: [ diff --git a/src/data/trainers/trainer-config.ts b/src/data/trainers/trainer-config.ts index fec1d4757e7..a2e62e6761b 100644 --- a/src/data/trainers/trainer-config.ts +++ b/src/data/trainers/trainer-config.ts @@ -2579,252 +2579,252 @@ export const trainerConfigs: TrainerConfigs = { ), [TrainerType.BROCK]: new TrainerConfig((t = TrainerType.BROCK)) - .initForGymLeader(signatureSpecies["BROCK"], true, PokemonType.ROCK) + .initForGymLeader(signatureSpecies["BROCK"], true, PokemonType.ROCK, false, -1) .setBattleBgm("battle_kanto_gym") .setMixedBattleBgm("battle_kanto_gym"), [TrainerType.MISTY]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["MISTY"], false, PokemonType.WATER) + .initForGymLeader(signatureSpecies["MISTY"], false, PokemonType.WATER, false, -1) .setBattleBgm("battle_kanto_gym") .setMixedBattleBgm("battle_kanto_gym"), [TrainerType.LT_SURGE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["LT_SURGE"], true, PokemonType.ELECTRIC) + .initForGymLeader(signatureSpecies["LT_SURGE"], true, PokemonType.ELECTRIC, false, -1) .setBattleBgm("battle_kanto_gym") .setMixedBattleBgm("battle_kanto_gym"), [TrainerType.ERIKA]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["ERIKA"], false, PokemonType.GRASS) + .initForGymLeader(signatureSpecies["ERIKA"], false, PokemonType.GRASS, false, -1) .setBattleBgm("battle_kanto_gym") .setMixedBattleBgm("battle_kanto_gym"), [TrainerType.JANINE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["JANINE"], false, PokemonType.POISON) + .initForGymLeader(signatureSpecies["JANINE"], false, PokemonType.POISON, false, -1) .setBattleBgm("battle_kanto_gym") .setMixedBattleBgm("battle_kanto_gym"), [TrainerType.SABRINA]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["SABRINA"], false, PokemonType.PSYCHIC) + .initForGymLeader(signatureSpecies["SABRINA"], false, PokemonType.PSYCHIC, false, -1) .setBattleBgm("battle_kanto_gym") .setMixedBattleBgm("battle_kanto_gym"), [TrainerType.BLAINE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["BLAINE"], true, PokemonType.FIRE) + .initForGymLeader(signatureSpecies["BLAINE"], true, PokemonType.FIRE, false, -1) .setBattleBgm("battle_kanto_gym") .setMixedBattleBgm("battle_kanto_gym"), [TrainerType.GIOVANNI]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["GIOVANNI"], true, PokemonType.DARK) + .initForGymLeader(signatureSpecies["GIOVANNI"], true, PokemonType.GROUND, false, -2) .setBattleBgm("battle_kanto_gym") .setMixedBattleBgm("battle_kanto_gym"), [TrainerType.FALKNER]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["FALKNER"], true, PokemonType.FLYING) + .initForGymLeader(signatureSpecies["FALKNER"], true, PokemonType.FLYING, false, -1) .setBattleBgm("battle_johto_gym") .setMixedBattleBgm("battle_johto_gym"), [TrainerType.BUGSY]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["BUGSY"], true, PokemonType.BUG) + .initForGymLeader(signatureSpecies["BUGSY"], true, PokemonType.BUG, false, -1) .setBattleBgm("battle_johto_gym") .setMixedBattleBgm("battle_johto_gym"), [TrainerType.WHITNEY]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["WHITNEY"], false, PokemonType.NORMAL) + .initForGymLeader(signatureSpecies["WHITNEY"], false, PokemonType.NORMAL, false, -1) .setBattleBgm("battle_johto_gym") .setMixedBattleBgm("battle_johto_gym"), [TrainerType.MORTY]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["MORTY"], true, PokemonType.GHOST) + .initForGymLeader(signatureSpecies["MORTY"], true, PokemonType.GHOST, false, -1) .setBattleBgm("battle_johto_gym") .setMixedBattleBgm("battle_johto_gym"), [TrainerType.CHUCK]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["CHUCK"], true, PokemonType.FIGHTING) + .initForGymLeader(signatureSpecies["CHUCK"], true, PokemonType.FIGHTING, false, -1) .setBattleBgm("battle_johto_gym") .setMixedBattleBgm("battle_johto_gym"), [TrainerType.JASMINE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["JASMINE"], false, PokemonType.STEEL) + .initForGymLeader(signatureSpecies["JASMINE"], false, PokemonType.STEEL, false, -1) .setBattleBgm("battle_johto_gym") .setMixedBattleBgm("battle_johto_gym"), [TrainerType.PRYCE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["PRYCE"], true, PokemonType.ICE) + .initForGymLeader(signatureSpecies["PRYCE"], true, PokemonType.ICE, false, -1) .setBattleBgm("battle_johto_gym") .setMixedBattleBgm("battle_johto_gym"), [TrainerType.CLAIR]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["CLAIR"], false, PokemonType.DRAGON) + .initForGymLeader(signatureSpecies["CLAIR"], false, PokemonType.DRAGON, false, -3) .setBattleBgm("battle_johto_gym") .setMixedBattleBgm("battle_johto_gym"), [TrainerType.ROXANNE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["ROXANNE"], false, PokemonType.ROCK) + .initForGymLeader(signatureSpecies["ROXANNE"], false, PokemonType.ROCK, false, -1) .setBattleBgm("battle_hoenn_gym") .setMixedBattleBgm("battle_hoenn_gym"), [TrainerType.BRAWLY]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["BRAWLY"], true, PokemonType.FIGHTING) + .initForGymLeader(signatureSpecies["BRAWLY"], true, PokemonType.FIGHTING, false, -1) .setBattleBgm("battle_hoenn_gym") .setMixedBattleBgm("battle_hoenn_gym"), [TrainerType.WATTSON]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["WATTSON"], true, PokemonType.ELECTRIC) + .initForGymLeader(signatureSpecies["WATTSON"], true, PokemonType.ELECTRIC, false, -1) .setBattleBgm("battle_hoenn_gym") .setMixedBattleBgm("battle_hoenn_gym"), [TrainerType.FLANNERY]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["FLANNERY"], false, PokemonType.FIRE) + .initForGymLeader(signatureSpecies["FLANNERY"], false, PokemonType.FIRE, false, -1) .setBattleBgm("battle_hoenn_gym") .setMixedBattleBgm("battle_hoenn_gym"), [TrainerType.NORMAN]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["NORMAN"], true, PokemonType.NORMAL) + .initForGymLeader(signatureSpecies["NORMAN"], true, PokemonType.NORMAL, false, -1) .setBattleBgm("battle_hoenn_gym") .setMixedBattleBgm("battle_hoenn_gym"), [TrainerType.WINONA]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["WINONA"], false, PokemonType.FLYING) + .initForGymLeader(signatureSpecies["WINONA"], false, PokemonType.FLYING, false, -1) .setBattleBgm("battle_hoenn_gym") .setMixedBattleBgm("battle_hoenn_gym"), [TrainerType.TATE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["TATE"], true, PokemonType.PSYCHIC) + .initForGymLeader(signatureSpecies["TATE"], true, PokemonType.PSYCHIC, false, -1) .setBattleBgm("battle_hoenn_gym") .setMixedBattleBgm("battle_hoenn_gym") .setHasDouble("tate_liza_double") .setDoubleTrainerType(TrainerType.LIZA) .setDoubleTitle("gym_leader_double"), [TrainerType.LIZA]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["LIZA"], false, PokemonType.PSYCHIC) + .initForGymLeader(signatureSpecies["LIZA"], false, PokemonType.PSYCHIC, false, -1) .setBattleBgm("battle_hoenn_gym") .setMixedBattleBgm("battle_hoenn_gym") .setHasDouble("liza_tate_double") .setDoubleTrainerType(TrainerType.TATE) .setDoubleTitle("gym_leader_double"), [TrainerType.JUAN]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["JUAN"], true, PokemonType.WATER) + .initForGymLeader(signatureSpecies["JUAN"], true, PokemonType.WATER, false, -1) .setBattleBgm("battle_hoenn_gym") .setMixedBattleBgm("battle_hoenn_gym"), [TrainerType.ROARK]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["ROARK"], true, PokemonType.ROCK) + .initForGymLeader(signatureSpecies["ROARK"], true, PokemonType.ROCK, false, -1) .setBattleBgm("battle_sinnoh_gym") .setMixedBattleBgm("battle_sinnoh_gym"), [TrainerType.GARDENIA]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["GARDENIA"], false, PokemonType.GRASS) + .initForGymLeader(signatureSpecies["GARDENIA"], false, PokemonType.GRASS, false, -1) .setBattleBgm("battle_sinnoh_gym") .setMixedBattleBgm("battle_sinnoh_gym"), [TrainerType.MAYLENE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["MAYLENE"], false, PokemonType.FIGHTING) + .initForGymLeader(signatureSpecies["MAYLENE"], false, PokemonType.FIGHTING, false, -1) .setBattleBgm("battle_sinnoh_gym") .setMixedBattleBgm("battle_sinnoh_gym"), [TrainerType.CRASHER_WAKE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["CRASHER_WAKE"], true, PokemonType.WATER) + .initForGymLeader(signatureSpecies["CRASHER_WAKE"], true, PokemonType.WATER, false, -1) .setBattleBgm("battle_sinnoh_gym") .setMixedBattleBgm("battle_sinnoh_gym"), [TrainerType.FANTINA]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["FANTINA"], false, PokemonType.GHOST) + .initForGymLeader(signatureSpecies["FANTINA"], false, PokemonType.GHOST, false, -1) .setBattleBgm("battle_sinnoh_gym") .setMixedBattleBgm("battle_sinnoh_gym"), [TrainerType.BYRON]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["BYRON"], true, PokemonType.STEEL) + .initForGymLeader(signatureSpecies["BYRON"], true, PokemonType.STEEL, false, -1) .setBattleBgm("battle_sinnoh_gym") .setMixedBattleBgm("battle_sinnoh_gym"), [TrainerType.CANDICE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["CANDICE"], false, PokemonType.ICE) + .initForGymLeader(signatureSpecies["CANDICE"], false, PokemonType.ICE, false, -1) .setBattleBgm("battle_sinnoh_gym") .setMixedBattleBgm("battle_sinnoh_gym"), [TrainerType.VOLKNER]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["VOLKNER"], true, PokemonType.ELECTRIC) + .initForGymLeader(signatureSpecies["VOLKNER"], true, PokemonType.ELECTRIC, false, -1) .setBattleBgm("battle_sinnoh_gym") .setMixedBattleBgm("battle_sinnoh_gym"), [TrainerType.CILAN]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["CILAN"], true, PokemonType.GRASS) + .initForGymLeader(signatureSpecies["CILAN"], true, PokemonType.GRASS, false, -1) .setMixedBattleBgm("battle_unova_gym"), [TrainerType.CHILI]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["CHILI"], true, PokemonType.FIRE) + .initForGymLeader(signatureSpecies["CHILI"], true, PokemonType.FIRE, false, -1) .setMixedBattleBgm("battle_unova_gym"), [TrainerType.CRESS]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["CRESS"], true, PokemonType.WATER) + .initForGymLeader(signatureSpecies["CRESS"], true, PokemonType.WATER, false, -1) .setMixedBattleBgm("battle_unova_gym"), [TrainerType.CHEREN]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["CHEREN"], true, PokemonType.NORMAL) + .initForGymLeader(signatureSpecies["CHEREN"], true, PokemonType.NORMAL, false, -1) .setMixedBattleBgm("battle_unova_gym"), [TrainerType.LENORA]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["LENORA"], false, PokemonType.NORMAL) + .initForGymLeader(signatureSpecies["LENORA"], false, PokemonType.NORMAL, false, -1) .setMixedBattleBgm("battle_unova_gym"), [TrainerType.ROXIE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["ROXIE"], false, PokemonType.POISON) + .initForGymLeader(signatureSpecies["ROXIE"], false, PokemonType.POISON, false, -1) .setMixedBattleBgm("battle_unova_gym"), [TrainerType.BURGH]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["BURGH"], true, PokemonType.BUG) + .initForGymLeader(signatureSpecies["BURGH"], true, PokemonType.BUG, false, -1) .setMixedBattleBgm("battle_unova_gym"), [TrainerType.ELESA]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["ELESA"], false, PokemonType.ELECTRIC) + .initForGymLeader(signatureSpecies["ELESA"], false, PokemonType.ELECTRIC, false, -1) .setMixedBattleBgm("battle_unova_gym"), [TrainerType.CLAY]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["CLAY"], true, PokemonType.GROUND) + .initForGymLeader(signatureSpecies["CLAY"], true, PokemonType.GROUND, false, -1) .setMixedBattleBgm("battle_unova_gym"), [TrainerType.SKYLA]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["SKYLA"], false, PokemonType.FLYING) + .initForGymLeader(signatureSpecies["SKYLA"], false, PokemonType.FLYING, false, -1) .setMixedBattleBgm("battle_unova_gym"), [TrainerType.BRYCEN]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["BRYCEN"], true, PokemonType.ICE) + .initForGymLeader(signatureSpecies["BRYCEN"], true, PokemonType.ICE, false, -1) .setMixedBattleBgm("battle_unova_gym"), [TrainerType.DRAYDEN]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["DRAYDEN"], true, PokemonType.DRAGON) + .initForGymLeader(signatureSpecies["DRAYDEN"], true, PokemonType.DRAGON, false, -1) .setMixedBattleBgm("battle_unova_gym"), [TrainerType.MARLON]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["MARLON"], true, PokemonType.WATER) + .initForGymLeader(signatureSpecies["MARLON"], true, PokemonType.WATER, false, -1) .setMixedBattleBgm("battle_unova_gym"), [TrainerType.VIOLA]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["VIOLA"], false, PokemonType.BUG) + .initForGymLeader(signatureSpecies["VIOLA"], false, PokemonType.BUG, false, -1) .setMixedBattleBgm("battle_kalos_gym"), [TrainerType.GRANT]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["GRANT"], true, PokemonType.ROCK) + .initForGymLeader(signatureSpecies["GRANT"], true, PokemonType.ROCK, false, -1) .setMixedBattleBgm("battle_kalos_gym"), [TrainerType.KORRINA]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["KORRINA"], false, PokemonType.FIGHTING) + .initForGymLeader(signatureSpecies["KORRINA"], false, PokemonType.FIGHTING, false, -1) .setMixedBattleBgm("battle_kalos_gym"), [TrainerType.RAMOS]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["RAMOS"], true, PokemonType.GRASS) + .initForGymLeader(signatureSpecies["RAMOS"], true, PokemonType.GRASS, false, -1) .setMixedBattleBgm("battle_kalos_gym"), [TrainerType.CLEMONT]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["CLEMONT"], true, PokemonType.ELECTRIC) + .initForGymLeader(signatureSpecies["CLEMONT"], true, PokemonType.ELECTRIC, false, -1) .setMixedBattleBgm("battle_kalos_gym"), [TrainerType.VALERIE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["VALERIE"], false, PokemonType.FAIRY) + .initForGymLeader(signatureSpecies["VALERIE"], false, PokemonType.FAIRY, false, -1) .setMixedBattleBgm("battle_kalos_gym"), [TrainerType.OLYMPIA]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["OLYMPIA"], false, PokemonType.PSYCHIC) + .initForGymLeader(signatureSpecies["OLYMPIA"], false, PokemonType.PSYCHIC, false, -1) .setMixedBattleBgm("battle_kalos_gym"), [TrainerType.WULFRIC]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["WULFRIC"], true, PokemonType.ICE) + .initForGymLeader(signatureSpecies["WULFRIC"], true, PokemonType.ICE, false, -1) .setMixedBattleBgm("battle_kalos_gym"), [TrainerType.MILO]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["MILO"], true, PokemonType.GRASS) + .initForGymLeader(signatureSpecies["MILO"], true, PokemonType.GRASS, false, -1) .setMixedBattleBgm("battle_galar_gym"), [TrainerType.NESSA]: new TrainerConfig(++t) .setName("Nessa") - .initForGymLeader(signatureSpecies["NESSA"], false, PokemonType.WATER) + .initForGymLeader(signatureSpecies["NESSA"], false, PokemonType.WATER, false, -1) .setMixedBattleBgm("battle_galar_gym"), [TrainerType.KABU]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["KABU"], true, PokemonType.FIRE) + .initForGymLeader(signatureSpecies["KABU"], true, PokemonType.FIRE, false, -1) .setMixedBattleBgm("battle_galar_gym"), [TrainerType.BEA]: new TrainerConfig(++t) .setName("Bea") - .initForGymLeader(signatureSpecies["BEA"], false, PokemonType.FIGHTING) + .initForGymLeader(signatureSpecies["BEA"], false, PokemonType.FIGHTING, false, -1) .setMixedBattleBgm("battle_galar_gym"), [TrainerType.ALLISTER]: new TrainerConfig(++t) .setName("Allister") - .initForGymLeader(signatureSpecies["ALLISTER"], true, PokemonType.GHOST) + .initForGymLeader(signatureSpecies["ALLISTER"], true, PokemonType.GHOST, false, -1) .setMixedBattleBgm("battle_galar_gym"), [TrainerType.OPAL]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["OPAL"], false, PokemonType.FAIRY) + .initForGymLeader(signatureSpecies["OPAL"], false, PokemonType.FAIRY, false, -1) .setMixedBattleBgm("battle_galar_gym"), [TrainerType.BEDE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["BEDE"], true, PokemonType.FAIRY) + .initForGymLeader(signatureSpecies["BEDE"], true, PokemonType.FAIRY, false, -1) .setMixedBattleBgm("battle_galar_gym"), [TrainerType.GORDIE]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["GORDIE"], true, PokemonType.ROCK) + .initForGymLeader(signatureSpecies["GORDIE"], true, PokemonType.ROCK, false, -1) .setMixedBattleBgm("battle_galar_gym"), [TrainerType.MELONY]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["MELONY"], false, PokemonType.ICE) + .initForGymLeader(signatureSpecies["MELONY"], false, PokemonType.ICE, false, -1) .setMixedBattleBgm("battle_galar_gym"), [TrainerType.PIERS]: new TrainerConfig(++t) - .initForGymLeader(signatureSpecies["PIERS"], true, PokemonType.DARK) + .initForGymLeader(signatureSpecies["PIERS"], true, PokemonType.DARK, false, -3) .setHasDouble("piers_marnie_double") .setDoubleTrainerType(TrainerType.MARNIE) .setDoubleTitle("gym_leader_double") .setMixedBattleBgm("battle_galar_gym"), [TrainerType.MARNIE]: new TrainerConfig(++t) .setName("Marnie") - .initForGymLeader(signatureSpecies["MARNIE"], false, PokemonType.DARK) + .initForGymLeader(signatureSpecies["MARNIE"], false, PokemonType.DARK, false, -4) .setHasDouble("marnie_piers_double") .setDoubleTrainerType(TrainerType.PIERS) .setDoubleTitle("gym_leader_double") .setMixedBattleBgm("battle_galar_gym"), [TrainerType.RAIHAN]: new TrainerConfig(++t) .setName("Raihan") - .initForGymLeader(signatureSpecies["RAIHAN"], true, PokemonType.DRAGON) + .initForGymLeader(signatureSpecies["RAIHAN"], true, PokemonType.DRAGON, false, -1) .setMixedBattleBgm("battle_galar_gym"), [TrainerType.KATY]: new TrainerConfig(++t) .initForGymLeader(signatureSpecies["KATY"], false, PokemonType.BUG, true, -1) From 2cf0b51299ef78f1313410441d1ffd6458552813 Mon Sep 17 00:00:00 2001 From: Dean <69436131+emdeann@users.noreply.github.com> Date: Sun, 20 Apr 2025 11:14:19 -0700 Subject: [PATCH 40/52] [Bug] Properly handle suppression with Illusion (#5671) * Remove extra attributes on neutralizing gas * Add IllusionBreakAbAttr to applyOnLose * Add test case --- src/data/abilities/ability.ts | 45 ++++++++++++++++++--------------- test/abilities/illusion.test.ts | 14 ++++++++++ 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 55a1a4eb902..27c3cb69073 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -2219,18 +2219,6 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr { } } -/** - * Removes illusions when a Pokemon is summoned. - */ -export class PostSummonRemoveIllusionAbAttr extends PostSummonAbAttr { - applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - for (const pokemon of globalScene.getField(true)) { - pokemon.breakIllusion(); - } - return true; - } -} - export class PostSummonUnnamedMessageAbAttr extends PostSummonAbAttr { //Attr doesn't force pokemon name on the message private message: string; @@ -5177,7 +5165,14 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { } } -export class IllusionBreakAbAttr extends PostDefendAbAttr { +export class IllusionBreakAbAttr extends AbAttr { + override apply(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _cancelled: BooleanHolder | null, _args: any[]): void { + pokemon.breakIllusion(); + pokemon.summonData.illusionBroken = true; + } +} + +export class PostDefendIllusionBreakAbAttr extends PostDefendAbAttr { /** * Destroy the illusion upon taking damage * @@ -6269,7 +6264,7 @@ export function applyOnGainAbAttrs( } /** - * Clears primal weather/neutralizing gas during the turn if {@linkcode pokemon}'s ability corresponds to one + * Applies ability attributes which activate when the ability is lost or suppressed (i.e. primal weather) */ export function applyOnLoseAbAttrs(pokemon: Pokemon, passive = false, simulated = false, ...args: any[]): void { applySingleAbAttrs( @@ -6281,6 +6276,17 @@ export function applyOnLoseAbAttrs(pokemon: Pokemon, passive = false, simulated args, true, simulated); + + applySingleAbAttrs( + pokemon, + passive, + IllusionBreakAbAttr, + (attr, passive) => attr.apply(pokemon, passive, simulated, null, args), + (attr, passive) => attr.canApply(pokemon, passive, simulated, args), + args, + true, + simulated + ) } /** @@ -6780,11 +6786,12 @@ export function initAbilities() { return isNullOrUndefined(movePhase); }, 1.3), new Ability(Abilities.ILLUSION, 5) - //The pokemon generate an illusion if it's available + // The Pokemon generate an illusion if it's available .attr(IllusionPreSummonAbAttr, false) - //The pokemon loses his illusion when he is damaged by a move - .attr(IllusionBreakAbAttr, true) - //Illusion is available again after a battle + .attr(IllusionBreakAbAttr) + // The Pokemon loses its illusion when damaged by a move + .attr(PostDefendIllusionBreakAbAttr, true) + // Illusion is available again after a battle .conditionalAttr((pokemon) => pokemon.isAllowedInBattle(), IllusionPostBattleAbAttr, false) .uncopiable() .bypassFaint(), @@ -7198,8 +7205,6 @@ export function initAbilities() { .attr(PreLeaveFieldRemoveSuppressAbilitiesSourceAbAttr) .uncopiable() .attr(NoTransformAbilityAbAttr) - .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonNeutralizingGas", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) - .attr(PostSummonRemoveIllusionAbAttr) .bypassFaint(), new Ability(Abilities.PASTEL_VEIL, 8) .attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) diff --git a/test/abilities/illusion.test.ts b/test/abilities/illusion.test.ts index 382d7d74a08..b7c116a1b67 100644 --- a/test/abilities/illusion.test.ts +++ b/test/abilities/illusion.test.ts @@ -142,4 +142,18 @@ describe("Abilities - Illusion", () => { expect(zoroark.isShiny(true)).equals(true); expect(zoroark.getPokeball(true)).equals(PokeballType.GREAT_BALL); }); + + it("breaks when suppressed", async () => { + game.override.moveset(Moves.GASTRO_ACID); + await game.classicMode.startBattle([Species.MAGIKARP]); + const zorua = game.scene.getEnemyPokemon()!; + + expect(!!zorua.summonData?.illusion).toBe(true); + + game.move.select(Moves.GASTRO_ACID); + await game.phaseInterceptor.to(BerryPhase); + + expect(zorua.isFullHp()).toBe(true); + expect(!!zorua.summonData?.illusion).toBe(false); + }); }); From d0be6a9274862456ca806e0869833a45d9fe21b6 Mon Sep 17 00:00:00 2001 From: zaccie Date: Mon, 21 Apr 2025 06:33:17 +1200 Subject: [PATCH 41/52] [Bug] Fix order of operations when displaying enemy Boss level (#5685) * order of operations in creating boss battleInfo fixed a bug where because of an order of operations error in this file it ignored the position update of the boss life value set in battle-info.ts (around line 562) --- src/field/pokemon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index f2e5fd4c2b6..6356f723a79 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -7162,8 +7162,8 @@ export class EnemyPokemon extends Pokemon { initBattleInfo(): void { if (!this.battleInfo) { this.battleInfo = new EnemyBattleInfo(); - this.battleInfo.updateBossSegments(this); this.battleInfo.initInfo(this); + this.battleInfo.updateBossSegments(this); } else { this.battleInfo.updateBossSegments(this); } From b89b945b11ce8891b28f595cc7ab70266e08cb37 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sun, 20 Apr 2025 11:51:06 -0700 Subject: [PATCH 42/52] [Dev] Fix imports in `overrides.ts` and `illusion.test.ts` (#5686) --- src/overrides.ts | 8 ++++---- test/abilities/illusion.test.ts | 26 ++++++++++++-------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/overrides.ts b/src/overrides.ts index d36cfbfac98..7e6a46f2f85 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -2,10 +2,11 @@ import { type PokeballCounts } from "#app/battle-scene"; import { EvolutionItem } from "#app/data/balance/pokemon-evolutions"; import { Gender } from "#app/data/gender"; import { FormChangeItem } from "#app/data/pokemon-forms"; -import { Variant } from "#app/sprites/variant"; import { type ModifierOverride } from "#app/modifier/modifier-type"; +import { Variant } from "#app/sprites/variant"; import { Unlockables } from "#app/system/unlockables"; import { Abilities } from "#enums/abilities"; +import { BattleType } from "#enums/battle-type"; import { BerryType } from "#enums/berry-type"; import { Biome } from "#enums/biome"; import { EggTier } from "#enums/egg-type"; @@ -15,13 +16,12 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PokeballType } from "#enums/pokeball"; import { PokemonType } from "#enums/pokemon-type"; import { Species } from "#enums/species"; -import { BATTLE_STATS, Stat } from "#enums/stat"; +import { Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; import { TimeOfDay } from "#enums/time-of-day"; +import { TrainerType } from "#enums/trainer-type"; import { VariantTier } from "#enums/variant-tier"; import { WeatherType } from "#enums/weather-type"; -import { TrainerType } from "#enums/trainer-type"; -import { BattleType } from "#enums/battle-type"; /** * This comment block exists to prevent IDEs from automatically removing unused imports diff --git a/test/abilities/illusion.test.ts b/test/abilities/illusion.test.ts index b7c116a1b67..c743a59ef00 100644 --- a/test/abilities/illusion.test.ts +++ b/test/abilities/illusion.test.ts @@ -1,13 +1,11 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import Phaser from "phaser"; -import GameManager from "#test/testUtils/gameManager"; -import { Species } from "#enums/species"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import { Moves } from "#enums/moves"; -import { Abilities } from "#enums/abilities"; -import { PokeballType } from "#app/enums/pokeball"; import { Gender } from "#app/data/gender"; -import { BerryPhase } from "#app/phases/berry-phase"; +import { PokeballType } from "#app/enums/pokeball"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Abilities - Illusion", () => { let phaserGame: Phaser.Game; @@ -48,7 +46,7 @@ describe("Abilities - Illusion", () => { await game.classicMode.startBattle([Species.AXEW]); game.move.select(Moves.TACKLE); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); const zorua = game.scene.getEnemyPokemon()!; @@ -60,7 +58,7 @@ describe("Abilities - Illusion", () => { await game.classicMode.startBattle([Species.AXEW]); game.move.select(Moves.WORRY_SEED); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); const zorua = game.scene.getEnemyPokemon()!; @@ -114,7 +112,7 @@ describe("Abilities - Illusion", () => { game.move.select(Moves.FLARE_BLITZ); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); const zoroark = game.scene.getPlayerPokemon()!; @@ -132,7 +130,7 @@ describe("Abilities - Illusion", () => { game.doSwitchPokemon(1); - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("TurnEndPhase"); const zoroark = game.scene.getPlayerPokemon()!; @@ -151,7 +149,7 @@ describe("Abilities - Illusion", () => { expect(!!zorua.summonData?.illusion).toBe(true); game.move.select(Moves.GASTRO_ACID); - await game.phaseInterceptor.to(BerryPhase); + await game.phaseInterceptor.to("BerryPhase"); expect(zorua.isFullHp()).toBe(true); expect(!!zorua.summonData?.illusion).toBe(false); From 0da56cda9fef735490aeaca691a2fed60f97acf6 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Mon, 21 Apr 2025 13:46:32 -0500 Subject: [PATCH 43/52] [Bug][Sprite] Fix variant loading console spam (#5690) --- src/data/pokemon-species.ts | 42 ++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 95ff28e61e0..34efefd2849 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -27,7 +27,7 @@ import { } from "#app/data/balance/pokemon-level-moves"; import type { Stat } from "#enums/stat"; import type { Variant, VariantSet } from "#app/sprites/variant"; -import { populateVariantColorCache, variantData } from "#app/sprites/variant"; +import { populateVariantColorCache, variantColorCache, variantData } from "#app/sprites/variant"; import { speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters"; import { SpeciesFormKey } from "#enums/species-form-key"; import { starterPassiveAbilities } from "#app/data/balance/passives"; @@ -594,6 +594,34 @@ export abstract class PokemonSpeciesForm { return true; } + /** + * Load the variant colors for the species into the variant color cache + * + * @param spriteKey - The sprite key to use + * @param female - Whether to get + * + */ + async loadVariantColors(spriteKey: string, female: boolean, variant: Variant, formIndex?: number): Promise { + const baseSpriteKey = this.getBaseSpriteKey(female, formIndex); + + if (variantColorCache.hasOwnProperty(baseSpriteKey)) { + // Variant colors have already been loaded + return; + } + + const variantInfo = variantData[this.getVariantDataIndex(formIndex)]; + // Do nothing if there is no variant information or the variant does not have color replacements + if (!variantInfo || variantInfo[variant] !== 1) { + return; + } + + await populateVariantColorCache( + "pkmn__" + baseSpriteKey, + globalScene.experimentalSprites && hasExpSprite(spriteKey), + baseSpriteKey, + ); + } + async loadAssets( female: boolean, formIndex?: number, @@ -606,15 +634,9 @@ export abstract class PokemonSpeciesForm { const spriteKey = this.getSpriteKey(female, formIndex, shiny, variant, back); globalScene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant, back)); globalScene.load.audio(this.getCryKey(formIndex), `audio/${this.getCryKey(formIndex)}.m4a`); - - const baseSpriteKey = this.getBaseSpriteKey(female, formIndex); - - // Force the variant color cache to be loaded for the form - await populateVariantColorCache( - "pkmn__" + baseSpriteKey, - globalScene.experimentalSprites && hasExpSprite(spriteKey), - baseSpriteKey, - ); + if (!isNullOrUndefined(variant)) { + await this.loadVariantColors(spriteKey, female, variant, formIndex); + } return new Promise(resolve => { globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => { const originalWarn = console.warn; From be6a117b1e0716e5f3b0cd7c495ebe01bd0a4350 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Mon, 21 Apr 2025 14:52:08 -0500 Subject: [PATCH 44/52] [Bug][Sprite] Fix variants not using recolors for back sprite (#5691) Fix variants not showing back recolors with exp --- src/data/pokemon-species.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 34efefd2849..2fff2b562c0 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -404,7 +404,7 @@ export abstract class PokemonSpeciesForm { } /** Compute the sprite ID of the pokemon form. */ - getSpriteId(female: boolean, formIndex?: number, shiny?: boolean, variant = 0, back?: boolean): string { + getSpriteId(female: boolean, formIndex?: number, shiny?: boolean, variant = 0, back = false): string { const baseSpriteKey = this.getBaseSpriteKey(female, formIndex); let config = variantData; @@ -598,11 +598,21 @@ export abstract class PokemonSpeciesForm { * Load the variant colors for the species into the variant color cache * * @param spriteKey - The sprite key to use - * @param female - Whether to get + * @param female - Whether to load female instead of male + * @param back - Whether the back sprite is being loaded * */ - async loadVariantColors(spriteKey: string, female: boolean, variant: Variant, formIndex?: number): Promise { - const baseSpriteKey = this.getBaseSpriteKey(female, formIndex); + async loadVariantColors( + spriteKey: string, + female: boolean, + variant: Variant, + back = false, + formIndex?: number, + ): Promise { + let baseSpriteKey = this.getBaseSpriteKey(female, formIndex); + if (back) { + baseSpriteKey = "back__" + baseSpriteKey; + } if (variantColorCache.hasOwnProperty(baseSpriteKey)) { // Variant colors have already been loaded @@ -618,7 +628,7 @@ export abstract class PokemonSpeciesForm { await populateVariantColorCache( "pkmn__" + baseSpriteKey, globalScene.experimentalSprites && hasExpSprite(spriteKey), - baseSpriteKey, + baseSpriteKey.replace("__", "/"), ); } @@ -635,7 +645,7 @@ export abstract class PokemonSpeciesForm { globalScene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant, back)); globalScene.load.audio(this.getCryKey(formIndex), `audio/${this.getCryKey(formIndex)}.m4a`); if (!isNullOrUndefined(variant)) { - await this.loadVariantColors(spriteKey, female, variant, formIndex); + await this.loadVariantColors(spriteKey, female, variant, back, formIndex); } return new Promise(resolve => { globalScene.load.once(Phaser.Loader.Events.COMPLETE, () => { From aadb57ab75ab5af4355c50f7e5132bebd016a1e6 Mon Sep 17 00:00:00 2001 From: AJ Fontaine <36677462+Fontbane@users.noreply.github.com> Date: Tue, 22 Apr 2025 20:03:49 -0400 Subject: [PATCH 45/52] [Balance] [Mystery] Salesman ME offers mons from event encounter pool (#5674) * Initial event commit * Salesman odds * Clean up imports * globalScene shiny rate getter, fix reroll, remove placeholder event * Rerolling shiny also tries rerolling for better variant * Shiny reroll affects 'trainer' mons too --------- Co-authored-by: damocleas Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> --- .../the-pokemon-salesman-encounter.ts | 49 ++++++++++++++++--- .../utils/encounter-phase-utils.ts | 4 +- src/field/pokemon.ts | 32 ++++++------ 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts index 4e8e1c2524e..cfff59b45f5 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -3,7 +3,7 @@ import { transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; -import { isNullOrUndefined, randSeedInt } from "#app/utils/common"; +import { isNullOrUndefined, NumberHolder, randSeedInt, randSeedItem } from "#app/utils/common"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { globalScene } from "#app/global-scene"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; @@ -28,7 +28,8 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { Abilities } from "#enums/abilities"; -import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups"; +import { NON_LEGEND_PARADOX_POKEMON, NON_LEGEND_ULTRA_BEASTS } from "#app/data/balance/special-species-groups"; +import { timedEventManager } from "#app/global-event-manager"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounters/thePokemonSalesman"; @@ -38,6 +39,9 @@ const MAX_POKEMON_PRICE_MULTIPLIER = 4; /** Odds of shiny magikarp will be 1/value */ const SHINY_MAGIKARP_WEIGHT = 100; +/** Odds of event sale will be value/100 */ +const EVENT_THRESHOLD = 50; + /** * Pokemon Salesman encounter. * @see {@link https://github.com/pagefaultgames/pokerogue/issues/3799 | GitHub Issue #3799} @@ -82,15 +86,46 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = MysteryEncounterBui tries++; } + const r = randSeedInt(SHINY_MAGIKARP_WEIGHT); + + const validEventEncounters = timedEventManager + .getEventEncounters() + .filter( + s => + !getPokemonSpecies(s.species).legendary && + !getPokemonSpecies(s.species).subLegendary && + !getPokemonSpecies(s.species).mythical && + !NON_LEGEND_PARADOX_POKEMON.includes(s.species) && + !NON_LEGEND_ULTRA_BEASTS.includes(s.species), + ); + let pokemon: PlayerPokemon; + /** + * Mon is determined as follows: + * If you roll the 1% for Shiny Magikarp, you get Magikarp with a random variant + * If an event with more than 1 valid event encounter species is active, you have 20% chance to get one of those + * If the rolled species has no HA, and there are valid event encounters, you will get one of those + * If the rolled species has no HA and there are no valid event encounters, you will get Shiny Magikarp + * Mons rolled from the event encounter pool get 2 extra shiny rolls + */ if ( - randSeedInt(SHINY_MAGIKARP_WEIGHT) === 0 || - isNullOrUndefined(species.abilityHidden) || - species.abilityHidden === Abilities.NONE + r === 0 || + ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) && + (validEventEncounters.length === 0)) ) { - // If no HA mon found or you roll 1%, give shiny Magikarp with random variant + // If you roll 1%, give shiny Magikarp with random variant species = getPokemonSpecies(Species.MAGIKARP); - pokemon = new PlayerPokemon(species, 5, 2, species.formIndex, undefined, true); + pokemon = new PlayerPokemon(species, 5, 2, undefined, undefined, true); + } else if ( + (validEventEncounters.length > 0 && (r <= EVENT_THRESHOLD || + (isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE))) + ) { + // If you roll 20%, give event encounter with 2 extra shiny rolls and its HA, if it has one + const enc = randSeedItem(validEventEncounters); + species = getPokemonSpecies(enc.species); + pokemon = new PlayerPokemon(species, 5, species.abilityHidden === Abilities.NONE ? undefined : 2, enc.formIndex); + pokemon.trySetShinySeed(); + pokemon.trySetShinySeed(); } else { pokemon = new PlayerPokemon(species, 5, 2, species.formIndex); } diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index d77b70caa31..65051b937f8 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -1075,8 +1075,8 @@ export function getRandomEncounterSpecies(level: number, isBoss = false, rerollH ret.formIndex = formIndex; } - //Reroll shiny for event encounters - if (isEventEncounter && !ret.shiny) { + //Reroll shiny or variant for event encounters + if (isEventEncounter) { ret.trySetShinySeed(); } //Reroll hidden ability diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 6356f723a79..86d74ea5555 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -3170,7 +3170,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * Function that tries to set a Pokemon shiny based on seed. * For manual use only, usually to roll a Pokemon's shiny chance a second time. - * If it rolls shiny, also sets a random variant and give the Pokemon the associated luck. + * If it rolls shiny, or if it's already shiny, also sets a random variant and give the Pokemon the associated luck. * * The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / `65536` * @param thresholdOverride number that is divided by `2^16` (`65536`) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm) @@ -3181,29 +3181,31 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { thresholdOverride?: number, applyModifiersToOverride?: boolean, ): boolean { - const shinyThreshold = new NumberHolder(BASE_SHINY_CHANCE); - if (thresholdOverride === undefined || applyModifiersToOverride) { - if (thresholdOverride !== undefined && applyModifiersToOverride) { - shinyThreshold.value = thresholdOverride; - } - if (timedEventManager.isEventActive()) { - shinyThreshold.value *= timedEventManager.getShinyMultiplier(); - } - if (!this.hasTrainer()) { + if (!this.shiny) { + const shinyThreshold = new NumberHolder(BASE_SHINY_CHANCE); + if (thresholdOverride === undefined || applyModifiersToOverride) { + if (thresholdOverride !== undefined && applyModifiersToOverride) { + shinyThreshold.value = thresholdOverride; + } + if (timedEventManager.isEventActive()) { + shinyThreshold.value *= timedEventManager.getShinyMultiplier(); + } globalScene.applyModifiers( ShinyRateBoosterModifier, true, shinyThreshold, ); } - } else { - shinyThreshold.value = thresholdOverride; + else { + shinyThreshold.value = thresholdOverride; + } + + this.shiny = randSeedInt(65536) < shinyThreshold.value; } - this.shiny = randSeedInt(65536) < shinyThreshold.value; - if (this.shiny) { - this.variant = this.generateShinyVariant(); + this.variant = this.variant ?? 0; + this.variant = Math.max(this.generateShinyVariant(), this.variant) as Variant; // Don't set a variant lower than the current one this.luck = this.variant + 1 + (this.fusionShiny ? this.fusionVariant + 1 : 0); this.initShinySparkle(); From 110fd2f0a1888787f7b267f71b233ed1d1a63675 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Tue, 22 Apr 2025 19:10:27 -0500 Subject: [PATCH 46/52] [Refactor][Move] Refactor move effect phase (#5678) * Add enum for hit check result Co-authored-by: innerthunder * Refactor parameter list for pokemon#getBaseDamage and pokemon#getAttackDamage * Rewrite move phase Co-authored-by: innerthunder * Update tests to reflect move effect phase changes Co-authored-by: innerthunder * Fix pluck / bug bite Co-authored-by: innerthunder * Fix reviver seed ohko, remove leftover dead code Co-authored-by: innerthunder * Cleanup jsdoc comments * Remove hitsSubstitute check from postDefend abilities * Fix improper i18nkey in moveEffectPhase#applyToTargets * Cleanup comments * Fix type issue with substitute test * Move MYSTERY_ENCOUNTER_WAVES to constants.ts * Update linkcode in damageparams to use proper tsdoc syntax --------- Co-authored-by: innerthunder --- src/constants.ts | 5 + src/data/abilities/ability.ts | 44 +- src/data/arena-tag.ts | 10 +- src/data/battler-tags.ts | 6 +- src/data/moves/move-utils.ts | 20 + src/data/moves/move.ts | 172 +-- .../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/clowning-around-encounter.ts | 2 +- .../encounters/dancing-lessons-encounter.ts | 2 +- .../encounters/dark-deal-encounter.ts | 2 +- .../encounters/delibirdy-encounter.ts | 2 +- .../department-store-sale-encounter.ts | 2 +- .../encounters/field-trip-encounter.ts | 2 +- .../encounters/fiery-fallout-encounter.ts | 2 +- .../encounters/fight-or-flight-encounter.ts | 2 +- .../encounters/fun-and-games-encounter.ts | 2 +- .../global-trade-system-encounter.ts | 2 +- .../encounters/lost-at-sea-encounter.ts | 2 +- .../mysterious-challengers-encounter.ts | 2 +- .../encounters/mysterious-chest-encounter.ts | 2 +- .../encounters/part-timer-encounter.ts | 2 +- .../encounters/safari-zone-encounter.ts | 2 +- .../shady-vitamin-dealer-encounter.ts | 2 +- .../slumbering-snorlax-encounter.ts | 2 +- .../teleporting-hijinks-encounter.ts | 2 +- .../the-expert-pokemon-breeder-encounter.ts | 2 +- .../the-pokemon-salesman-encounter.ts | 2 +- .../encounters/the-strong-stuff-encounter.ts | 2 +- .../the-winstrate-challenge-encounter.ts | 2 +- .../encounters/training-session-encounter.ts | 2 +- .../encounters/trash-to-treasure-encounter.ts | 2 +- .../encounters/uncommon-breed-encounter.ts | 2 +- src/enums/MoveEffectTrigger.ts | 1 - src/enums/hit-check-result.ts | 23 + src/field/pokemon.ts | 399 ++---- src/game-mode.ts | 5 +- src/phases/faint-phase.ts | 10 +- src/phases/move-effect-phase.ts | 1166 +++++++++-------- src/phases/move-phase.ts | 5 +- test/abilities/friend_guard.test.ts | 11 +- test/abilities/galvanize.test.ts | 27 +- test/abilities/infiltrator.test.ts | 4 +- test/abilities/no_guard.test.ts | 4 +- test/abilities/shield_dust.test.ts | 2 +- test/abilities/super_luck.test.ts | 1 - test/abilities/tera_shell.test.ts | 14 +- test/battle/damage_calculation.test.ts | 8 +- test/battlerTags/substitute.test.ts | 8 +- test/items/dire_hit.test.ts | 3 +- test/items/leek.test.ts | 1 - test/items/scope_lens.test.ts | 3 +- test/moves/dig.test.ts | 10 +- test/moves/dynamax_cannon.test.ts | 16 +- test/moves/fusion_flare_bolt.test.ts | 36 +- test/moves/spectral_thief.test.ts | 4 +- test/moves/tera_blast.test.ts | 27 +- test/testUtils/helpers/moveHelper.ts | 20 +- 61 files changed, 1057 insertions(+), 1068 deletions(-) create mode 100644 src/data/moves/move-utils.ts create mode 100644 src/enums/hit-check-result.ts diff --git a/src/constants.ts b/src/constants.ts index 927575c0a28..dc901e4a766 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -9,3 +9,8 @@ export const SESSION_ID_COOKIE_NAME: string = "pokerogue_sessionId"; /** Max value for an integer attribute in {@linkcode SystemSaveData} */ export const MAX_INT_ATTR_VALUE = 0x80000000; + +/** The min and max waves for mystery encounters to spawn in classic mode */ +export const CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES: [number, number] = [10, 180] as const; +/** The min and max waves for mystery encounters to spawn in challenge mode */ +export const CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES: [number, number] = [10, 180] as const; diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 27c3cb69073..53d024ac655 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -653,8 +653,8 @@ export class MoveImmunityStatStageChangeAbAttr extends MoveImmunityAbAttr { */ export class ReverseDrainAbAttr extends PostDefendAbAttr { - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return move.hasAttr(HitHealAttr) && !move.hitsSubstitute(attacker, pokemon); + override canApplyPostDefend(_pokemon: Pokemon, _passive: boolean, _simulated: boolean, _attacker: Pokemon, move: Move, _hitResult: HitResult | null, args: any[]): boolean { + return move.hasAttr(HitHealAttr); } /** @@ -693,7 +693,7 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon); + return this.condition(pokemon, attacker, move); } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { @@ -734,7 +734,7 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { 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) && !move.hitsSubstitute(attacker, pokemon); + 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 { @@ -757,7 +757,7 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr { 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) && !move.hitsSubstitute(attacker, pokemon)) + return (this.condition(pokemon, attacker, move)) && (!globalScene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers); } @@ -779,7 +779,7 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon); + return this.condition(pokemon, attacker, move); } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { @@ -796,7 +796,7 @@ export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr { 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 && !move.hitsSubstitute(attacker, pokemon) && (simulated || pokemonTypes.length !== 1 || pokemonTypes[0] !== this.type); + 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 { @@ -823,7 +823,7 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { } override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - return hitResult < HitResult.NO_EFFECT && !move.hitsSubstitute(attacker, pokemon) && globalScene.arena.canSetTerrain(this.terrainType); + 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 { @@ -847,7 +847,7 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { 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.randSeedInt(this.effects.length)]; return move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && !attacker.status - && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !move.hitsSubstitute(attacker, pokemon) + && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && attacker.canSetStatus(effect, true, false, pokemon); } @@ -887,7 +887,7 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { 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.randSeedInt(100) < this.chance - && !move.hitsSubstitute(attacker, pokemon) && attacker.canAddTag(this.tagType); + && attacker.canAddTag(this.tagType); } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { @@ -908,10 +908,6 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { this.stages = stages; } - override canApplyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean { - return !move.hitsSubstitute(attacker, pokemon); - } - 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)); @@ -934,7 +930,7 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { 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) && !move.hitsSubstitute(attacker, pokemon); + && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr); } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void { @@ -993,7 +989,7 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { } 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) || move.hitsSubstitute(attacker, pokemon)) + return (!(this.condition && !this.condition(pokemon, attacker, move)) && !globalScene.arena.weather?.isImmutable() && globalScene.arena.canSetWeather(this.weatherType)); } @@ -1011,7 +1007,7 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { 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 && !move.hitsSubstitute(attacker, pokemon); + && attacker.getAbility().isSwappable; } override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, args: any[]): void { @@ -1037,10 +1033,10 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { 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) && !move.hitsSubstitute(attacker, pokemon); + && !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]); } @@ -1066,7 +1062,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { } 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.hitsSubstitute(attacker, pokemon) + return attacker.getTag(BattlerTagType.DISABLED) === null && move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon}) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance); } @@ -1770,7 +1766,6 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr { 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) - && !(pokemon !== attacker && move.hitsSubstitute(attacker, pokemon)) && (simulated || !attacker.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && pokemon !== attacker && (!this.contactRequired || move.doesFlagEffectApply({flag: MoveFlags.MAKES_CONTACT, user: attacker, target: pokemon})) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) ) { @@ -1837,8 +1832,7 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { if ( !simulated && hitResult < HitResult.NO_EFFECT && - (!this.condition || this.condition(pokemon, attacker, move)) && - !move.hitsSubstitute(attacker, pokemon) + (!this.condition || this.condition(pokemon, attacker, move)) ) { const heldItems = this.getTargetHeldItems(attacker).filter((i) => i.isTransferable); if (heldItems.length) { @@ -5063,6 +5057,8 @@ export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageC /** * Takes no damage from the first hit of a damaging move. * This is used in the Disguise and Ice Face abilities. + * + * Does not apply to a user's substitute * @extends ReceivedMoveDamageMultiplierAbAttr */ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { @@ -7410,4 +7406,4 @@ export function initAbilities() { .unreplaceable() // TODO is this true? .attr(ConfusionOnStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) ); -} +} \ No newline at end of file diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 2ef98723cea..ff9e4068292 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -7,7 +7,7 @@ import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; import { getPokemonNameWithAffix } from "#app/messages"; import type Pokemon from "#app/field/pokemon"; -import { HitResult, PokemonMove } from "#app/field/pokemon"; +import { HitResult } from "#app/field/pokemon"; import { StatusEffect } from "#enums/status-effect"; import type { BattlerIndex } from "#app/battle"; import { @@ -335,7 +335,7 @@ export class ConditionalProtectTag extends ArenaTag { * @param arena the {@linkcode Arena} containing this tag * @param simulated `true` if the tag is applied quietly; `false` otherwise. * @param isProtected a {@linkcode BooleanHolder} used to flag if the move is protected against - * @param attacker the attacking {@linkcode Pokemon} + * @param _attacker the attacking {@linkcode Pokemon} * @param defender the defending {@linkcode Pokemon} * @param moveId the {@linkcode Moves | identifier} for the move being used * @param ignoresProtectBypass a {@linkcode BooleanHolder} used to flag if a protection effect supercedes effects that ignore protection @@ -345,7 +345,7 @@ export class ConditionalProtectTag extends ArenaTag { arena: Arena, simulated: boolean, isProtected: BooleanHolder, - attacker: Pokemon, + _attacker: Pokemon, defender: Pokemon, moveId: Moves, ignoresProtectBypass: BooleanHolder, @@ -354,8 +354,6 @@ export class ConditionalProtectTag extends ArenaTag { if (!isProtected.value) { isProtected.value = true; if (!simulated) { - attacker.stopMultiHit(defender); - new CommonBattleAnim(CommonAnim.PROTECT, defender).play(); globalScene.queueMessage( i18next.t("arenaTag:conditionalProtectApply", { @@ -899,7 +897,7 @@ export class DelayedAttackTag extends ArenaTag { if (!ret) { globalScene.unshiftPhase( - new MoveEffectPhase(this.sourceId!, [this.targetIndex], new PokemonMove(this.sourceMove!, 0, 0, true)), + new MoveEffectPhase(this.sourceId!, [this.targetIndex], allMoves[this.sourceMove!], false, true), ); // TODO: are those bangs correct? } diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 3b2421897c9..ee41f0435b9 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -2637,7 +2637,7 @@ export class GulpMissileTag extends BattlerTag { return false; } - if (moveEffectPhase.move.getMove().hitsSubstitute(attacker, pokemon)) { + if (moveEffectPhase.move.hitsSubstitute(attacker, pokemon)) { return true; } @@ -2993,7 +2993,7 @@ export class SubstituteTag extends BattlerTag { if (!attacker) { return; } - const move = moveEffectPhase.move.getMove(); + const move = moveEffectPhase.move; const firstHit = attacker.turnData.hitCount === attacker.turnData.hitsLeft; if (firstHit && move.hitsSubstitute(attacker, pokemon)) { @@ -3681,7 +3681,7 @@ function getMoveEffectPhaseData(_pokemon: Pokemon): { phase: MoveEffectPhase; at return { phase: phase, attacker: phase.getPokemon(), - move: phase.move.getMove(), + move: phase.move, }; } return null; diff --git a/src/data/moves/move-utils.ts b/src/data/moves/move-utils.ts new file mode 100644 index 00000000000..3323d6f4a0c --- /dev/null +++ b/src/data/moves/move-utils.ts @@ -0,0 +1,20 @@ +import { MoveTarget } from "#enums/MoveTarget"; +import type Move from "./move"; + +/** + * Return whether the move targets the field + * + * Examples include + * - Hazard moves like spikes + * - Weather moves like rain dance + * - User side moves like reflect and safeguard + */ +export function isFieldTargeted(move: Move): boolean { + switch (move.moveTarget) { + case MoveTarget.BOTH_SIDES: + case MoveTarget.USER_SIDE: + case MoveTarget.ENEMY_SIDE: + return true; + } + return false; +} diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 26654fee18f..35d98f6f781 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -60,6 +60,7 @@ import { MoveTypeChangeAbAttr, PostDamageForceSwitchAbAttr, PostItemLostAbAttr, + ReflectStatusMoveAbAttr, ReverseDrainAbAttr, UserFieldMoveTypePowerBoostAbAttr, VariableMovePowerAbAttr, @@ -665,6 +666,17 @@ export default class Move implements Localizable { return true; } break; + case MoveFlags.REFLECTABLE: + // If the target is not semi-invulnerable and either has magic coat active or an unignored magic bounce ability + if ( + target?.getTag(SemiInvulnerableTag) || + !(target?.getTag(BattlerTagType.MAGIC_COAT) || + (!this.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user, target }) && + target?.hasAbilityWithAttr(ReflectStatusMoveAbAttr))) + ) { + return false; + } + break; } return !!(this.flags & flag); @@ -1716,7 +1728,7 @@ export class SacrificialAttr extends MoveEffectAttr { **/ export class SacrificialAttrOnHit extends MoveEffectAttr { constructor() { - super(true, { trigger: MoveEffectTrigger.HIT }); + super(true); } /** @@ -1955,6 +1967,14 @@ export class PartyStatusCureAttr extends MoveEffectAttr { * @extends MoveEffectAttr */ export class FlameBurstAttr extends MoveEffectAttr { + constructor() { + /** + * This is self-targeted to bypass immunity to target-facing secondary + * effects when the target has an active Substitute doll. + * TODO: Find a more intuitive way to implement Substitute bypassing. + */ + super(true); + } /** * @param user - n/a * @param target - The target Pokémon. @@ -2177,7 +2197,7 @@ export class HitHealAttr extends MoveEffectAttr { private healStat: EffectiveStat | null; constructor(healRatio?: number | null, healStat?: EffectiveStat) { - super(true, { trigger: MoveEffectTrigger.HIT }); + super(true); this.healRatio = healRatio ?? 0.5; this.healStat = healStat ?? null; @@ -2426,7 +2446,7 @@ export class StatusEffectAttr extends MoveEffectAttr { public overrideStatus: boolean = false; constructor(effect: StatusEffect, selfTarget?: boolean, turnsRemaining?: number, overrideStatus: boolean = false) { - super(selfTarget, { trigger: MoveEffectTrigger.HIT }); + super(selfTarget); this.effect = effect; this.turnsRemaining = turnsRemaining; @@ -2434,10 +2454,6 @@ export class StatusEffectAttr extends MoveEffectAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - if (!this.selfTarget && move.hitsSubstitute(user, target)) { - return false; - } - const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); const statusCheck = moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance; if (statusCheck) { @@ -2495,7 +2511,7 @@ export class MultiStatusEffectAttr extends StatusEffectAttr { export class PsychoShiftEffectAttr extends MoveEffectAttr { constructor() { - super(false, { trigger: MoveEffectTrigger.HIT }); + super(false); } /** @@ -2534,15 +2550,11 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr { private chance: number; constructor(chance: number) { - super(false, { trigger: MoveEffectTrigger.HIT }); + super(false); this.chance = chance; } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - if (move.hitsSubstitute(user, target)) { - return false; - } - const rand = Phaser.Math.RND.realInRange(0, 1); if (rand >= this.chance) { return false; @@ -2590,7 +2602,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { private berriesOnly: boolean; constructor(berriesOnly: boolean) { - super(false, { trigger: MoveEffectTrigger.HIT }); + super(false); this.berriesOnly = berriesOnly; } @@ -2600,17 +2612,13 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { * @param target Target {@linkcode Pokemon} that the moves applies to * @param move {@linkcode Move} that is used * @param args N/A - * @returns {boolean} True if an item was removed + * @returns True if an item was removed */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!this.berriesOnly && target.isPlayer()) { // "Wild Pokemon cannot knock off Player Pokemon's held items" (See Bulbapedia) return false; } - if (move.hitsSubstitute(user, target)) { - return false; - } - const cancelled = new BooleanHolder(false); applyAbAttrs(BlockItemTheftAbAttr, target, cancelled); // Check for abilities that block item theft @@ -2664,8 +2672,8 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { */ export class EatBerryAttr extends MoveEffectAttr { protected chosenBerry: BerryModifier | undefined; - constructor() { - super(true, { trigger: MoveEffectTrigger.HIT }); + constructor(selfTarget: boolean) { + super(selfTarget); } /** * Causes the target to eat a berry. @@ -2680,17 +2688,20 @@ export class EatBerryAttr extends MoveEffectAttr { return false; } - const heldBerries = this.getTargetHeldBerries(target); + const pokemon = this.selfTarget ? user : target; + + const heldBerries = this.getTargetHeldBerries(pokemon); if (heldBerries.length <= 0) { return false; } this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)]; const preserve = new BooleanHolder(false); - globalScene.applyModifiers(PreserveBerryModifier, target.isPlayer(), target, preserve); // check for berry pouch preservation + // check for berry pouch preservation + globalScene.applyModifiers(PreserveBerryModifier, pokemon.isPlayer(), pokemon, preserve); if (!preserve.value) { - this.reduceBerryModifier(target); + this.reduceBerryModifier(pokemon); } - this.eatBerry(target); + this.eatBerry(pokemon); return true; } @@ -2718,20 +2729,17 @@ export class EatBerryAttr extends MoveEffectAttr { */ export class StealEatBerryAttr extends EatBerryAttr { constructor() { - super(); + super(false); } /** * User steals a random berry from the target and then eats it. - * @param {Pokemon} user Pokemon that used the move and will eat the stolen berry - * @param {Pokemon} target Pokemon that will have its berry stolen - * @param {Move} move Move being used - * @param {any[]} args Unused - * @returns {boolean} true if the function succeeds + * @param user - Pokemon that used the move and will eat the stolen berry + * @param target - Pokemon that will have its berry stolen + * @param move - Move being used + * @param args Unused + * @returns true if the function succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - if (move.hitsSubstitute(user, target)) { - return false; - } const cancelled = new BooleanHolder(false); applyAbAttrs(BlockItemTheftAbAttr, target, cancelled); // check for abilities that block item theft if (cancelled.value === true) { @@ -2782,10 +2790,6 @@ export class HealStatusEffectAttr extends MoveEffectAttr { return false; } - if (!this.selfTarget && move.hitsSubstitute(user, target)) { - return false; - } - // Special edge case for shield dust blocking Sparkling Aria curing burn const moveTargets = getMoveTargets(user, move.id); if (target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && move.id === Moves.SPARKLING_ARIA && moveTargets.targets.length === 1) { @@ -3162,15 +3166,7 @@ export class StatStageChangeAttr extends MoveEffectAttr { private get showMessage () { return this.options?.showMessage ?? true; } - - /** - * Indicates when the stat change should trigger - * @default MoveEffectTrigger.HIT - */ - public override get trigger () { - return this.options?.trigger ?? MoveEffectTrigger.HIT; - } - + /** * Attempts to change stats of the user or target (depending on value of selfTarget) if conditions are met * @param user {@linkcode Pokemon} the user of the move @@ -3184,10 +3180,6 @@ export class StatStageChangeAttr extends MoveEffectAttr { return false; } - if (!this.selfTarget && move.hitsSubstitute(user, target)) { - return false; - } - const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) { const stages = this.getLevels(user); @@ -3471,7 +3463,7 @@ export class CutHpStatStageBoostAttr extends StatStageChangeAttr { */ export class OrderUpStatBoostAttr extends MoveEffectAttr { constructor() { - super(true, { trigger: MoveEffectTrigger.HIT }); + super(true); } override apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean { @@ -3548,17 +3540,15 @@ export class ResetStatsAttr extends MoveEffectAttr { this.targetAllPokemon = targetAllPokemon; } - override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + override apply(_user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { if (this.targetAllPokemon) { // 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")); } else { // Affects only the single target when Clear Smog is used - if (!move.hitsSubstitute(user, target)) { - this.resetStats(target); - globalScene.queueMessage(i18next.t("moveTriggers:resetStats", { pokemonName: getPokemonNameWithAffix(target) })); - } + this.resetStats(target); + globalScene.queueMessage(i18next.t("moveTriggers:resetStats", { pokemonName: getPokemonNameWithAffix(target) })); } return true; } @@ -4217,7 +4207,8 @@ export class PresentPowerAttr extends VariablePowerAttr { (args[0] as NumberHolder).value = 120; } else if (80 < powerSeed && powerSeed <= 100) { // If this move is multi-hit, disable all other hits - user.stopMultiHit(); + user.turnData.hitCount = 1; + user.turnData.hitsLeft = 1; globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(), toDmgValue(target.getMaxHp() / 4), i18next.t("moveTriggers:regainedHealth", { pokemonName: getPokemonNameWithAffix(target) }), true)); } @@ -4811,8 +4802,8 @@ export class ShellSideArmCategoryAttr extends VariableMoveCategoryAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const category = (args[0] as NumberHolder); - const predictedPhysDmg = target.getBaseDamage(user, move, MoveCategory.PHYSICAL, true, true, true, true); - const predictedSpecDmg = target.getBaseDamage(user, move, MoveCategory.SPECIAL, true, true, true, true); + const predictedPhysDmg = target.getBaseDamage({source: user, move, moveCategory: MoveCategory.PHYSICAL, ignoreAbility: true, ignoreSourceAbility: true, ignoreAllyAbility: true, ignoreSourceAllyAbility: true, simulated: true}); + const predictedSpecDmg = target.getBaseDamage({source: user, move, moveCategory: MoveCategory.SPECIAL, ignoreAbility: true, ignoreSourceAbility: true, ignoreAllyAbility: true, ignoreSourceAllyAbility: true, simulated: true}); if (predictedPhysDmg > predictedSpecDmg) { category.value = MoveCategory.PHYSICAL; @@ -5371,7 +5362,7 @@ export class BypassRedirectAttr extends MoveAttr { export class FrenzyAttr extends MoveEffectAttr { constructor() { - super(true, { trigger: MoveEffectTrigger.HIT, lastHitOnly: true }); + super(true, { lastHitOnly: true }); } canApply(user: Pokemon, target: Pokemon, move: Move, args: any[]) { @@ -5443,22 +5434,20 @@ export class AddBattlerTagAttr extends MoveEffectAttr { protected cancelOnFail: boolean; private failOnOverlap: boolean; - constructor(tagType: BattlerTagType, selfTarget: boolean = false, failOnOverlap: boolean = false, turnCountMin: number = 0, turnCountMax?: number, lastHitOnly: boolean = false, cancelOnFail: boolean = false) { + constructor(tagType: BattlerTagType, selfTarget: boolean = false, failOnOverlap: boolean = false, turnCountMin: number = 0, turnCountMax?: number, lastHitOnly: boolean = false) { super(selfTarget, { lastHitOnly: lastHitOnly }); this.tagType = tagType; this.turnCountMin = turnCountMin; this.turnCountMax = turnCountMax !== undefined ? turnCountMax : turnCountMin; this.failOnOverlap = !!failOnOverlap; - this.cancelOnFail = cancelOnFail; } canApply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - if (!super.canApply(user, target, move, args) || (this.cancelOnFail === true && user.getLastXMoves(1)[0]?.result === MoveResult.FAIL)) { + if (!super.canApply(user, target, move, args)) { return false; - } else { - return true; } + return true; } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { @@ -5549,19 +5538,6 @@ export class LeechSeedAttr extends AddBattlerTagAttr { constructor() { super(BattlerTagType.SEEDED); } - - /** - * Adds a Seeding effect to the target if the target does not have an active Substitute. - * @param user the {@linkcode Pokemon} using the move - * @param target the {@linkcode Pokemon} targeted by the move - * @param move the {@linkcode Move} invoking this effect - * @param args n/a - * @returns `true` if the effect successfully applies; `false` otherwise - */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - return !move.hitsSubstitute(user, target) - && super.apply(user, target, move, args); - } } /** @@ -5737,13 +5713,6 @@ export class FlinchAttr extends AddBattlerTagAttr { constructor() { super(BattlerTagType.FLINCHED, false); } - - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - if (!move.hitsSubstitute(user, target)) { - return super.apply(user, target, move, args); - } - return false; - } } export class ConfuseAttr extends AddBattlerTagAttr { @@ -5759,16 +5728,13 @@ export class ConfuseAttr extends AddBattlerTagAttr { return false; } - if (!move.hitsSubstitute(user, target)) { - return super.apply(user, target, move, args); - } - return false; + return super.apply(user, target, move, args); } } export class RechargeAttr extends AddBattlerTagAttr { constructor() { - super(BattlerTagType.RECHARGING, true, false, 1, 1, true, true); + super(BattlerTagType.RECHARGING, true, false, 1, 1, true); } } @@ -6151,7 +6117,7 @@ export class AddPledgeEffectAttr extends AddArenaTagAttr { * @see {@linkcode apply} */ export class RevivalBlessingAttr extends MoveEffectAttr { - constructor(user?: boolean) { + constructor() { super(true); } @@ -6392,10 +6358,6 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { const player = switchOutTarget instanceof PlayerPokemon; if (!this.selfSwitch) { - if (move.hitsSubstitute(user, target)) { - return false; - } - // Dondozo with an allied Tatsugiri in its mouth cannot be forced out const commandedTag = switchOutTarget.getTag(BattlerTagType.COMMANDED); if (commandedTag?.getSourcePokemon()?.isActive(true)) { @@ -6650,7 +6612,7 @@ export class ChangeTypeAttr extends MoveEffectAttr { private type: PokemonType; constructor(type: PokemonType) { - super(false, { trigger: MoveEffectTrigger.HIT }); + super(false); this.type = type; } @@ -6673,7 +6635,7 @@ export class AddTypeAttr extends MoveEffectAttr { private type: PokemonType; constructor(type: PokemonType) { - super(false, { trigger: MoveEffectTrigger.HIT }); + super(false); this.type = type; } @@ -7369,7 +7331,7 @@ export class AbilityChangeAttr extends MoveEffectAttr { public ability: Abilities; constructor(ability: Abilities, selfTarget?: boolean) { - super(selfTarget, { trigger: MoveEffectTrigger.HIT }); + super(selfTarget); this.ability = ability; } @@ -7400,7 +7362,7 @@ export class AbilityCopyAttr extends MoveEffectAttr { public copyToPartner: boolean; constructor(copyToPartner: boolean = false) { - super(false, { trigger: MoveEffectTrigger.HIT }); + super(false); this.copyToPartner = copyToPartner; } @@ -7441,7 +7403,7 @@ export class AbilityGiveAttr extends MoveEffectAttr { public copyToPartner: boolean; constructor() { - super(false, { trigger: MoveEffectTrigger.HIT }); + super(false); } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { @@ -7720,7 +7682,7 @@ export class DiscourageFrequentUseAttr extends MoveAttr { export class MoneyAttr extends MoveEffectAttr { constructor() { - super(true, { trigger: MoveEffectTrigger.HIT, firstHitOnly: true }); + super(true, {firstHitOnly: true }); } apply(user: Pokemon, target: Pokemon, move: Move): boolean { @@ -7787,7 +7749,7 @@ export class StatusIfBoostedAttr extends MoveEffectAttr { public effect: StatusEffect; constructor(effect: StatusEffect) { - super(true, { trigger: MoveEffectTrigger.HIT }); + super(true); this.effect = effect; } @@ -10566,7 +10528,7 @@ export function initMoves() { .attr(JawLockAttr) .bitingMove(), new SelfStatusMove(Moves.STUFF_CHEEKS, PokemonType.NORMAL, -1, 10, -1, 0, 8) - .attr(EatBerryAttr) + .attr(EatBerryAttr, true) .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true) .condition((user) => { const userBerries = globalScene.findModifiers(m => m instanceof BerryModifier, user.isPlayer()); @@ -10590,7 +10552,7 @@ export function initMoves() { .makesContact(false) .partial(), // smart targetting is unimplemented new StatusMove(Moves.TEATIME, PokemonType.NORMAL, -1, 10, -1, 0, 8) - .attr(EatBerryAttr) + .attr(EatBerryAttr, false) .target(MoveTarget.ALL), new StatusMove(Moves.OCTOLOCK, PokemonType.FIGHTING, 100, 15, -1, 0, 8) .condition(failIfGhostTypeCondition) 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 d8af7b6aac8..48b36369190 100644 --- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts +++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts @@ -22,7 +22,7 @@ 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/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/aTrainersTest"; diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index 0a270aebf37..e0486c83e77 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -37,7 +37,7 @@ import type HeldModifierConfig from "#app/interfaces/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 { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import i18next from "i18next"; /** the i18n namespace for this encounter */ 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 b66052cfd16..b403c5f291c 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 @@ -23,7 +23,7 @@ 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/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import i18next from "i18next"; /** the i18n namespace for this encounter */ diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index bf49dfdea91..7f54e51565e 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -36,7 +36,7 @@ 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/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/berriesAbound"; 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 8dfd1a270bd..001faf3a67f 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -52,7 +52,7 @@ import i18next from "i18next"; import MoveInfoOverlay from "#app/ui/move-info-overlay"; import { allMoves } from "#app/data/moves/move"; import { ModifierTier } from "#app/modifier/modifier-tier"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { getSpriteKeysFromSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; /** the i18n namespace for the encounter */ diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 07688db4583..24c076f750e 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -46,7 +46,7 @@ import { Moves } from "#enums/moves"; import { EncounterBattleAnim } from "#app/data/battle-anims"; import { MoveCategory } from "#enums/MoveCategory"; import { CustomPokemonData } from "#app/data/custom-pokemon-data"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { EncounterAnim } from "#enums/encounter-anims"; import { Challenges } from "#enums/challenges"; diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index 75527e1f8c1..bdd4bfaacaa 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -24,7 +24,7 @@ import { TrainerSlot } from "#enums/trainer-slot"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { EnemyPokemon, PokemonMove } from "#app/field/pokemon"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +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"; diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index 85ebf175f43..e746b13c6a5 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -19,7 +19,7 @@ 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/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { Challenges } from "#enums/challenges"; /** i18n namespace for encounter */ diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts index e57955c324a..7040bb47d19 100644 --- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts +++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts @@ -18,7 +18,7 @@ import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/u import { getPokemonSpecies } from "#app/data/pokemon-species"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import type { PokemonHeldItemModifier, PokemonInstantReviveModifier } from "#app/modifier/modifier"; import { BerryModifier, diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index 6a26cf19d7f..39341bef2d5 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -10,7 +10,7 @@ import { Species } from "#enums/species"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; /** i18n namespace for encounter */ const namespace = "mysteryEncounters/departmentStoreSale"; diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index a1964aa5ab4..2cd6123838b 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -18,7 +18,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { Stat } from "#enums/stat"; import i18next from "i18next"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; /** i18n namespace for the encounter */ const namespace = "mysteryEncounters/fieldTrip"; diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index f0fb6398334..0364b98abe2 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -41,7 +41,7 @@ import { import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { EncounterAnim } from "#enums/encounter-anims"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { Abilities } from "#enums/abilities"; import { BattlerTagType } from "#enums/battler-tag-type"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; 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 d9b4140c6ee..ecc2e17a06f 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -33,7 +33,7 @@ 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/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/fightOrFlight"; 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 282c6c149ff..2d0828b8c0c 100644 --- a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts +++ b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts @@ -30,7 +30,7 @@ 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/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; /** the i18n namespace for the encounter */ diff --git a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts index 63db5c7c5d6..b0721ddfee9 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -48,7 +48,7 @@ import { Gender, getGenderSymbol } from "#app/data/gender"; import { getNatureName } from "#app/data/nature"; import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball"; import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { addPokemonDataToDexAndValidateAchievements } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import type { PokeballType } from "#enums/pokeball"; import { doShinySparkleAnim } from "#app/field/anims"; diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 97fd5783ebb..6d8a1fc8c6b 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -10,7 +10,7 @@ import { leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter import { applyDamageToPokemon } 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 { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { PokemonMove } from "#app/field/pokemon"; const OPTION_1_REQUIRED_MOVE = Moves.SURF; diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index b10f2f3dba2..6907e18cfdc 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -16,7 +16,7 @@ import { randSeedInt } from "#app/utils/common"; import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/mysteriousChallengers"; diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 8877bf36ce8..e9976ba04aa 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -15,7 +15,7 @@ import { koPlayerPokemon, } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getPokemonSpecies } from "#app/data/pokemon-species"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +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"; diff --git a/src/data/mystery-encounters/encounters/part-timer-encounter.ts b/src/data/mystery-encounters/encounters/part-timer-encounter.ts index 61b48353997..1074eaf8c81 100644 --- a/src/data/mystery-encounters/encounters/part-timer-encounter.ts +++ b/src/data/mystery-encounters/encounters/part-timer-encounter.ts @@ -20,7 +20,7 @@ import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-enco import i18next from "i18next"; import type { PlayerPokemon } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; /** the i18n namespace for the encounter */ diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 602a8d397db..7a12c86edff 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -31,7 +31,7 @@ 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/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { NON_LEGEND_PARADOX_POKEMON } from "#app/data/balance/special-species-groups"; /** the i18n namespace for the encounter */ diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index 79f4b53a73e..daf4d860cdf 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -26,7 +26,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import type { Nature } from "#enums/nature"; import { getNatureName } from "#app/data/nature"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import i18next from "i18next"; /** the i18n namespace for this encounter */ diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index bfa1204a8ba..41c20f35ba1 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -26,7 +26,7 @@ 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 { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { BerryType } from "#enums/berry-type"; import { CustomPokemonData } from "#app/data/custom-pokemon-data"; diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index ef3532b080e..28c7fe4644f 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -29,7 +29,7 @@ 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/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER, 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 ab2f19cfb77..076171b3e5e 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 @@ -11,7 +11,7 @@ import { randSeedShuffle } from "#app/utils/common"; import type MysteryEncounter from "../mystery-encounter"; import { MysteryEncounterBuilder } from "../mystery-encounter"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { Biome } from "#enums/biome"; import { TrainerType } from "#enums/trainer-type"; import i18next from "i18next"; diff --git a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts index cfff59b45f5..bfba553af5d 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -26,7 +26,7 @@ import { showEncounterDialogue } from "#app/data/mystery-encounters/utils/encoun import PokemonData from "#app/system/pokemon-data"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { Abilities } from "#enums/abilities"; import { NON_LEGEND_PARADOX_POKEMON, NON_LEGEND_ULTRA_BEASTS } from "#app/data/balance/special-species-groups"; import { timedEventManager } from "#app/global-event-manager"; 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 c994c6e993f..294f1a78b34 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -28,7 +28,7 @@ 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/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/theStrongStuff"; 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 41bf87351f4..bc7c570abca 100644 --- a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts @@ -32,7 +32,7 @@ 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/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { BattlerTagType } from "#enums/battler-tag-type"; /** the i18n namespace for the encounter */ diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index 11d00f1dd8c..597a6b009b3 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -28,7 +28,7 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode import type HeldModifierConfig from "#app/interfaces/held-modifier-config"; import i18next from "i18next"; import { getStatKey } from "#enums/stat"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import type { Nature } from "#enums/nature"; diff --git a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts index 1ff96f21edc..1e1db14705a 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -26,7 +26,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Moves } from "#enums/moves"; import { BattlerIndex } from "#app/battle"; import { PokemonMove } from "#app/field/pokemon"; -import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; import { randSeedInt } from "#app/utils/common"; /** the i18n namespace for this encounter */ diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index 66c7f7afc56..f4eec5b0923 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -37,7 +37,7 @@ import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encoun 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/game-mode"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/uncommonBreed"; diff --git a/src/enums/MoveEffectTrigger.ts b/src/enums/MoveEffectTrigger.ts index 1e7753d94fa..d22953c3690 100644 --- a/src/enums/MoveEffectTrigger.ts +++ b/src/enums/MoveEffectTrigger.ts @@ -1,7 +1,6 @@ export enum MoveEffectTrigger { PRE_APPLY, POST_APPLY, - HIT, /** Triggers one time after all target effects have applied */ POST_TARGET } diff --git a/src/enums/hit-check-result.ts b/src/enums/hit-check-result.ts new file mode 100644 index 00000000000..cf8a2b17194 --- /dev/null +++ b/src/enums/hit-check-result.ts @@ -0,0 +1,23 @@ +/** The result of a hit check calculation */ +export const HitCheckResult = { + /** Hit checks haven't been evaluated yet in this pass */ + PENDING: 0, + /** The move hits the target successfully */ + HIT: 1, + /** The move has no effect on the target */ + NO_EFFECT: 2, + /** The move has no effect on the target, but doesn't proc the default "no effect" message */ + NO_EFFECT_NO_MESSAGE: 3, + /** The target protected itself against the move */ + PROTECTED: 4, + /** The move missed the target */ + MISS: 5, + /** The move is reflected by magic coat or magic bounce */ + REFLECTED: 6, + /** The target is no longer on the field */ + TARGET_NOT_ON_FIELD: 7, + /** The move failed unexpectedly */ + ERROR: 8, +} as const; + +export type HitCheckResult = typeof HitCheckResult[keyof typeof HitCheckResult]; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 86d74ea5555..d565a590792 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -277,6 +277,36 @@ export enum FieldPosition { RIGHT, } +/** Base typeclass for damage parameter methods, used for DRY */ +type damageParams = { + /** The attacking {@linkcode Pokemon} */ + source: Pokemon; + /** The move used in the attack */ + move: Move; + /** The move's {@linkcode MoveCategory} after variable-category effects are applied */ + moveCategory: MoveCategory; + /** If `true`, ignores this Pokemon's defensive ability effects */ + ignoreAbility?: boolean; + /** If `true`, ignores the attacking Pokemon's ability effects */ + ignoreSourceAbility?: boolean; + /** If `true`, ignores the ally Pokemon's ability effects */ + ignoreAllyAbility?: boolean; + /** If `true`, ignores the ability effects of the attacking pokemon's ally */ + ignoreSourceAllyAbility?: boolean; + /** If `true`, calculates damage for a critical hit */ + isCritical?: boolean; + /** If `true`, suppresses changes to game state during the calculation */ + simulated?: boolean; + /** If defined, used in place of calculated effectiveness values */ + effectiveness?: number; +} + +/** Type for the parameters of {@linkcode Pokemon#getBaseDamage | getBaseDamage} */ +type getBaseDamageParams = Omit + +/** Type for the parameters of {@linkcode Pokemon#getAttackDamage | getAttackDamage} */ +type getAttackDamageParams = Omit; + export default abstract class Pokemon extends Phaser.GameObjects.Container { public id: number; public name: string; @@ -1441,25 +1471,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * Calculate the critical-hit stage of a move used against this pokemon by * the given source * - * @param source the {@linkcode Pokemon} who using the move - * @param move the {@linkcode Move} being used - * @returns the final critical-hit stage value + * @param source - The {@linkcode Pokemon} who using the move + * @param move - The {@linkcode Move} being used + * @returns The final critical-hit stage value */ getCritStage(source: Pokemon, move: Move): number { const critStage = new NumberHolder(0); applyMoveAttrs(HighCritAttr, source, this, move, critStage); - globalScene.applyModifiers( - CritBoosterModifier, - source.isPlayer(), - source, - critStage, - ); - globalScene.applyModifiers( - TempCritBoosterModifier, - source.isPlayer(), - critStage, - ); - applyAbAttrs(BonusCritAbAttr, source, null, false, critStage) + globalScene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critStage); + globalScene.applyModifiers(TempCritBoosterModifier, source.isPlayer(), critStage); + applyAbAttrs(BonusCritAbAttr, source, null, false, critStage); const critBoostTag = source.getTag(CritBoostTag); if (critBoostTag) { if (critBoostTag instanceof DragonCheerTag) { @@ -1475,6 +1496,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return critStage.value; } + /** + * Calculates the category of a move when used by this pokemon after + * category-changing move effects are applied. + * @param target - The {@linkcode Pokemon} using the move + * @param move - The {@linkcode Move} being used + * @returns The given move's final category + */ + getMoveCategory(target: Pokemon, move: Move): MoveCategory { + const moveCategory = new NumberHolder(move.category); + applyMoveAttrs(VariableMoveCategoryAttr, this, target, move, moveCategory); + return moveCategory.value; + } + /** * Calculates and retrieves the final value of a stat considering any held * items, move effects, opponent abilities, and whether there was a critical @@ -2584,7 +2618,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param simulated Whether to apply abilities via simulated calls (defaults to `true`) * @param cancelled {@linkcode BooleanHolder} Stores whether the move was cancelled by a non-type-based immunity. * @param useIllusion - Whether we want the attack move effectiveness on the illusion or not - * Currently only used by {@linkcode Pokemon.apply} to determine whether a "No effect" message should be shown. * @returns The type damage multiplier, indicating the effectiveness of the move */ getMoveEffectiveness( @@ -4075,27 +4108,28 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * Calculates the base damage of the given move against this Pokemon when attacked by the given source. * Used during damage calculation and for Shell Side Arm's forecasting effect. - * @param source the attacking {@linkcode Pokemon}. - * @param move the {@linkcode Move} used in the attack. - * @param moveCategory the move's {@linkcode MoveCategory} after variable-category effects are applied. - * @param ignoreAbility if `true`, ignores this Pokemon's defensive ability effects (defaults to `false`). - * @param ignoreSourceAbility if `true`, ignore's the attacking Pokemon's ability effects (defaults to `false`). - * @param ignoreAllyAbility if `true`, ignores the ally Pokemon's ability effects (defaults to `false`). - * @param ignoreSourceAllyAbility if `true`, ignores the attacking Pokemon's ally's ability effects (defaults to `false`). - * @param isCritical if `true`, calculates effective stats as if the hit were critical (defaults to `false`). - * @param simulated if `true`, suppresses changes to game state during calculation (defaults to `true`). + * @param source - The attacking {@linkcode Pokemon}. + * @param move - The {@linkcode Move} used in the attack. + * @param moveCategory - The move's {@linkcode MoveCategory} after variable-category effects are applied. + * @param ignoreAbility - If `true`, ignores this Pokemon's defensive ability effects (defaults to `false`). + * @param ignoreSourceAbility - If `true`, ignore's the attacking Pokemon's ability effects (defaults to `false`). + * @param ignoreAllyAbility - If `true`, ignores the ally Pokemon's ability effects (defaults to `false`). + * @param ignoreSourceAllyAbility - If `true`, ignores the attacking Pokemon's ally's ability effects (defaults to `false`). + * @param isCritical - if `true`, calculates effective stats as if the hit were critical (defaults to `false`). + * @param simulated - if `true`, suppresses changes to game state during calculation (defaults to `true`). * @returns The move's base damage against this Pokemon when used by the source Pokemon. */ getBaseDamage( - source: Pokemon, - move: Move, - moveCategory: MoveCategory, + { + source, + move, + moveCategory, ignoreAbility = false, ignoreSourceAbility = false, ignoreAllyAbility = false, ignoreSourceAllyAbility = false, isCritical = false, - simulated = true, + simulated = true}: getBaseDamageParams ): number { const isPhysical = moveCategory === MoveCategory.PHYSICAL; @@ -4222,27 +4256,27 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * Calculates the damage of an attack made by another Pokemon against this Pokemon * @param source {@linkcode Pokemon} the attacking Pokemon - * @param move {@linkcode Pokemon} the move used in the attack + * @param move The {@linkcode Move} used in the attack * @param ignoreAbility If `true`, ignores this Pokemon's defensive ability effects * @param ignoreSourceAbility If `true`, ignores the attacking Pokemon's ability effects * @param ignoreAllyAbility If `true`, ignores the ally Pokemon's ability effects * @param ignoreSourceAllyAbility If `true`, ignores the ability effects of the attacking pokemon's ally * @param isCritical If `true`, calculates damage for a critical hit. * @param simulated If `true`, suppresses changes to game state during the calculation. - * @returns a {@linkcode DamageCalculationResult} object with three fields: - * - `cancelled`: `true` if the move was cancelled by another effect. - * - `result`: {@linkcode HitResult} indicates the attack's type effectiveness. - * - `damage`: `number` the attack's final damage output. + * @param effectiveness If defined, used in place of calculated effectiveness values + * @returns The {@linkcode DamageCalculationResult} */ getAttackDamage( - source: Pokemon, - move: Move, - ignoreAbility = false, - ignoreSourceAbility = false, - ignoreAllyAbility = false, - ignoreSourceAllyAbility = false, - isCritical = false, - simulated = true, + { + source, + move, + ignoreAbility = false, + ignoreSourceAbility = false, + ignoreAllyAbility = false, + ignoreSourceAllyAbility = false, + isCritical = false, + simulated = true, + effectiveness}: getAttackDamageParams, ): DamageCalculationResult { const damage = new NumberHolder(0); const defendingSide = this.isPlayer() @@ -4272,7 +4306,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * * Note that the source's abilities are not ignored here */ - const typeMultiplier = this.getMoveEffectiveness( + const typeMultiplier = effectiveness ?? this.getMoveEffectiveness( source, move, ignoreAbility, @@ -4344,7 +4378,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * The attack's base damage, as determined by the source's level, move power * and Attack stat as well as this Pokemon's Defense stat */ - const baseDamage = this.getBaseDamage( + const baseDamage = this.getBaseDamage({ source, move, moveCategory, @@ -4354,7 +4388,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ignoreSourceAllyAbility, isCritical, simulated, - ); + }); /** 25% damage debuff on moves hitting more than one non-fainted target (regardless of immunities) */ const { targets, multiple } = getMoveTargets(source, move.id); @@ -4565,211 +4599,36 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { }; } - /** - * Applies the results of a move to this pokemon - * @param source The {@linkcode Pokemon} using the move - * @param move The {@linkcode Move} being used - * @returns The {@linkcode HitResult} of the attack - */ - apply(source: Pokemon, move: Move): HitResult { - const defendingSide = this.isPlayer() - ? ArenaTagSide.PLAYER - : ArenaTagSide.ENEMY; - const moveCategory = new NumberHolder(move.category); - applyMoveAttrs(VariableMoveCategoryAttr, source, this, move, moveCategory); - if (moveCategory.value === MoveCategory.STATUS) { - const cancelled = new BooleanHolder(false); - const typeMultiplier = this.getMoveEffectiveness( - source, - move, - false, - false, - cancelled, - ); - - if (!cancelled.value && typeMultiplier === 0) { - globalScene.queueMessage( - i18next.t("battle:hitResultNoEffect", { - pokemonName: getPokemonNameWithAffix(this), - }), - ); - } - return typeMultiplier === 0 ? HitResult.NO_EFFECT : HitResult.STATUS; + /** Calculate whether the given move critically hits this pokemon + * @param source - The {@linkcode Pokemon} using the move + * @param move - The {@linkcode Move} being used + * @param simulated - If `true`, suppresses changes to game state during calculation (defaults to `true`) + * @returns whether the move critically hits the pokemon + */ + getCriticalHitResult(source: Pokemon, move: Move, simulated: boolean = true): boolean { + const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + const noCritTag = globalScene.arena.getTagOnSide(NoCritTag, defendingSide); + if (noCritTag || Overrides.NEVER_CRIT_OVERRIDE || move.hasAttr(FixedDamageAttr)) { + return false; } - /** Determines whether the attack critically hits */ - let isCritical: boolean; - const critOnly = new BooleanHolder(false); - const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT); - applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly); - applyAbAttrs( - ConditionalCritAbAttr, - source, - null, - false, - critOnly, - this, - move, - ); - if (critOnly.value || critAlways) { - isCritical = true; - } else { + const isCritical = new BooleanHolder(false); + + if (source.getTag(BattlerTagType.ALWAYS_CRIT)) { + isCritical.value = true; + } + applyMoveAttrs(CritOnlyAttr, source, this, move, isCritical); + applyAbAttrs(ConditionalCritAbAttr, source, null, simulated, isCritical, this, move); + if (!isCritical.value) { const critChance = [24, 8, 2, 1][ Math.max(0, Math.min(this.getCritStage(source, move), 3)) ]; - isCritical = - critChance === 1 || !globalScene.randBattleSeedInt(critChance); + isCritical.value = critChance === 1 || !globalScene.randBattleSeedInt(critChance); } - const noCritTag = globalScene.arena.getTagOnSide(NoCritTag, defendingSide); - const blockCrit = new BooleanHolder(false); - applyAbAttrs(BlockCritAbAttr, this, null, false, blockCrit); - if (noCritTag || blockCrit.value || Overrides.NEVER_CRIT_OVERRIDE) { - isCritical = false; - } + applyAbAttrs(BlockCritAbAttr, this, null, simulated, isCritical); - /** - * Applies stat changes from {@linkcode move} and gives it to {@linkcode source} - * before damage calculation - */ - applyMoveAttrs(StatChangeBeforeDmgCalcAttr, source, this, move); - - const { - cancelled, - result, - damage: dmg, - } = this.getAttackDamage(source, move, false, false, false, false, isCritical, false); - - const typeBoost = source.findTag( - t => - t instanceof TypeBoostTag && t.boostedType === source.getMoveType(move), - ) as TypeBoostTag; - if (typeBoost?.oneUse) { - source.removeTag(typeBoost.tagType); - } - - if ( - cancelled || - result === HitResult.IMMUNE || - result === HitResult.NO_EFFECT - ) { - source.stopMultiHit(this); - - if (!cancelled) { - if (result === HitResult.IMMUNE) { - globalScene.queueMessage( - i18next.t("battle:hitResultImmune", { - pokemonName: getPokemonNameWithAffix(this), - }), - ); - } else { - globalScene.queueMessage( - i18next.t("battle:hitResultNoEffect", { - pokemonName: getPokemonNameWithAffix(this), - }), - ); - } - } - return result; - } - - // In case of fatal damage, this tag would have gotten cleared before we could lapse it. - const destinyTag = this.getTag(BattlerTagType.DESTINY_BOND); - const grudgeTag = this.getTag(BattlerTagType.GRUDGE); - - if (dmg) { - this.lapseTags(BattlerTagLapseType.HIT); - - const substitute = this.getTag(SubstituteTag); - const isBlockedBySubstitute = - !!substitute && move.hitsSubstitute(source, this); - if (isBlockedBySubstitute) { - substitute.hp -= dmg; - } - if (!this.isPlayer() && dmg >= this.hp) { - globalScene.applyModifiers(EnemyEndureChanceModifier, false, this); - } - - /** - * We explicitly require to ignore the faint phase here, as we want to show the messages - * about the critical hit and the super effective/not very effective messages before the faint phase. - */ - const damage = this.damageAndUpdate(isBlockedBySubstitute ? 0 : dmg, - { - result: result as DamageResult, - isCritical, - ignoreFaintPhase: true, - source - }); - - if (damage > 0) { - if (source.isPlayer()) { - globalScene.validateAchvs(DamageAchv, new NumberHolder(damage)); - if (damage > globalScene.gameData.gameStats.highestDamage) { - globalScene.gameData.gameStats.highestDamage = damage; - } - } - source.turnData.totalDamageDealt += damage; - source.turnData.singleHitDamageDealt = damage; - this.turnData.damageTaken += damage; - this.battleData.hitCount++; - - const attackResult = { - move: move.id, - result: result as DamageResult, - damage: damage, - critical: isCritical, - sourceId: source.id, - sourceBattlerIndex: source.getBattlerIndex(), - }; - this.turnData.attacksReceived.unshift(attackResult); - if (source.isPlayer() && !this.isPlayer()) { - globalScene.applyModifiers( - DamageMoneyRewardModifier, - true, - source, - new NumberHolder(damage), - ); - } - } - } - - if (isCritical) { - globalScene.queueMessage(i18next.t("battle:hitResultCriticalHit")); - } - - // want to include is.Fainted() in case multi hit move ends early, still want to render message - if (source.turnData.hitsLeft === 1 || this.isFainted()) { - switch (result) { - case HitResult.SUPER_EFFECTIVE: - globalScene.queueMessage(i18next.t("battle:hitResultSuperEffective")); - break; - case HitResult.NOT_VERY_EFFECTIVE: - globalScene.queueMessage( - i18next.t("battle:hitResultNotVeryEffective"), - ); - break; - case HitResult.ONE_HIT_KO: - globalScene.queueMessage(i18next.t("battle:hitResultOneHitKO")); - break; - } - } - - if (this.isFainted()) { - // set splice index here, so future scene queues happen before FaintedPhase - globalScene.setPhaseQueueSplice(); - globalScene.unshiftPhase( - new FaintPhase( - this.getBattlerIndex(), - false, - source, - ), - ); - - this.destroySubstitute(); - this.lapseTag(BattlerTagType.COMMANDED); - } - - return result; + return isCritical.value; + } /** @@ -4833,7 +4692,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** - * Called by apply(), given the damage, adds a new DamagePhase and actually updates HP values, etc. + * Given the damage, adds a new DamagePhase and update HP values, etc. + * * Checks for 'Indirect' HitResults to account for Endure/Reviver Seed applying correctly * @param damage integer - passed to damage() * @param result an enum if it's super effective, not very, etc. @@ -5136,8 +4996,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * Gets whether the given move is currently disabled for this Pokemon. * - * @param {Moves} moveId {@linkcode Moves} ID of the move to check - * @returns {boolean} `true` if the move is disabled for this Pokemon, otherwise `false` + * @param moveId - The {@linkcode Moves} ID of the move to check + * @returns `true` if the move is disabled for this Pokemon, otherwise `false` * * @see {@linkcode MoveRestrictionBattlerTag} */ @@ -5148,9 +5008,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * Gets whether the given move is currently disabled for the user based on the player's target selection * - * @param {Moves} moveId {@linkcode Moves} ID of the move to check - * @param {Pokemon} user {@linkcode Pokemon} the move user - * @param {Pokemon} target {@linkcode Pokemon} the target of the move + * @param moveId - The {@linkcode Moves} ID of the move to check + * @param user - The move user + * @param target - The target of the move * * @returns {boolean} `true` if the move is disabled for this Pokemon due to the player's target selection * @@ -5180,10 +5040,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * Gets the {@link MoveRestrictionBattlerTag} that is restricting a move, if it exists. * - * @param {Moves} moveId {@linkcode Moves} ID of the move to check - * @param {Pokemon} user {@linkcode Pokemon} the move user, optional and used when the target is a factor in the move's restricted status - * @param {Pokemon} target {@linkcode Pokemon} the target of the move, optional and used when the target is a factor in the move's restricted status - * @returns {MoveRestrictionBattlerTag | null} the first tag on this Pokemon that restricts the move, or `null` if the move is not restricted. + * @param moveId - {@linkcode Moves} ID of the move to check + * @param user - {@linkcode Pokemon} the move user, optional and used when the target is a factor in the move's restricted status + * @param target - {@linkcode Pokemon} the target of the move, optional and used when the target is a factor in the move's restricted status + * @returns The first tag on this Pokemon that restricts the move, or `null` if the move is not restricted. */ getRestrictingTag( moveId: Moves, @@ -5245,20 +5105,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.summonData.moveQueue; } - /** - * If this Pokemon is using a multi-hit move, cancels all subsequent strikes - * @param {Pokemon} target If specified, this only cancels subsequent strikes against the given target - */ - stopMultiHit(target?: Pokemon): void { - const effectPhase = globalScene.getCurrentPhase(); - if ( - effectPhase instanceof MoveEffectPhase && - effectPhase.getUserPokemon() === this - ) { - effectPhase.stopMultiHit(target); - } - } - changeForm(formChange: SpeciesFormChange): Promise { return new Promise(resolve => { this.formIndex = Math.max( @@ -5676,7 +5522,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * cancel the attack's subsequent hits. */ if (effect === StatusEffect.SLEEP || effect === StatusEffect.FREEZE) { - this.stopMultiHit(); + const currentPhase = globalScene.getCurrentPhase(); + if (currentPhase instanceof MoveEffectPhase && currentPhase.getUserPokemon() === this) { + this.turnData.hitCount = 1; + this.turnData.hitsLeft = 1; + } } if (asPhase) { @@ -7311,14 +7161,15 @@ export class EnemyPokemon extends Pokemon { ].includes(move.id); return ( doesNotFail && - p.getAttackDamage( - this, + p.getAttackDamage({ + source: this, move, - !p.battleData.abilityRevealed, - false, - !p.getAlly()?.battleData.abilityRevealed, - false, + ignoreAbility: !p.battleData.abilityRevealed, + ignoreSourceAbility: false, + ignoreAllyAbility: !p.getAlly()?.battleData.abilityRevealed, + ignoreSourceAllyAbility: false, isCritical, + } ).damage >= p.hp ); }) diff --git a/src/game-mode.ts b/src/game-mode.ts index dfe6b8cf123..ec7171b0024 100644 --- a/src/game-mode.ts +++ b/src/game-mode.ts @@ -13,6 +13,7 @@ import { Species } from "#enums/species"; import { Challenges } from "./enums/challenges"; import { globalScene } from "#app/global-scene"; import { getDailyStartingBiome } from "./data/daily-run"; +import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES, CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES } from "./constants"; export enum GameModes { CLASSIC, @@ -36,10 +37,6 @@ interface GameModeConfig { hasMysteryEncounters?: boolean; } -// Describes min and max waves for MEs in specific game modes -export const CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES: [number, number] = [10, 180]; -export const CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES: [number, number] = [10, 180]; - export class GameMode implements GameModeConfig { public modeId: GameModes; public isClassic: boolean; diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 5a25cf6330d..4c99a609b11 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -35,19 +35,19 @@ import { BattlerTagType } from "#enums/battler-tag-type"; export class FaintPhase extends PokemonPhase { /** - * Whether or not enduring (for this phase's purposes, Reviver Seed) should be prevented + * Whether or not instant revive should be prevented */ - private preventEndure: boolean; + private preventInstantRevive: boolean; /** * The source Pokemon that dealt fatal damage */ private source?: Pokemon; - constructor(battlerIndex: BattlerIndex, preventEndure = false, source?: Pokemon) { + constructor(battlerIndex: BattlerIndex, preventInstantRevive = false, source?: Pokemon) { super(battlerIndex); - this.preventEndure = preventEndure; + this.preventInstantRevive = preventInstantRevive; this.source = source; } @@ -63,7 +63,7 @@ export class FaintPhase extends PokemonPhase { faintPokemon.resetSummonData(); - if (!this.preventEndure) { + if (!this.preventInstantRevive) { const instantReviveModifier = globalScene.applyModifier( PokemonInstantReviveModifier, this.player, diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index c29e3fe5cda..01085834ba5 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -13,7 +13,6 @@ import { PostDamageAbAttr, PostDefendAbAttr, ReflectStatusMoveAbAttr, - TypeImmunityAbAttr, } from "#app/data/abilities/ability"; import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag"; import { MoveAnim } from "#app/data/battle-anims"; @@ -23,10 +22,10 @@ import { ProtectedTag, SemiInvulnerableTag, SubstituteTag, + TypeBoostTag, } from "#app/data/battler-tags"; import type { MoveAttr } from "#app/data/moves/move"; import { - AddArenaTrapTagAttr, applyFilteredMoveAttrs, applyMoveAttrs, AttackMove, @@ -40,8 +39,8 @@ import { NoEffectAttr, OneHitKOAttr, OverrideMoveEffectAttr, + StatChangeBeforeDmgCalcAttr, ToxicAccuracyAttr, - VariableTargetAttr, } from "#app/data/moves/move"; import { MoveEffectTrigger } from "#enums/MoveEffectTrigger"; import { MoveFlags } from "#enums/MoveFlags"; @@ -49,13 +48,15 @@ import { MoveTarget } from "#enums/MoveTarget"; import { MoveCategory } from "#enums/MoveCategory"; import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms"; import { PokemonType } from "#enums/pokemon-type"; -import { PokemonMove } from "#app/field/pokemon"; +import { DamageResult, PokemonMove, type TurnMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { HitResult, MoveResult } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { ContactHeldItemTransferChanceModifier, + DamageMoneyRewardModifier, EnemyAttackStatusEffectChanceModifier, + EnemyEndureChanceModifier, FlinchChanceModifier, HitHealModifier, PokemonMultiHitModifier, @@ -64,36 +65,182 @@ import { PokemonPhase } from "#app/phases/pokemon-phase"; import { BooleanHolder, isNullOrUndefined, NumberHolder } from "#app/utils/common"; import type { nil } from "#app/utils/common"; import { BattlerTagType } from "#enums/battler-tag-type"; -import type { Moves } from "#enums/moves"; +import { Moves } from "#enums/moves"; 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 { 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]; export class MoveEffectPhase extends PokemonPhase { - public move: PokemonMove; + public move: Move; + private virtual = false; protected targets: BattlerIndex[]; protected reflected = false; + /** The result of the hit check against each target */ + private hitChecks: HitCheckEntry[]; + + /** The move history entry for the move */ + private moveHistoryEntry: TurnMove; + + /** Is this the first strike of a move? */ + private firstHit: boolean; + /** Is this the last strike of a move? */ + private lastHit: boolean; + + /** Phases queued during moves */ + private queuedPhases: Phase[] = []; + /** * @param reflected Indicates that the move was reflected by the user due to magic coat or magic bounce + * @param virtual Indicates that the move is a virtual move (i.e. called by metronome) */ - constructor(battlerIndex: BattlerIndex, targets: BattlerIndex[], move: PokemonMove, reflected = false) { + constructor(battlerIndex: BattlerIndex, targets: BattlerIndex[], move: Move, reflected = false, virtual = false) { super(battlerIndex); this.move = move; + this.virtual = virtual; + this.reflected = reflected; /** * In double battles, if the right Pokemon selects a spread move and the left Pokemon dies * with no party members available to switch in, then the right Pokemon takes the index * of the left Pokemon and gets hit unless this is checked. */ - if (targets.includes(battlerIndex) && this.move.getMove().moveTarget === MoveTarget.ALL_NEAR_OTHERS) { + if (targets.includes(battlerIndex) && this.move.moveTarget === MoveTarget.ALL_NEAR_OTHERS) { const i = targets.indexOf(battlerIndex); targets.splice(i, i + 1); } this.targets = targets; + + this.hitChecks = Array(this.targets.length).fill([HitCheckResult.PENDING, 0]); + } + + /** + * Compute targets and the results of hit checks of the invoked move against all targets, + * organized by battler index. + * + * **This is *not* a pure function**; it has the following side effects + * - `this.hitChecks` - The results of the hit checks against each target + * - `this.moveHistoryEntry` - Sets success or failure based on the hit check results + * - user.turnData.hitCount and user.turnData.hitsLeft - Both set to 1 if the + * move was unsuccessful against all targets + * + * @returns The targets of the invoked move + * @see {@linkcode hitCheck} + */ + private conductHitChecks(user: Pokemon, fieldMove: boolean): Pokemon[] { + /** All Pokemon targeted by this phase's invoked move */ + /** Whether any hit check ended in a success */ + let anySuccess = false; + /** Whether the attack missed all of its targets */ + let allMiss = true; + + let targets = this.getTargets(); + + // For field targeted moves, we only look for the first target that may magic bounce + + for (const [i, target] of targets.entries()) { + const hitCheck = this.hitCheck(target); + // If the move bounced and was a field targeted move, + // then immediately stop processing other targets + if (fieldMove && hitCheck[0] === HitCheckResult.REFLECTED) { + targets = [target]; + this.hitChecks = [hitCheck]; + break; + } + if (hitCheck[0] === HitCheckResult.HIT) { + anySuccess = true; + } else { + allMiss ||= hitCheck[0] === HitCheckResult.MISS; + } + this.hitChecks[i] = hitCheck; + } + + if (anySuccess) { + this.moveHistoryEntry.result = MoveResult.SUCCESS; + } else { + user.turnData.hitCount = 1; + user.turnData.hitsLeft = 1; + this.moveHistoryEntry.result = allMiss ? MoveResult.MISS : MoveResult.FAIL; + } + + return targets; + } + + /** + * Queue the phaes that should occur when the target reflects the move back to the user + * @param user - The {@linkcode Pokemon} using this phase's invoked move + * @param target - The {@linkcode Pokemon} that is reflecting the move + * + */ + private queueReflectedMove(user: Pokemon, target: Pokemon): void { + const newTargets = this.move.isMultiTarget() + ? getMoveTargets(target, this.move.id).targets + : [user.getBattlerIndex()]; + // 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)), + ); + this.queuedPhases.push(new HideAbilityPhase()); + } + + this.queuedPhases.push( + new MovePhase(target, newTargets, new PokemonMove(this.move.id, 0, 0, true), true, true, true), + ); + } + + /** + * Apply the move to each of the resolved targets. + * @param targets - The resolved set of targets of the move + * @throws Error if there was an unexpected hit check result + */ + private applyToTargets(user: Pokemon, targets: Pokemon[]): void { + for (const [i, target] of targets.entries()) { + const [hitCheckResult, effectiveness] = this.hitChecks[i]; + switch (hitCheckResult) { + case HitCheckResult.HIT: + this.applyMoveEffects(target, effectiveness); + if (isFieldTargeted(this.move)) { + // Stop processing other targets if the move is a field move + return; + } + break; + case HitCheckResult.NO_EFFECT: + globalScene.queueMessage( + i18next.t(this.move.id === Moves.SHEER_COLD ? "battle:hitResultImmune" : "battle:hitResultNoEffect", { + pokemonName: getPokemonNameWithAffix(target), + }), + ); + case HitCheckResult.NO_EFFECT_NO_MESSAGE: + case HitCheckResult.PROTECTED: + case HitCheckResult.TARGET_NOT_ON_FIELD: + applyMoveAttrs(NoEffectAttr, user, target, this.move); + break; + case HitCheckResult.MISS: + globalScene.queueMessage( + i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) }), + ); + applyMoveAttrs(MissEffectAttr, user, target, this.move); + break; + case HitCheckResult.REFLECTED: + this.queueReflectedMove(user, target); + break; + case HitCheckResult.PENDING: + case HitCheckResult.ERROR: + throw new Error("Unexpected hit check result"); + } + } } public override start(): void { @@ -101,11 +248,10 @@ export class MoveEffectPhase extends PokemonPhase { /** The Pokemon using this phase's invoked move */ const user = this.getUserPokemon(); - /** All Pokemon targeted by this phase's invoked move */ - const targets = this.getTargets(); if (!user) { - return super.end(); + super.end(); + return; } /** If an enemy used this move, set this as last enemy that used move or ability */ @@ -115,23 +261,24 @@ export class MoveEffectPhase extends PokemonPhase { globalScene.currentBattle.lastPlayerInvolved = this.fieldIndex; } - const isDelayedAttack = this.move.getMove().hasAttr(DelayedAttackAttr); + const isDelayedAttack = this.move.hasAttr(DelayedAttackAttr); /** If the user was somehow removed from the field and it's not a delayed attack, end this phase */ if (!user.isOnField()) { if (!isDelayedAttack) { - return super.end(); - } else { - if (!user.scene) { - /** - * This happens if the Pokemon that used the delayed attack gets caught and released - * on the turn the attack would have triggered. Having access to the global scene - * in the future may solve this entirely, so for now we just cancel the hit - */ - return super.end(); - } - if (isNullOrUndefined(user.turnData)) { - user.resetTurnData(); - } + super.end(); + return; + } + if (!user.scene) { + /* + * This happens if the Pokemon that used the delayed attack gets caught and released + * on the turn the attack would have triggered. Having access to the global scene + * in the future may solve this entirely, so for now we just cancel the hit + */ + super.end(); + return; + } + if (isNullOrUndefined(user.turnData)) { + user.resetTurnData(); } } @@ -140,17 +287,17 @@ export class MoveEffectPhase extends PokemonPhase { * e.g. Charging moves (Fly, etc.) on their first turn of use. */ const overridden = new BooleanHolder(false); - /** The {@linkcode Move} object from {@linkcode allMoves} invoked by this phase */ - const move = this.move.getMove(); + const move = this.move; // Assume single target for override - applyMoveAttrs(OverrideMoveEffectAttr, user, this.getFirstTarget() ?? null, move, overridden, this.move.virtual); + applyMoveAttrs(OverrideMoveEffectAttr, user, this.getFirstTarget() ?? null, move, overridden, this.virtual); // If other effects were overriden, stop this phase before they can be applied if (overridden.value) { return this.end(); } + // Lapse `MOVE_EFFECT` effects (i.e. semi-invulnerability) when applicable user.lapseTags(BattlerTagLapseType.MOVE_EFFECT); // If the user is acting again (such as due to Instruct), reset hitsLeft/hitCount so that @@ -179,339 +326,75 @@ export class MoveEffectPhase extends PokemonPhase { user.turnData.hitsLeft = hitCount.value; } - /** + /* * Log to be entered into the user's move history once the move result is resolved. - * Note that `result` (a {@linkcode MoveResult}) logs whether the move was successfully + * Note that `result` logs whether the move was successfully * used in the sense of "Does it have an effect on the user?". */ - const moveHistoryEntry = { - move: this.move.moveId, + this.moveHistoryEntry = { + move: this.move.id, targets: this.targets, result: MoveResult.PENDING, - virtual: this.move.virtual, + virtual: this.virtual, }; - /** - * Stores results of hit checks of the invoked move against all targets, organized by battler index. - * @see {@linkcode hitCheck} - */ - const targetHitChecks = Object.fromEntries(targets.map(p => [p.getBattlerIndex(), this.hitCheck(p)])); - const hasActiveTargets = targets.some(t => t.isActive(true)); + const fieldMove = isFieldTargeted(move); - /** Check if the target is immune via ability to the attacking move, and NOT in semi invulnerable state */ - const isImmune = - targets[0]?.hasAbilityWithAttr(TypeImmunityAbAttr) && - targets[0]?.getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move) && - !targets[0]?.getTag(SemiInvulnerableTag); + const targets = this.conductHitChecks(user, fieldMove); - const mayBounce = - move.hasFlag(MoveFlags.REFLECTABLE) && - !this.reflected && - targets.some(t => t.hasAbilityWithAttr(ReflectStatusMoveAbAttr) || !!t.getTag(BattlerTagType.MAGIC_COAT)); + this.firstHit = user.turnData.hitCount === user.turnData.hitsLeft; + this.lastHit = user.turnData.hitsLeft === 1 || !targets.some(t => t.isActive(true)); - /** - * If no targets are left for the move to hit and it is not a hazard move (FAIL), or the invoked move is non-reflectable, single-target - * (and not random target) and failed the hit check against its target (MISS), log the move - * as FAILed or MISSed (depending on the conditions above) and end this phase. - */ + // Play the animation if the move was successful against any of its targets or it has a POST_TARGET effect (like self destruct) if ( - (!hasActiveTargets && !move.hasAttr(AddArenaTrapTagAttr)) || - (!mayBounce && - !move.hasAttr(VariableTargetAttr) && - !move.isMultiTarget() && - !targetHitChecks[this.targets[0]] && - !targets[0].getTag(ProtectedTag) && - !isImmune) + this.moveHistoryEntry.result === MoveResult.SUCCESS || + move.getAttrs(MoveEffectAttr).some(attr => attr.trigger === MoveEffectTrigger.POST_TARGET) ) { - this.stopMultiHit(); - if (hasActiveTargets) { - globalScene.queueMessage( - i18next.t("battle:attackMissed", { - pokemonNameWithAffix: this.getFirstTarget() ? getPokemonNameWithAffix(this.getFirstTarget()!) : "", - }), - ); - moveHistoryEntry.result = MoveResult.MISS; - applyMoveAttrs(MissEffectAttr, user, null, this.move.getMove()); - } else { - globalScene.queueMessage(i18next.t("battle:attackFailed")); - moveHistoryEntry.result = MoveResult.FAIL; - } - user.pushMoveHistory(moveHistoryEntry); - return this.end(); + const firstTarget = this.getFirstTarget(); + new MoveAnim( + move.id as Moves, + user, + firstTarget?.getBattlerIndex() ?? BattlerIndex.ATTACKER, + // Field moves and some moves used in mystery encounters should be played even on an empty field + fieldMove || (globalScene.currentBattle?.mysteryEncounter?.hasBattleAnimationsWithoutTargets ?? false), + ).play(move.hitsSubstitute(user, firstTarget), () => this.postAnimCallback(user, targets)); + + return; + } + this.postAnimCallback(user, targets); + } + + /** + * Callback to be called after the move animation is played + */ + private postAnimCallback(user: Pokemon, targets: Pokemon[]) { + // Add to the move history entry + if (this.firstHit) { + user.pushMoveHistory(this.moveHistoryEntry); } - const playOnEmptyField = - (globalScene.currentBattle?.mysteryEncounter?.hasBattleAnimationsWithoutTargets ?? false) || - (!hasActiveTargets && move.hasAttr(AddArenaTrapTagAttr)); - // Move animation only needs one target. The attacker is used as a fallback. - new MoveAnim( - move.id as Moves, - user, - this.getFirstTarget()?.getBattlerIndex() ?? BattlerIndex.ATTACKER, - playOnEmptyField, - ).play(move.hitsSubstitute(user, this.getFirstTarget()!), () => { - /** Has the move successfully hit a target (for damage) yet? */ - let hasHit = false; - - // Prevent ENEMY_SIDE targeted moves from occurring twice in double battles - // and check which target will magic bounce. - // In the event that the move is a hazard move, there may be no target and the move should still succeed. - // In this case, the user is used as the "target" to prevent a crash. - // This should not affect normal execution of the move otherwise. - const trueTargets: Pokemon[] = - !hasActiveTargets && move.hasAttr(AddArenaTrapTagAttr) - ? [user] - : move.moveTarget !== MoveTarget.ENEMY_SIDE - ? targets - : (() => { - const magicCoatTargets = targets.filter( - t => t.getTag(BattlerTagType.MAGIC_COAT) || t.hasAbilityWithAttr(ReflectStatusMoveAbAttr), - ); - - // only magic coat effect cares about order - if (!mayBounce || magicCoatTargets.length === 0) { - return [targets[0]]; - } - return [magicCoatTargets[0]]; - })(); - - const queuedPhases: Phase[] = []; - for (const target of trueTargets) { - /** The {@linkcode ArenaTagSide} to which the target belongs */ - const targetSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; - /** Has the invoked move been cancelled by conditional protection (e.g Quick Guard)? */ - const hasConditionalProtectApplied = new BooleanHolder(false); - /** Does the applied conditional protection bypass Protect-ignoring effects? */ - const bypassIgnoreProtect = new BooleanHolder(false); - /** If the move is not targeting a Pokemon on the user's side, try to apply conditional protection effects */ - if (!this.move.getMove().isAllyTarget()) { - globalScene.arena.applyTagsForSide( - ConditionalProtectTag, - targetSide, - false, - hasConditionalProtectApplied, - user, - target, - move.id, - bypassIgnoreProtect, - ); - } - - /** Is the target protected by Protect, etc. or a relevant conditional protection effect? */ - const isProtected = - ![MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES].includes(this.move.getMove().moveTarget) && - (bypassIgnoreProtect.value || - !this.move.getMove().doesFlagEffectApply({ flag: MoveFlags.IGNORE_PROTECT, user, target })) && - (hasConditionalProtectApplied.value || - (!target.findTags(t => t instanceof DamageProtectedTag).length && - target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))) || - (this.move.getMove().category !== MoveCategory.STATUS && - target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType)))); - - /** Is the target hidden by the effects of its Commander ability? */ - const isCommanding = - globalScene.currentBattle.double && - target.getAlly()?.getTag(BattlerTagType.COMMANDED)?.getSourcePokemon() === target; - - /** Is the target reflecting status moves from the magic coat move? */ - const isReflecting = !!target.getTag(BattlerTagType.MAGIC_COAT); - - /** Is the target's magic bounce ability not ignored and able to reflect this move? */ - const canMagicBounce = - !isReflecting && - !move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user, target }) && - target.hasAbilityWithAttr(ReflectStatusMoveAbAttr); - - const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); - - /** Is the target reflecting the effect, not protected, and not in an semi-invulnerable state?*/ - const willBounce = - !isProtected && - !this.reflected && - !isCommanding && - move.hasFlag(MoveFlags.REFLECTABLE) && - (isReflecting || canMagicBounce) && - !semiInvulnerableTag; - - // If the move will bounce, then queue the bounce and move on to the next target - if (!target.switchOutStatus && willBounce) { - const newTargets = move.isMultiTarget() ? getMoveTargets(target, move.id).targets : [user.getBattlerIndex()]; - if (!isReflecting) { - // TODO: Ability displays should be handled by the ability - queuedPhases.push( - new ShowAbilityPhase( - target.getBattlerIndex(), - target.getPassiveAbility().hasAttr(ReflectStatusMoveAbAttr), - ), - ); - queuedPhases.push(new HideAbilityPhase()); - } - - queuedPhases.push(new MovePhase(target, newTargets, new PokemonMove(move.id, 0, 0, true), true, true, true)); - continue; - } - - /** Is the pokemon immune due to an ablility, and also not in a semi invulnerable state? */ - const isImmune = - target.hasAbilityWithAttr(TypeImmunityAbAttr) && - target.getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move) && - !semiInvulnerableTag; - - /** - * If the move missed a target, stop all future hits against that target - * and move on to the next target (if there is one). - */ - if ( - target.switchOutStatus || - isCommanding || - (!isImmune && - !isProtected && - !targetHitChecks[target.getBattlerIndex()] && - !move.hasAttr(AddArenaTrapTagAttr)) - ) { - this.stopMultiHit(target); - if (!target.switchOutStatus) { - globalScene.queueMessage( - i18next.t("battle:attackMissed", { - pokemonNameWithAffix: getPokemonNameWithAffix(target), - }), - ); - } - if (moveHistoryEntry.result === MoveResult.PENDING) { - moveHistoryEntry.result = MoveResult.MISS; - } - user.pushMoveHistory(moveHistoryEntry); - applyMoveAttrs(MissEffectAttr, user, null, move); - continue; - } - - /** Does this phase represent the invoked move's first strike? */ - const firstHit = user.turnData.hitsLeft === user.turnData.hitCount; - - // Only log the move's result on the first strike - if (firstHit) { - user.pushMoveHistory(moveHistoryEntry); - } - - /** - * Since all fail/miss checks have applied, the move is considered successfully applied. - * It's worth noting that if the move has no effect or is protected against, this assignment - * is overwritten and the move is logged as a FAIL. - */ - moveHistoryEntry.result = MoveResult.SUCCESS; - - /** - * Stores the result of applying the invoked move to the target. - * If the target is protected, the result is always `NO_EFFECT`. - * Otherwise, the hit result is based on type effectiveness, immunities, - * and other factors that may negate the attack or status application. - * - * Internally, the call to {@linkcode Pokemon.apply} is where damage is calculated - * (for attack moves) and the target's HP is updated. However, this isn't - * made visible to the user until the resulting {@linkcode DamagePhase} - * is invoked. - */ - const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT; - - /** Does {@linkcode hitResult} indicate that damage was dealt to the target? */ - const dealsDamage = [ - HitResult.EFFECTIVE, - HitResult.SUPER_EFFECTIVE, - HitResult.NOT_VERY_EFFECTIVE, - HitResult.ONE_HIT_KO, - ].includes(hitResult); - - /** Is this target the first one hit by the move on its current strike? */ - const firstTarget = dealsDamage && !hasHit; - if (firstTarget) { - hasHit = true; - } - - /** - * If the move has no effect on the target (i.e. the target is protected or immune), - * change the logged move result to FAIL. - */ - if (hitResult === HitResult.NO_EFFECT) { - moveHistoryEntry.result = MoveResult.FAIL; - } - - /** Does this phase represent the invoked move's last strike? */ - const lastHit = user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive(); - - /** - * If the user can change forms by using the invoked move, - * it only changes forms after the move's last hit - * (see Relic Song's interaction with Parental Bond when used by Meloetta). - */ - if (lastHit) { - globalScene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger); - /** - * Multi-Lens, Multi Hit move and Parental Bond check for PostDamageAbAttr - * other damage source are calculated in damageAndUpdate in pokemon.ts - */ - if (user.turnData.hitCount > 1) { - applyPostDamageAbAttrs(PostDamageAbAttr, target, 0, target.hasPassive(), false, [], user); - } - } - - applyFilteredMoveAttrs( - (attr: MoveAttr) => - attr instanceof MoveEffectAttr && - attr.trigger === MoveEffectTrigger.PRE_APPLY && - (!attr.firstHitOnly || firstHit) && - (!attr.lastHitOnly || lastHit) && - hitResult !== HitResult.NO_EFFECT, - user, - target, - move, - ); - - if (hitResult !== HitResult.FAIL) { - this.applySelfTargetEffects(user, target, firstHit, lastHit); - - if (hitResult !== HitResult.NO_EFFECT) { - this.applyPostApplyEffects(user, target, firstHit, lastHit); - this.applyHeldItemFlinchCheck(user, target, dealsDamage); - this.applySuccessfulAttackEffects(user, target, firstHit, lastHit, !!isProtected, hitResult, firstTarget); - } else { - applyMoveAttrs(NoEffectAttr, user, null, move); - } - } - } - - // Apply queued phases - if (queuedPhases.length) { - globalScene.appendToPhase(queuedPhases, MoveEndPhase); - } - // Apply the move's POST_TARGET effects on the move's last hit, after all targeted effects have resolved - if (user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive()) { - applyFilteredMoveAttrs( - (attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET, - user, - null, - move, - ); - } - - /** - * Remove the target's substitute (if it exists and has expired) - * after all targeted effects have applied. - * This prevents blocked effects from applying until after this hit resolves. - */ - targets.forEach(target => { - const substitute = target.getTag(SubstituteTag); - if (substitute && substitute.hp <= 0) { - target.lapseTag(BattlerTagType.SUBSTITUTE); - } - }); - - const moveType = user.getMoveType(move, true); - if (move.category !== MoveCategory.STATUS && !user.stellarTypesBoosted.includes(moveType)) { - user.stellarTypesBoosted.push(moveType); - } - + try { + this.applyToTargets(user, targets); + } catch (e) { + console.warn(e.message || "Unexpected error in move effect phase"); this.end(); - }); + return; + } + + if (this.queuedPhases.length) { + globalScene.appendToPhase(this.queuedPhases, MoveEndPhase); + } + const moveType = user.getMoveType(this.move, true); + if (this.move.category !== MoveCategory.STATUS && !user.stellarTypesBoosted.includes(moveType)) { + user.stellarTypesBoosted.push(moveType); + } + + if (this.lastHit) { + this.triggerMoveEffects(MoveEffectTrigger.POST_TARGET, user, null); + } + + this.updateSubstitutes(); + this.end(); } public override end(): void { @@ -535,7 +418,6 @@ export class MoveEffectPhase extends PokemonPhase { globalScene.queueMessage(i18next.t("battle:attackHitsCount", { count: hitsTotal })); } globalScene.applyModifiers(HitHealModifier, this.player, user); - // Clear all cached move effectiveness values among targets this.getTargets().forEach(target => (target.turnData.moveEffectiveness = null)); } } @@ -543,82 +425,6 @@ export class MoveEffectPhase extends PokemonPhase { super.end(); } - /** - * Apply self-targeted effects that trigger `POST_APPLY` - * - * @param user - The {@linkcode Pokemon} using this phase's invoked move - * @param target - {@linkcode Pokemon} the current target of this phase's invoked move - * @param firstHit - `true` if this is the first hit in a multi-hit attack - * @param lastHit - `true` if this is the last hit in a multi-hit attack - * @returns a function intended to be passed into a `then()` call. - */ - protected applySelfTargetEffects(user: Pokemon, target: Pokemon, firstHit: boolean, lastHit: boolean): void { - applyFilteredMoveAttrs( - (attr: MoveAttr) => - attr instanceof MoveEffectAttr && - attr.trigger === MoveEffectTrigger.POST_APPLY && - attr.selfTarget && - (!attr.firstHitOnly || firstHit) && - (!attr.lastHitOnly || lastHit), - user, - target, - this.move.getMove(), - ); - } - - /** - * Applies non-self-targeted effects that trigger `POST_APPLY` - * (i.e. Smelling Salts curing Paralysis, and the forced switch from U-Turn, Dragon Tail, etc) - * @param user - The {@linkcode Pokemon} using this phase's invoked move - * @param target - {@linkcode Pokemon} the current target of this phase's invoked move - * @param firstHit - `true` if this is the first hit in a multi-hit attack - * @param lastHit - `true` if this is the last hit in a multi-hit attack - * @returns a function intended to be passed into a `then()` call. - */ - protected applyPostApplyEffects(user: Pokemon, target: Pokemon, firstHit: boolean, lastHit: boolean): void { - applyFilteredMoveAttrs( - (attr: MoveAttr) => - attr instanceof MoveEffectAttr && - attr.trigger === MoveEffectTrigger.POST_APPLY && - !attr.selfTarget && - (!attr.firstHitOnly || firstHit) && - (!attr.lastHitOnly || lastHit), - user, - target, - this.move.getMove(), - ); - } - - /** - * Applies effects that trigger on HIT - * (i.e. Final Gambit, Power-Up Punch, Drain Punch) - * @param user - The {@linkcode Pokemon} using this phase's invoked move - * @param target - {@linkcode Pokemon} the current target of this phase's invoked move - * @param firstHit - `true` if this is the first hit in a multi-hit attack - * @param lastHit - `true` if this is the last hit in a multi-hit attack - * @param firstTarget - `true` if {@linkcode target} is the first target hit by this strike of {@linkcode move} - * @returns a function intended to be passed into a `then()` call. - */ - protected applyOnHitEffects( - user: Pokemon, - target: Pokemon, - firstHit: boolean, - lastHit: boolean, - firstTarget: boolean, - ): void { - applyFilteredMoveAttrs( - (attr: MoveAttr) => - attr instanceof MoveEffectAttr && - attr.trigger === MoveEffectTrigger.HIT && - (!attr.firstHitOnly || firstHit) && - (!attr.lastHitOnly || lastHit) && - (!attr.firstTargetOnly || firstTarget), - user, - target, - this.move.getMove(), - ); - } - /** * Applies reactive effects that occur when a Pokémon is hit. * (i.e. Effect Spore, Disguise, Liquid Ooze, Beak Blast) @@ -627,51 +433,9 @@ export class MoveEffectPhase extends PokemonPhase { * @param hitResult - The {@linkcode HitResult} of the attempted move * @returns a `Promise` intended to be passed into a `then()` call. */ - protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult) { - const hitsSubstitute = this.move.getMove().hitsSubstitute(user, target); - if (!target.isFainted() || target.canApplyAbility()) { - applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult); - - if (!hitsSubstitute) { - if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) { - globalScene.applyShuffledModifiers(EnemyAttackStatusEffectChanceModifier, false, target); - } - } - } - if (!hitsSubstitute) { - target.lapseTags(BattlerTagLapseType.AFTER_HIT); - } - } - - /** - * Applies all effects and attributes that require a move to connect with a target, - * namely reactive effects like Weak Armor, on-hit effects like that of Power-Up Punch, and item stealing effects - * @param user - The {@linkcode Pokemon} using this phase's invoked move - * @param target - {@linkcode Pokemon} the current target of this phase's invoked move - * @param firstHit - `true` if this is the first hit in a multi-hit attack - * @param lastHit - `true` if this is the last hit in a multi-hit attack - * @param isProtected - `true` if the target is protected by effects such as Protect - * @param hitResult - The {@linkcode HitResult} of the attempted move - * @param firstTarget - `true` if {@linkcode target} is the first target hit by this strike of {@linkcode move} - * @returns a function intended to be passed into a `then()` call. - */ - protected applySuccessfulAttackEffects( - user: Pokemon, - target: Pokemon, - firstHit: boolean, - lastHit: boolean, - isProtected: boolean, - hitResult: HitResult, - firstTarget: boolean, - ): void { - if (!isProtected) { - this.applyOnHitEffects(user, target, firstHit, lastHit, firstTarget); - this.applyOnGetHitAbEffects(user, target, hitResult); - applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move.getMove(), hitResult); - if (this.move.getMove() instanceof AttackMove && hitResult !== HitResult.STATUS) { - globalScene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target); - } - } + protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult): void { + applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, hitResult); + target.lapseTags(BattlerTagLapseType.AFTER_HIT); } /** @@ -682,80 +446,162 @@ export class MoveEffectPhase extends PokemonPhase { * @returns a function intended to be passed into a `then()` call. */ protected applyHeldItemFlinchCheck(user: Pokemon, target: Pokemon, dealsDamage: boolean): void { - if (this.move.getMove().hasAttr(FlinchAttr)) { + if (this.move.hasAttr(FlinchAttr)) { return; } - if ( - dealsDamage && - !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && - !this.move.getMove().hitsSubstitute(user, target) - ) { + if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && !this.move.hitsSubstitute(user, target)) { const flinched = new BooleanHolder(false); globalScene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched); if (flinched.value) { - target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id); + target.addTag(BattlerTagType.FLINCHED, undefined, this.move.id, user.id); } } } - /** - * Resolves whether this phase's invoked move hits the given target - * @param target - The {@linkcode Pokemon} targeted by the invoked move - * @returns `true` if the move hits the target + /** Return whether the target is protected by protect or a relevant conditional protection + * @param user - The {@linkcode Pokemon} using this phase's invoked move + * @param target - {@linkcode Pokemon} the target to check for protection + * @param move - The {@linkcode Move} being used */ - public hitCheck(target: Pokemon): boolean { - // Moves targeting the user and entry hazards can't miss - if ([MoveTarget.USER, MoveTarget.ENEMY_SIDE].includes(this.move.getMove().moveTarget)) { - return true; + private protectedCheck(user: Pokemon, target: Pokemon) { + /** The {@linkcode ArenaTagSide} to which the target belongs */ + const targetSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + /** Has the invoked move been cancelled by conditional protection (e.g Quick Guard)? */ + const hasConditionalProtectApplied = new BooleanHolder(false); + /** Does the applied conditional protection bypass Protect-ignoring effects? */ + const bypassIgnoreProtect = new BooleanHolder(false); + /** If the move is not targeting a Pokemon on the user's side, try to apply conditional protection effects */ + if (!this.move.isAllyTarget()) { + globalScene.arena.applyTagsForSide( + ConditionalProtectTag, + targetSide, + false, + hasConditionalProtectApplied, + user, + target, + this.move.id, + bypassIgnoreProtect, + ); } + return ( + ![MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES].includes(this.move.moveTarget) && + (bypassIgnoreProtect.value || !this.move.doesFlagEffectApply({ flag: MoveFlags.IGNORE_PROTECT, user, target })) && + (hasConditionalProtectApplied.value || + (!target.findTags(t => t instanceof DamageProtectedTag).length && + target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))) || + (this.move.category !== MoveCategory.STATUS && + target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType)))) + ); + } + + /** + * Conduct the hit check and type effectiveness for this move against the target + * + * Checks occur in the following order: + * 1. if the move is self-target + * 2. if the target is on the field + * 3. if the target is hidden by the effects of its commander ability + * 4. if the target is in an applicable semi-invulnerable state + * 5. if the target has an applicable protection effect + * 6. if the move is reflected by magic coat or magic bounce + * 7. type effectiveness calculation, including immunities from abilities and typing + * 9. if accuracy is checked, whether the roll passes the accuracy check + * @param target - The {@linkcode Pokemon} targeted by the invoked move + * @returns a {@linkcode HitCheckEntry} containing the attack's {@linkcode HitCheckResult} + * and {@linkcode TypeDamageMultiplier | effectiveness} against the target. + */ + public hitCheck(target: Pokemon): HitCheckEntry { const user = this.getUserPokemon(); + const move = this.move; if (!user) { - return false; + return [HitCheckResult.ERROR, 0]; } - // Hit check only calculated on first hit for multi-hit moves unless flag is set to check all hits. - // However, if an ability with the MaxMultiHitAbAttr, namely Skill Link, is present, act as a normal - // multi-hit move and proceed with all hits + // Moves targeting the user bypass all checks + if (move.moveTarget === MoveTarget.USER) { + return [HitCheckResult.HIT, 1]; + } + + const fieldTargeted = isFieldTargeted(move); + + if (!target.isActive(true) && !fieldTargeted) { + return [HitCheckResult.TARGET_NOT_ON_FIELD, 0]; + } + + // Commander causes moves used against the target to miss + if ( + !fieldTargeted && + globalScene.currentBattle.double && + target.getAlly()?.getTag(BattlerTagType.COMMANDED)?.getSourcePokemon() === target + ) { + return [HitCheckResult.MISS, 0]; + } + + /** Whether both accuracy and invulnerability checks can be skipped */ + const bypassAccAndInvuln = fieldTargeted || this.checkBypassAccAndInvuln(target); + const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); + + if (semiInvulnerableTag && !bypassAccAndInvuln && !this.checkBypassSemiInvuln(semiInvulnerableTag)) { + return [HitCheckResult.MISS, 0]; + } + + if (!fieldTargeted && this.protectedCheck(user, target)) { + return [HitCheckResult.PROTECTED, 0]; + } + + if (!this.reflected && move.doesFlagEffectApply({ flag: MoveFlags.REFLECTABLE, user, target })) { + return [HitCheckResult.REFLECTED, 0]; + } + + // After the magic bounce check, field targeted moves are always successful + if (fieldTargeted) { + return [HitCheckResult.HIT, 1]; + } + + const cancelNoEffectMessage = new BooleanHolder(false); + + /** + * The effectiveness of the move against the given target. + * Accounts for type and move immunities from defensive typing, abilities, and other effects. + */ + const effectiveness = target.getMoveEffectiveness(user, move, false, false, cancelNoEffectMessage); + if (effectiveness === 0) { + return [ + cancelNoEffectMessage.value ? HitCheckResult.NO_EFFECT_NO_MESSAGE : HitCheckResult.NO_EFFECT, + effectiveness, + ]; + } + + const moveAccuracy = move.calculateBattleAccuracy(user, target); + + // Strikes after the first in a multi-strike move are guaranteed to hit, + // unless the move is flagged to check all hits and the user does not have Skill Link. if (user.turnData.hitsLeft < user.turnData.hitCount) { - if (!this.move.getMove().hasFlag(MoveFlags.CHECK_ALL_HITS) || user.hasAbilityWithAttr(MaxMultiHitAbAttr)) { - return true; + if (!move.hasFlag(MoveFlags.CHECK_ALL_HITS) || user.hasAbilityWithAttr(MaxMultiHitAbAttr)) { + return [HitCheckResult.HIT, effectiveness]; } } - if (this.checkBypassAccAndInvuln(target)) { - return true; + const bypassAccuracy = + bypassAccAndInvuln || + target.getTag(BattlerTagType.ALWAYS_GET_HIT) || + (target.getTag(BattlerTagType.TELEKINESIS) && !this.move.hasAttr(OneHitKOAttr)); + + if (moveAccuracy === -1 || bypassAccuracy) { + return [HitCheckResult.HIT, effectiveness]; } - if (target.getTag(BattlerTagType.ALWAYS_GET_HIT)) { - return true; - } - - const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); - if ( - target.getTag(BattlerTagType.TELEKINESIS) && - !semiInvulnerableTag && - !this.move.getMove().hasAttr(OneHitKOAttr) - ) { - return true; - } - - if (semiInvulnerableTag && !this.checkBypassSemiInvuln(semiInvulnerableTag)) { - return false; - } - - const moveAccuracy = this.move.getMove().calculateBattleAccuracy(user, target); - - if (moveAccuracy === -1) { - return true; - } - - const accuracyMultiplier = user.getAccuracyMultiplier(target, this.move.getMove()); + const accuracyMultiplier = user.getAccuracyMultiplier(target, this.move); const rand = user.randSeedInt(100); - return rand < moveAccuracy * accuracyMultiplier; + if (rand < moveAccuracy * accuracyMultiplier) { + return [HitCheckResult.HIT, effectiveness]; + } + + return [HitCheckResult.MISS, 0]; } /** @@ -767,6 +613,7 @@ export class MoveEffectPhase extends PokemonPhase { * - An ability like {@linkcode Abilities.NO_GUARD | No Guard} * - A poison type using {@linkcode Moves.TOXIC | Toxic} * - A move like {@linkcode Moves.LOCK_ON | Lock-On} or {@linkcode Moves.MIND_READER | Mind Reader}. + * - A field-targeted move like spikes * * Does *not* check against effects {@linkcode Moves.GLAIVE_RUSH | Glaive Rush} status (which * should not bypass semi-invulnerability), or interactions like Earthquake hitting against Dig, @@ -782,7 +629,7 @@ export class MoveEffectPhase extends PokemonPhase { if (user.hasAbilityWithAttr(AlwaysHitAbAttr) || target.hasAbilityWithAttr(AlwaysHitAbAttr)) { return true; } - if (this.move.getMove().hasAttr(ToxicAccuracyAttr) && user.isOfType(PokemonType.POISON)) { + if (this.move.hasAttr(ToxicAccuracyAttr) && user.isOfType(PokemonType.POISON)) { return true; } // TODO: Fix lock on / mind reader check. @@ -792,18 +639,21 @@ export class MoveEffectPhase extends PokemonPhase { ) { return true; } + if (isFieldTargeted(this.move)) { + return true; + } } /** * Check whether the move is able to ignore the given `semiInvulnerableTag` - * @param semiInvulnerableTag - The semiInvulnerbale tag to check against + * @param semiInvulnerableTag - The semiInvulnerable tag to check against * @returns `true` if the move can ignore the semi-invulnerable state */ public checkBypassSemiInvuln(semiInvulnerableTag: SemiInvulnerableTag | nil): boolean { if (!semiInvulnerableTag) { return false; } - const move = this.move.getMove(); + const move = this.move; return move.getAttrs(HitsTagAttr).some(hta => hta.tagType === semiInvulnerableTag.tagType); } @@ -862,6 +712,282 @@ export class MoveEffectPhase extends PokemonPhase { /** @returns A new `MoveEffectPhase` with the same properties as this phase */ protected getNewHitPhase(): MoveEffectPhase { - return new MoveEffectPhase(this.battlerIndex, this.targets, this.move); + return new MoveEffectPhase(this.battlerIndex, this.targets, this.move, this.reflected, this.virtual); + } + + /** Removes all substitutes that were broken by this phase's invoked move */ + protected updateSubstitutes(): void { + const targets = this.getTargets(); + for (const target of targets) { + const substitute = target.getTag(SubstituteTag); + if (substitute && substitute.hp <= 0) { + target.lapseTag(BattlerTagType.SUBSTITUTE); + } + } + } + + /** + * Triggers move effects of the given move effect trigger. + * @param triggerType The {@linkcode MoveEffectTrigger} being applied + * @param user The {@linkcode Pokemon} using the move + * @param target The {@linkcode Pokemon} targeted by the move + * @param firstTarget Whether the target is the first to be hit by the current strike + * @param selfTarget If defined, limits the effects triggered to either self-targeted + * effects (if set to `true`) or targeted effects (if set to `false`). + * @returns a `Promise` applying the relevant move effects. + */ + protected triggerMoveEffects( + triggerType: MoveEffectTrigger, + user: Pokemon, + target: Pokemon | null, + firstTarget?: boolean | null, + selfTarget?: boolean, + ): void { + return applyFilteredMoveAttrs( + (attr: MoveAttr) => + attr instanceof MoveEffectAttr && + attr.trigger === triggerType && + (isNullOrUndefined(selfTarget) || attr.selfTarget === selfTarget) && + (!attr.firstHitOnly || this.firstHit) && + (!attr.lastHitOnly || this.lastHit) && + (!attr.firstTargetOnly || (firstTarget ?? true)), + user, + target, + this.move, + ); + } + + /** + * Applies all move effects that trigger in the event of a successful hit: + * + * - {@linkcode MoveEffectTrigger.PRE_APPLY | PRE_APPLY} effects` + * - Applying damage to the target + * - {@linkcode MoveEffectTrigger.POST_APPLY | POST_APPLY} effects + * - Invoking {@linkcode applyOnTargetEffects} if the move does not hit a substitute + * - Triggering form changes and emergency exit / wimp out if this is the last hit + * + * @param target the {@linkcode Pokemon} hit by this phase's move. + * @param effectiveness the effectiveness of the move (as previously evaluated in {@linkcode hitCheck}) + */ + protected applyMoveEffects(target: Pokemon, effectiveness: TypeDamageMultiplier): void { + const user = this.getUserPokemon(); + + /** The first target hit by the move */ + const firstTarget = target === this.getTargets().find((_, i) => this.hitChecks[i][1] > 0); + + if (isNullOrUndefined(user)) { + return; + } + + this.triggerMoveEffects(MoveEffectTrigger.PRE_APPLY, user, target); + + const hitResult = this.applyMove(user, target, effectiveness); + + this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, true); + if (!this.move.hitsSubstitute(user, target)) { + this.applyOnTargetEffects(user, target, hitResult, firstTarget); + } + if (this.lastHit) { + globalScene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger); + + // Multi-hit check for Wimp Out/Emergency Exit + if (user.turnData.hitCount > 1) { + applyPostDamageAbAttrs(PostDamageAbAttr, target, 0, target.hasPassive(), false, [], user); + } + } + } + + /** + * Sub-method of for {@linkcode applyMoveEffects} that applies damage to the target. + * + * @param user - The {@linkcode Pokemon} using this phase's invoked move + * @param target - The {@linkcode Pokemon} targeted by the move + * @param effectiveness - The effectiveness of the move against the target + */ + protected applyMoveDamage(user: Pokemon, target: Pokemon, effectiveness: TypeDamageMultiplier): HitResult { + const isCritical = target.getCriticalHitResult(user, this.move, false); + + /* + * Apply stat changes from {@linkcode move} and gives it to {@linkcode source} + * before damage calculation + */ + applyMoveAttrs(StatChangeBeforeDmgCalcAttr, user, target, this.move); + + const { result: result, damage: dmg } = target.getAttackDamage({ + source: user, + move: this.move, + ignoreAbility: false, + ignoreSourceAbility: false, + ignoreAllyAbility: false, + ignoreSourceAllyAbility: false, + simulated: false, + effectiveness, + isCritical, + }); + + const typeBoost = user.findTag( + t => t instanceof TypeBoostTag && t.boostedType === user.getMoveType(this.move), + ) as TypeBoostTag; + if (typeBoost?.oneUse) { + user.removeTag(typeBoost.tagType); + } + + const isOneHitKo = result === HitResult.ONE_HIT_KO; + + if (!dmg) { + return result; + } + + target.lapseTags(BattlerTagLapseType.HIT); + + const substitute = target.getTag(SubstituteTag); + const isBlockedBySubstitute = substitute && this.move.hitsSubstitute(user, target); + if (isBlockedBySubstitute) { + substitute.hp -= dmg; + } else if (!target.isPlayer() && dmg >= target.hp) { + globalScene.applyModifiers(EnemyEndureChanceModifier, false, target); + } + + const damage = isBlockedBySubstitute + ? 0 + : target.damageAndUpdate(dmg, { + result: result as DamageResult, + ignoreFaintPhase: true, + ignoreSegments: isOneHitKo, + isCritical, + source: user, + }); + + if (isCritical) { + globalScene.queueMessage(i18next.t("battle:criticalHit")); + } + + if (damage <= 0) { + return result; + } + + if (user.isPlayer()) { + globalScene.validateAchvs(DamageAchv, new NumberHolder(damage)); + + if (damage > globalScene.gameData.gameStats.highestDamage) { + globalScene.gameData.gameStats.highestDamage = damage; + } + } + + user.turnData.totalDamageDealt += damage; + user.turnData.singleHitDamageDealt = damage; + target.battleData.hitCount++; + target.turnData.damageTaken += damage; + + target.turnData.attacksReceived.unshift({ + move: this.move.id, + result: result as DamageResult, + damage: damage, + critical: isCritical, + sourceId: user.id, + sourceBattlerIndex: user.getBattlerIndex(), + }); + + if (user.isPlayer() && !target.isPlayer()) { + globalScene.applyModifiers(DamageMoneyRewardModifier, true, user, new NumberHolder(damage)); + } + + return result; + } + + /** + * Sub-method of {@linkcode applyMove} that handles the event of a target fainting. + * @param user - The {@linkcode Pokemon} using this phase's invoked move + * @param target - The {@linkcode Pokemon} that fainted + */ + protected onFaintTarget(user: Pokemon, target: Pokemon): void { + // set splice index here, so future scene queues happen before FaintedPhase + globalScene.setPhaseQueueSplice(); + + globalScene.unshiftPhase(new FaintPhase(target.getBattlerIndex(), false, user)); + + target.destroySubstitute(); + target.lapseTag(BattlerTagType.COMMANDED); + } + + /** + * Sub-method of {@linkcode applyMove} that queues the hit-result message + * on the final strike of the move against a target + * @param result - The {@linkcode HitResult} of the move + */ + protected queueHitResultMessage(result: HitResult) { + let msg: string | undefined; + switch (result) { + case HitResult.SUPER_EFFECTIVE: + msg = i18next.t("battle:hitResultSuperEffective"); + break; + case HitResult.NOT_VERY_EFFECTIVE: + msg = i18next.t("battle:hitResultNotVeryEffective"); + break; + case HitResult.ONE_HIT_KO: + msg = i18next.t("battle:hitResultOneHitKO"); + break; + } + if (msg) { + globalScene.queueMessage(msg); + } + } + + /** Apply the result of this phase's move to the given target + * @param user - The {@linkcode Pokemon} using this phase's invoked move + * @param target - The {@linkcode Pokemon} struck by the move + * @param effectiveness - The effectiveness of the move against the target + */ + protected applyMove(user: Pokemon, target: Pokemon, effectiveness: TypeDamageMultiplier): HitResult { + const moveCategory = user.getMoveCategory(target, this.move); + + if (moveCategory === MoveCategory.STATUS) { + return HitResult.STATUS; + } + + const result = this.applyMoveDamage(user, target, effectiveness); + + if (user.turnData.hitsLeft === 1 && target.isFainted()) { + this.queueHitResultMessage(result); + } + + if (target.isFainted()) { + this.onFaintTarget(user, target); + } + + return result; + } + + /** + * Applies all effects aimed at the move's target. + * To be used when the target is successfully and directly hit by the move. + * @param user - The {@linkcode Pokemon} using the move + * @param target - The {@linkcode Pokemon} targeted by the move + * @param hitResult - The {@linkcode HitResult} obtained from applying the move + * @param firstTarget - `true` if the target is the first Pokemon hit by the attack + */ + protected applyOnTargetEffects(user: Pokemon, target: Pokemon, hitResult: HitResult, firstTarget: boolean): void { + /** Does {@linkcode hitResult} indicate that damage was dealt to the target? */ + const dealsDamage = [ + HitResult.EFFECTIVE, + HitResult.SUPER_EFFECTIVE, + HitResult.NOT_VERY_EFFECTIVE, + HitResult.ONE_HIT_KO, + ].includes(hitResult); + + this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, false); + this.applyHeldItemFlinchCheck(user, target, dealsDamage); + this.applyOnGetHitAbEffects(user, target, hitResult); + applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move, hitResult); + + // We assume only enemy Pokemon are able to have the EnemyAttackStatusEffectChanceModifier from tokens + if (!user.isPlayer() && this.move instanceof AttackMove) { + globalScene.applyShuffledModifiers(EnemyAttackStatusEffectChanceModifier, false, target); + } + + // Apply Grip Claw's chance to steal an item from the target + if (this.move instanceof AttackMove) { + globalScene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target); + } } } diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 7d2848a5d70..b24d7b61ebb 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -404,9 +404,10 @@ export class MovePhase extends BattlePhase { * if the move fails. */ if (success) { - applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove()); + const move = this.move.getMove(); + applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, move); globalScene.unshiftPhase( - new MoveEffectPhase(this.pokemon.getBattlerIndex(), this.targets, this.move, this.reflected), + new MoveEffectPhase(this.pokemon.getBattlerIndex(), this.targets, move, this.reflected, this.move.virtual), ); } else { if ([Moves.ROAR, Moves.WHIRLWIND, Moves.TRICK_OR_TREAT, Moves.FORESTS_CURSE].includes(this.move.moveId)) { diff --git a/test/abilities/friend_guard.test.ts b/test/abilities/friend_guard.test.ts index 302343c167b..43a378c47a2 100644 --- a/test/abilities/friend_guard.test.ts +++ b/test/abilities/friend_guard.test.ts @@ -50,7 +50,11 @@ describe("Moves - Friend Guard", () => { // Get the last return value from `getAttackDamage` const turn1Damage = spy.mock.results[spy.mock.results.length - 1].value.damage; // Making sure the test is controlled; turn 1 damage is equal to base damage (after rounding) - expect(turn1Damage).toBe(Math.floor(player1.getBaseDamage(enemy1, allMoves[Moves.TACKLE], MoveCategory.PHYSICAL))); + expect(turn1Damage).toBe( + Math.floor( + player1.getBaseDamage({ source: enemy1, move: allMoves[Moves.TACKLE], moveCategory: MoveCategory.PHYSICAL }), + ), + ); vi.spyOn(player2, "getAbility").mockReturnValue(allAbilities[Abilities.FRIEND_GUARD]); @@ -64,7 +68,10 @@ describe("Moves - Friend Guard", () => { const turn2Damage = spy.mock.results[spy.mock.results.length - 1].value.damage; // With the ally's Friend Guard, damage should have been reduced from base damage by 25% expect(turn2Damage).toBe( - Math.floor(player1.getBaseDamage(enemy1, allMoves[Moves.TACKLE], MoveCategory.PHYSICAL) * 0.75), + Math.floor( + player1.getBaseDamage({ source: enemy1, move: allMoves[Moves.TACKLE], moveCategory: MoveCategory.PHYSICAL }) * + 0.75, + ), ); }); diff --git a/test/abilities/galvanize.test.ts b/test/abilities/galvanize.test.ts index 438ec498aa1..5db8b642197 100644 --- a/test/abilities/galvanize.test.ts +++ b/test/abilities/galvanize.test.ts @@ -4,7 +4,6 @@ import { PokemonType } from "#enums/pokemon-type"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; -import { HitResult } from "#app/field/pokemon"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -38,13 +37,13 @@ describe("Abilities - Galvanize", () => { }); it("should change Normal-type attacks to Electric type and boost their power", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; vi.spyOn(playerPokemon, "getMoveType"); const enemyPokemon = game.scene.getEnemyPokemon()!; - vi.spyOn(enemyPokemon, "apply"); + const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness"); const move = allMoves[Moves.TACKLE]; vi.spyOn(move, "calculateBattlePower"); @@ -54,21 +53,23 @@ describe("Abilities - Galvanize", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(playerPokemon.getMoveType).toHaveLastReturnedWith(PokemonType.ELECTRIC); - expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.EFFECTIVE); + expect(spy).toHaveReturnedWith(1); expect(move.calculateBattlePower).toHaveReturnedWith(48); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); + + spy.mockRestore(); }); it("should cause Normal-type attacks to activate Volt Absorb", async () => { game.override.enemyAbility(Abilities.VOLT_ABSORB); - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; vi.spyOn(playerPokemon, "getMoveType"); const enemyPokemon = game.scene.getEnemyPokemon()!; - vi.spyOn(enemyPokemon, "apply"); + const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness"); enemyPokemon.hp = Math.floor(enemyPokemon.getMaxHp() * 0.8); @@ -77,37 +78,37 @@ describe("Abilities - Galvanize", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(playerPokemon.getMoveType).toHaveLastReturnedWith(PokemonType.ELECTRIC); - expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.NO_EFFECT); + expect(spy).toHaveReturnedWith(0); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); }); it("should not change the type of variable-type moves", async () => { game.override.enemySpecies(Species.MIGHTYENA); - await game.startBattle([Species.ESPEON]); + await game.classicMode.startBattle([Species.ESPEON]); const playerPokemon = game.scene.getPlayerPokemon()!; vi.spyOn(playerPokemon, "getMoveType"); const enemyPokemon = game.scene.getEnemyPokemon()!; - vi.spyOn(enemyPokemon, "apply"); + const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness"); game.move.select(Moves.REVELATION_DANCE); await game.phaseInterceptor.to("BerryPhase", false); expect(playerPokemon.getMoveType).not.toHaveLastReturnedWith(PokemonType.ELECTRIC); - expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.NO_EFFECT); + expect(spy).toHaveReturnedWith(0); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); }); it("should affect all hits of a Normal-type multi-hit move", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; vi.spyOn(playerPokemon, "getMoveType"); const enemyPokemon = game.scene.getEnemyPokemon()!; - vi.spyOn(enemyPokemon, "apply"); + const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness"); game.move.select(Moves.FURY_SWIPES); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); @@ -125,6 +126,6 @@ describe("Abilities - Galvanize", () => { expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp); } - expect(enemyPokemon.apply).not.toHaveReturnedWith(HitResult.NO_EFFECT); + expect(spy).not.toHaveReturnedWith(0); }); }); diff --git a/test/abilities/infiltrator.test.ts b/test/abilities/infiltrator.test.ts index 10353f35391..48671e54020 100644 --- a/test/abilities/infiltrator.test.ts +++ b/test/abilities/infiltrator.test.ts @@ -61,11 +61,11 @@ describe("Abilities - Infiltrator", () => { const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; - const preScreenDmg = enemy.getAttackDamage(player, allMoves[move]).damage; + const preScreenDmg = enemy.getAttackDamage({ source: player, move: allMoves[move] }).damage; game.scene.arena.addTag(tagType, 1, Moves.NONE, enemy.id, ArenaTagSide.ENEMY, true); - const postScreenDmg = enemy.getAttackDamage(player, allMoves[move]).damage; + const postScreenDmg = enemy.getAttackDamage({ source: player, move: allMoves[move] }).damage; expect(postScreenDmg).toBe(preScreenDmg); expect(player.battleData.abilitiesApplied[0]).toBe(Abilities.INFILTRATOR); diff --git a/test/abilities/no_guard.test.ts b/test/abilities/no_guard.test.ts index b34007bc700..a09e16388ee 100644 --- a/test/abilities/no_guard.test.ts +++ b/test/abilities/no_guard.test.ts @@ -4,6 +4,7 @@ import { MoveEndPhase } from "#app/phases/move-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import { HitCheckResult } from "#enums/hit-check-result"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; @@ -28,6 +29,7 @@ describe("Abilities - No Guard", () => { .moveset(Moves.ZAP_CANNON) .ability(Abilities.NO_GUARD) .enemyLevel(200) + .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH); }); @@ -48,7 +50,7 @@ describe("Abilities - No Guard", () => { await game.phaseInterceptor.to(MoveEndPhase); - expect(moveEffectPhase.hitCheck).toHaveReturnedWith(true); + expect(moveEffectPhase.hitCheck).toHaveReturnedWith([HitCheckResult.HIT, 1]); }); it("should guarantee double battle with any one LURE", async () => { diff --git a/test/abilities/shield_dust.test.ts b/test/abilities/shield_dust.test.ts index 0b96640a29f..4ab58e8c2a6 100644 --- a/test/abilities/shield_dust.test.ts +++ b/test/abilities/shield_dust.test.ts @@ -52,7 +52,7 @@ describe("Abilities - Shield Dust", () => { // Shield Dust negates secondary effect const phase = game.scene.getCurrentPhase() as MoveEffectPhase; - const move = phase.move.getMove(); + const move = phase.move; expect(move.id).toBe(Moves.AIR_SLASH); const chance = new NumberHolder(move.chance); diff --git a/test/abilities/super_luck.test.ts b/test/abilities/super_luck.test.ts index 9e0b6485734..fbcbd02bdd2 100644 --- a/test/abilities/super_luck.test.ts +++ b/test/abilities/super_luck.test.ts @@ -25,7 +25,6 @@ describe("Abilities - Super Luck", () => { .moveset([Moves.TACKLE]) .ability(Abilities.SUPER_LUCK) .battleStyle("single") - .disableCrits() .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH); diff --git a/test/abilities/tera_shell.test.ts b/test/abilities/tera_shell.test.ts index c387da30166..fdbcb14947d 100644 --- a/test/abilities/tera_shell.test.ts +++ b/test/abilities/tera_shell.test.ts @@ -2,7 +2,6 @@ import { BattlerIndex } from "#app/battle"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; -import { HitResult } from "#app/field/pokemon"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; @@ -87,13 +86,15 @@ describe("Abilities - Tera Shell", () => { await game.classicMode.startBattle([Species.CHARIZARD]); const playerPokemon = game.scene.getPlayerPokemon()!; - vi.spyOn(playerPokemon, "apply"); + const spy = vi.spyOn(playerPokemon, "getMoveEffectiveness"); game.move.select(Moves.SPLASH); await game.phaseInterceptor.to("BerryPhase", false); - expect(playerPokemon.apply).toHaveLastReturnedWith(HitResult.EFFECTIVE); + expect(spy).toHaveLastReturnedWith(1); expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp() - 40); + + spy.mockRestore(); }); it("should change the effectiveness of all strikes of a multi-strike move", async () => { @@ -102,7 +103,7 @@ describe("Abilities - Tera Shell", () => { await game.classicMode.startBattle([Species.SNORLAX]); const playerPokemon = game.scene.getPlayerPokemon()!; - vi.spyOn(playerPokemon, "apply"); + const spy = vi.spyOn(playerPokemon, "getMoveEffectiveness"); game.move.select(Moves.SPLASH); @@ -110,8 +111,9 @@ describe("Abilities - Tera Shell", () => { await game.move.forceHit(); for (let i = 0; i < 2; i++) { await game.phaseInterceptor.to("MoveEffectPhase"); - expect(playerPokemon.apply).toHaveLastReturnedWith(HitResult.NOT_VERY_EFFECTIVE); + expect(spy).toHaveLastReturnedWith(0.5); } - expect(playerPokemon.apply).toHaveReturnedTimes(2); + expect(spy).toHaveReturnedTimes(2); + spy.mockRestore(); }); }); diff --git a/test/battle/damage_calculation.test.ts b/test/battle/damage_calculation.test.ts index e8b3b65bd29..26772cbc4f0 100644 --- a/test/battle/damage_calculation.test.ts +++ b/test/battle/damage_calculation.test.ts @@ -47,7 +47,9 @@ describe("Battle Mechanics - Damage Calculation", () => { // expected base damage = [(2*level/5 + 2) * power * playerATK / enemyDEF / 50] + 2 // = 31.8666... - expect(enemyPokemon.getAttackDamage(playerPokemon, allMoves[Moves.TACKLE]).damage).toBeCloseTo(31); + expect(enemyPokemon.getAttackDamage({ source: playerPokemon, move: allMoves[Moves.TACKLE] }).damage).toBeCloseTo( + 31, + ); }); it("Attacks deal 1 damage at minimum", async () => { @@ -91,7 +93,7 @@ describe("Battle Mechanics - Damage Calculation", () => { const magikarp = game.scene.getPlayerPokemon()!; const dragonite = game.scene.getEnemyPokemon()!; - expect(dragonite.getAttackDamage(magikarp, allMoves[Moves.DRAGON_RAGE]).damage).toBe(40); + expect(dragonite.getAttackDamage({ source: magikarp, move: allMoves[Moves.DRAGON_RAGE] }).damage).toBe(40); }); it("One-hit KO moves ignore damage multipliers", async () => { @@ -102,7 +104,7 @@ describe("Battle Mechanics - Damage Calculation", () => { const magikarp = game.scene.getPlayerPokemon()!; const aggron = game.scene.getEnemyPokemon()!; - expect(aggron.getAttackDamage(magikarp, allMoves[Moves.FISSURE]).damage).toBe(aggron.hp); + expect(aggron.getAttackDamage({ source: magikarp, move: allMoves[Moves.FISSURE] }).damage).toBe(aggron.hp); }); it("When the user fails to use Jump Kick with Wonder Guard ability, the damage should be 1.", async () => { diff --git a/test/battlerTags/substitute.test.ts b/test/battlerTags/substitute.test.ts index fca3dc5ef7e..d2df5511c0a 100644 --- a/test/battlerTags/substitute.test.ts +++ b/test/battlerTags/substitute.test.ts @@ -1,5 +1,5 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import type { PokemonTurnData, TurnMove, PokemonMove } from "#app/field/pokemon"; +import type { PokemonTurnData, TurnMove } from "#app/field/pokemon"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; import type BattleScene from "#app/battle-scene"; @@ -186,12 +186,8 @@ describe("BattlerTag - SubstituteTag", () => { vi.spyOn(mockPokemon.scene as BattleScene, "triggerPokemonBattleAnim").mockReturnValue(true); vi.spyOn(mockPokemon.scene as BattleScene, "queueMessage").mockReturnValue(); - const pokemonMove = { - getMove: vi.fn().mockReturnValue(allMoves[Moves.TACKLE]) as PokemonMove["getMove"], - } as PokemonMove; - const moveEffectPhase = { - move: pokemonMove, + move: allMoves[Moves.TACKLE], getUserPokemon: vi.fn().mockReturnValue(undefined) as MoveEffectPhase["getUserPokemon"], } as MoveEffectPhase; diff --git a/test/items/dire_hit.test.ts b/test/items/dire_hit.test.ts index b409b2ac7cb..6e20bc723e5 100644 --- a/test/items/dire_hit.test.ts +++ b/test/items/dire_hit.test.ts @@ -36,8 +36,7 @@ describe("Items - Dire Hit", () => { .enemyMoveset(Moves.SPLASH) .moveset([Moves.POUND]) .startingHeldItems([{ name: "DIRE_HIT" }]) - .battleStyle("single") - .disableCrits(); + .battleStyle("single"); }, 20000); it("should raise CRIT stage by 1", async () => { diff --git a/test/items/leek.test.ts b/test/items/leek.test.ts index 7589b89bc15..9bde2c86339 100644 --- a/test/items/leek.test.ts +++ b/test/items/leek.test.ts @@ -28,7 +28,6 @@ describe("Items - Leek", () => { .enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]) .startingHeldItems([{ name: "LEEK" }]) .moveset([Moves.TACKLE]) - .disableCrits() .battleStyle("single"); }); diff --git a/test/items/scope_lens.test.ts b/test/items/scope_lens.test.ts index 4d2fd63f87b..f67966ea3c9 100644 --- a/test/items/scope_lens.test.ts +++ b/test/items/scope_lens.test.ts @@ -27,8 +27,7 @@ describe("Items - Scope Lens", () => { .enemyMoveset(Moves.SPLASH) .moveset([Moves.POUND]) .startingHeldItems([{ name: "SCOPE_LENS" }]) - .battleStyle("single") - .disableCrits(); + .battleStyle("single"); }, 20000); it("should raise CRIT stage by 1", async () => { diff --git a/test/moves/dig.test.ts b/test/moves/dig.test.ts index a53456ec083..80d51a5c2d5 100644 --- a/test/moves/dig.test.ts +++ b/test/moves/dig.test.ts @@ -97,14 +97,20 @@ describe("Moves - Dig", () => { const playerPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - const preDigEarthquakeDmg = playerPokemon.getAttackDamage(enemyPokemon, allMoves[Moves.EARTHQUAKE]).damage; + const preDigEarthquakeDmg = playerPokemon.getAttackDamage({ + source: enemyPokemon, + move: allMoves[Moves.EARTHQUAKE], + }).damage; game.move.select(Moves.DIG); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("MoveEffectPhase"); - const postDigEarthquakeDmg = playerPokemon.getAttackDamage(enemyPokemon, allMoves[Moves.EARTHQUAKE]).damage; + const postDigEarthquakeDmg = playerPokemon.getAttackDamage({ + source: enemyPokemon, + move: allMoves[Moves.EARTHQUAKE], + }).damage; // these hopefully get avoid rounding errors :shrug: expect(postDigEarthquakeDmg).toBeGreaterThanOrEqual(2 * preDigEarthquakeDmg); expect(postDigEarthquakeDmg).toBeLessThan(2 * (preDigEarthquakeDmg + 1)); diff --git a/test/moves/dynamax_cannon.test.ts b/test/moves/dynamax_cannon.test.ts index 94f07ae500f..84def8a821f 100644 --- a/test/moves/dynamax_cannon.test.ts +++ b/test/moves/dynamax_cannon.test.ts @@ -50,7 +50,7 @@ describe("Moves - Dynamax Cannon", () => { game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(dynamaxCannon.id); + expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100); }, 20000); @@ -62,7 +62,7 @@ describe("Moves - Dynamax Cannon", () => { game.move.select(dynamaxCannon.id); await game.phaseInterceptor.to(MoveEffectPhase, false); - expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(dynamaxCannon.id); + expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(dynamaxCannon.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(100); }, 20000); @@ -75,7 +75,7 @@ describe("Moves - Dynamax Cannon", () => { await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; - expect(phase.move.moveId).toBe(dynamaxCannon.id); + expect(phase.move.id).toBe(dynamaxCannon.id); // Force level cap to be 100 vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamageAnimPhase, false); @@ -90,7 +90,7 @@ describe("Moves - Dynamax Cannon", () => { await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; - expect(phase.move.moveId).toBe(dynamaxCannon.id); + expect(phase.move.id).toBe(dynamaxCannon.id); // Force level cap to be 100 vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamageAnimPhase, false); @@ -105,7 +105,7 @@ describe("Moves - Dynamax Cannon", () => { await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; - expect(phase.move.moveId).toBe(dynamaxCannon.id); + expect(phase.move.id).toBe(dynamaxCannon.id); // Force level cap to be 100 vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamageAnimPhase, false); @@ -120,7 +120,7 @@ describe("Moves - Dynamax Cannon", () => { await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; - expect(phase.move.moveId).toBe(dynamaxCannon.id); + expect(phase.move.id).toBe(dynamaxCannon.id); // Force level cap to be 100 vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamageAnimPhase, false); @@ -135,7 +135,7 @@ describe("Moves - Dynamax Cannon", () => { await game.phaseInterceptor.to(MoveEffectPhase, false); const phase = game.scene.getCurrentPhase() as MoveEffectPhase; - expect(phase.move.moveId).toBe(dynamaxCannon.id); + expect(phase.move.id).toBe(dynamaxCannon.id); // Force level cap to be 100 vi.spyOn(game.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamageAnimPhase, false); @@ -150,7 +150,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.moveId).toBe(dynamaxCannon.id); + expect((game.scene.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/fusion_flare_bolt.test.ts b/test/moves/fusion_flare_bolt.test.ts index 697ac57e739..ce6bb62d1d0 100644 --- a/test/moves/fusion_flare_bolt.test.ts +++ b/test/moves/fusion_flare_bolt.test.ts @@ -57,12 +57,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.moveId).toBe(fusionFlare.id); + expect((game.scene.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.moveId).toBe(fusionBolt.id); + expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); @@ -77,12 +77,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.moveId).toBe(fusionBolt.id); + expect((game.scene.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.moveId).toBe(fusionFlare.id); + expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); @@ -97,7 +97,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.moveId).toBe(fusionFlare.id); + expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100); @@ -107,7 +107,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.moveId).toBe(fusionBolt.id); + expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); @@ -123,7 +123,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.moveId).toBe(fusionFlare.id); + expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100); @@ -132,7 +132,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.moveId).toBe(fusionBolt.id); + expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); }, 20000); @@ -147,12 +147,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.moveId).toBe(fusionBolt.id); + expect((game.scene.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.moveId).toBe(fusionFlare.id); + expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); @@ -191,22 +191,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.moveId).toBe(fusionBolt.id); + expect((game.scene.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.moveId).toBe(fusionFlare.id); + expect((game.scene.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.moveId).toBe(fusionBolt.id); + expect((game.scene.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.moveId).toBe(fusionFlare.id); + expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); await game.phaseInterceptor.to(DamageAnimPhase, false); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); @@ -245,22 +245,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.moveId).toBe(fusionBolt.id); + expect((game.scene.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.moveId).toBe(fusionFlare.id); + expect((game.scene.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.moveId).toBe(fusionBolt.id); + expect((game.scene.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.moveId).toBe(fusionFlare.id); + expect((game.scene.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/spectral_thief.test.ts b/test/moves/spectral_thief.test.ts index 2e52b118a74..2654ab1ad8d 100644 --- a/test/moves/spectral_thief.test.ts +++ b/test/moves/spectral_thief.test.ts @@ -71,7 +71,7 @@ describe("Moves - Spectral Thief", () => { const player = game.scene.getPlayerPokemon()!; const enemy = game.scene.getEnemyPokemon()!; const moveToCheck = allMoves[Moves.SPECTRAL_THIEF]; - const dmgBefore = enemy.getAttackDamage(player, moveToCheck, false, false, false, false).damage; + const dmgBefore = enemy.getAttackDamage({ source: player, move: moveToCheck }).damage; enemy.setStatStage(Stat.ATK, 6); @@ -80,7 +80,7 @@ describe("Moves - Spectral Thief", () => { game.move.select(Moves.SPECTRAL_THIEF); await game.phaseInterceptor.to(TurnEndPhase); - expect(dmgBefore).toBeLessThan(enemy.getAttackDamage(player, moveToCheck, false, false, false, false).damage); + expect(dmgBefore).toBeLessThan(enemy.getAttackDamage({ source: player, move: moveToCheck }).damage); }); it("should steal stat stages as a negative value with Contrary.", async () => { diff --git a/test/moves/tera_blast.test.ts b/test/moves/tera_blast.test.ts index 5dc3a914a2e..8817f12b8cf 100644 --- a/test/moves/tera_blast.test.ts +++ b/test/moves/tera_blast.test.ts @@ -4,7 +4,6 @@ import { allMoves, TeraMoveCategoryAttr } from "#app/data/moves/move"; import type Move from "#app/data/moves/move"; import { PokemonType } from "#enums/pokemon-type"; import { Abilities } from "#app/enums/abilities"; -import { HitResult } from "#app/field/pokemon"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; @@ -49,9 +48,9 @@ describe("Moves - Tera Blast", () => { it("changes type to match user's tera type", async () => { game.override.enemySpecies(Species.FURRET); - await game.startBattle(); + await game.classicMode.startBattle(); const enemyPokemon = game.scene.getEnemyPokemon()!; - vi.spyOn(enemyPokemon, "apply"); + const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness"); const playerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.teraType = PokemonType.FIGHTING; @@ -61,11 +60,11 @@ describe("Moves - Tera Blast", () => { await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("MoveEffectPhase"); - expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.SUPER_EFFECTIVE); + expect(spy).toHaveReturnedWith(2); }, 20000); it("increases power if user is Stellar tera type", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.teraType = PokemonType.STELLAR; @@ -79,25 +78,25 @@ describe("Moves - Tera Blast", () => { }, 20000); it("is super effective against terastallized targets if user is Stellar tera type", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.teraType = PokemonType.STELLAR; playerPokemon.isTerastallized = true; const enemyPokemon = game.scene.getEnemyPokemon()!; - vi.spyOn(enemyPokemon, "apply"); + const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness"); enemyPokemon.isTerastallized = true; game.move.select(Moves.TERA_BLAST); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.phaseInterceptor.to("MoveEffectPhase"); - expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.SUPER_EFFECTIVE); + expect(spy).toHaveReturnedWith(2); }); it("uses the higher ATK for damage calculation", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.stats[Stat.ATK] = 100; @@ -112,7 +111,7 @@ describe("Moves - Tera Blast", () => { }); it("uses the higher SPATK for damage calculation", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.stats[Stat.ATK] = 1; @@ -127,7 +126,7 @@ describe("Moves - Tera Blast", () => { it("should stay as a special move if ATK turns lower than SPATK mid-turn", async () => { game.override.enemyMoveset([Moves.CHARM]); - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.stats[Stat.ATK] = 51; @@ -145,7 +144,7 @@ describe("Moves - Tera Blast", () => { game.override .startingHeldItems([{ name: "SPECIES_STAT_BOOSTER", type: "THICK_CLUB" }]) .starterSpecies(Species.CUBONE); - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; @@ -163,7 +162,7 @@ describe("Moves - Tera Blast", () => { it("does not change its move category from stat changes due to abilities", async () => { game.override.ability(Abilities.HUGE_POWER); - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.stats[Stat.ATK] = 50; @@ -178,7 +177,7 @@ describe("Moves - Tera Blast", () => { }); it("causes stat drops if user is Stellar tera type", async () => { - await game.startBattle(); + await game.classicMode.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.teraType = PokemonType.STELLAR; diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index edade109966..0f3d75c6268 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -18,29 +18,29 @@ import { vi } from "vitest"; */ export class MoveHelper extends GameManagerHelper { /** - * Intercepts {@linkcode MoveEffectPhase} and mocks the - * {@linkcode MoveEffectPhase.hitCheck | hitCheck}'s return value to `true`. - * Used to force a move to hit. + * Intercepts {@linkcode MoveEffectPhase} and mocks the phase's move's + * accuracy to -1, guaranteeing a hit. */ public async forceHit(): Promise { await this.game.phaseInterceptor.to(MoveEffectPhase, false); - vi.spyOn(this.game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true); + const moveEffectPhase = this.game.scene.getCurrentPhase() as MoveEffectPhase; + vi.spyOn(moveEffectPhase.move, "calculateBattleAccuracy").mockReturnValue(-1); } /** - * Intercepts {@linkcode MoveEffectPhase} and mocks the - * {@linkcode MoveEffectPhase.hitCheck | hitCheck}'s return value to `false`. - * Used to force a move to miss. + * Intercepts {@linkcode MoveEffectPhase} and mocks the phase's move's accuracy + * to 0, guaranteeing a miss. * @param firstTargetOnly - Whether the move should force miss on the first target only, in the case of multi-target moves. */ public async forceMiss(firstTargetOnly = false): Promise { await this.game.phaseInterceptor.to(MoveEffectPhase, false); - const hitCheck = vi.spyOn(this.game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck"); + const moveEffectPhase = this.game.scene.getCurrentPhase() as MoveEffectPhase; + const accuracy = vi.spyOn(moveEffectPhase.move, "calculateBattleAccuracy"); if (firstTargetOnly) { - hitCheck.mockReturnValueOnce(false); + accuracy.mockReturnValueOnce(0); } else { - hitCheck.mockReturnValue(false); + accuracy.mockReturnValue(0); } } From b848777880b8c05dc0f2849bf25e97f60135c04e Mon Sep 17 00:00:00 2001 From: Jimmybald1 <122436263+Jimmybald1@users.noreply.github.com> Date: Wed, 23 Apr 2025 19:54:43 +0200 Subject: [PATCH 47/52] [Bug][Misc] Moved `SelectBiomePhase` in front of `NewBattlePhase` (#5694) * Moved SelectBiomePhase in front of NewBattlePhase * disguise test now has to go to QuietFormChangePhase --------- Co-authored-by: Jimmybald1 <147992650+IBBCalc@users.noreply.github.com> --- src/battle-scene.ts | 19 +++++++++++-------- src/data/abilities/ability.ts | 6 ++++++ src/data/moves/move.ts | 6 ++++++ src/phases/attempt-run-phase.ts | 6 ++++++ src/phases/mystery-encounter-phases.ts | 5 +++++ src/phases/select-biome-phase.ts | 19 ++++++++++--------- src/phases/switch-biome-phase.ts | 4 ++++ src/phases/victory-phase.ts | 6 ++++++ test/abilities/disguise.test.ts | 2 +- 9 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index ecaffc5ed07..2ff5d718ede 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -151,7 +151,6 @@ 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 { SelectBiomePhase } from "#app/phases/select-biome-phase"; import { ShowTrainerPhase } from "#app/phases/show-trainer-phase"; import { SummonPhase } from "#app/phases/summon-phase"; import { SwitchPhase } from "#app/phases/switch-phase"; @@ -1298,6 +1297,16 @@ export default class BattleScene extends SceneBase { return Math.max(doubleChance.value, 1); } + isNewBiome(currentBattle = this.currentBattle) { + const isWaveIndexMultipleOfTen = !(currentBattle.waveIndex % 10); + const isEndlessOrDaily = this.gameMode.hasShortBiomes || this.gameMode.isDaily; + const isEndlessFifthWave = this.gameMode.hasShortBiomes && currentBattle.waveIndex % 5 === 0; + const isWaveIndexMultipleOfFiftyMinusOne = currentBattle.waveIndex % 50 === 49; + const isNewBiome = + isWaveIndexMultipleOfTen || isEndlessFifthWave || (isEndlessOrDaily && isWaveIndexMultipleOfFiftyMinusOne); + return isNewBiome; + } + // TODO: ...this never actually returns `null`, right? newBattle( waveIndex?: number, @@ -1461,12 +1470,7 @@ export default class BattleScene extends SceneBase { } if (!waveIndex && lastBattle) { - const isWaveIndexMultipleOfTen = !(lastBattle.waveIndex % 10); - const isEndlessOrDaily = this.gameMode.hasShortBiomes || this.gameMode.isDaily; - const isEndlessFifthWave = this.gameMode.hasShortBiomes && lastBattle.waveIndex % 5 === 0; - const isWaveIndexMultipleOfFiftyMinusOne = lastBattle.waveIndex % 50 === 49; - const isNewBiome = - isWaveIndexMultipleOfTen || isEndlessFifthWave || (isEndlessOrDaily && isWaveIndexMultipleOfFiftyMinusOne); + const isNewBiome = this.isNewBiome(lastBattle); const resetArenaState = isNewBiome || [BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(this.currentBattle.battleType) || @@ -1515,7 +1519,6 @@ export default class BattleScene extends SceneBase { if (!this.gameMode.hasRandomBiomes && !isNewBiome) { this.pushPhase(new NextEncounterPhase()); } else { - this.pushPhase(new SelectBiomePhase()); this.pushPhase(new NewBiomeEncounterPhase()); const newMaxExpLevel = this.getMaxExpLevel(); diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index 53d024ac655..d8b648ebe82 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -72,6 +72,7 @@ import type { AbAttrCondition, PokemonDefendCondition, PokemonStatStageChangeCon 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"; export class BlockRecoilDamageAttr extends AbAttr { constructor() { @@ -5483,6 +5484,11 @@ class ForceSwitchOutHelper { if (switchOutTarget.hp) { globalScene.pushPhase(new BattleEndPhase(false)); + + if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { + globalScene.pushPhase(new SelectBiomePhase()); + } + globalScene.pushPhase(new NewBattlePhase()); } } diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 35d98f6f781..5d57bb6dc49 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -123,6 +123,7 @@ import { MoveEffectTrigger } from "#enums/MoveEffectTrigger"; import { MultiHitType } from "#enums/MultiHitType"; import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves } from "./invalid-moves"; import { TrainerVariant } from "#app/field/trainer"; +import { SelectBiomePhase } from "#app/phases/select-biome-phase"; type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean; type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean; @@ -6332,6 +6333,11 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { if (!allyPokemon?.isActive(true) && switchOutTarget.hp) { globalScene.pushPhase(new BattleEndPhase(false)); + + if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { + globalScene.pushPhase(new SelectBiomePhase()); + } + globalScene.pushPhase(new NewBattlePhase()); } } diff --git a/src/phases/attempt-run-phase.ts b/src/phases/attempt-run-phase.ts index eed5c3c522e..274d3c40576 100644 --- a/src/phases/attempt-run-phase.ts +++ b/src/phases/attempt-run-phase.ts @@ -14,6 +14,7 @@ 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 { /** For testing purposes: this is to force the pokemon to fail and escape */ @@ -59,6 +60,11 @@ export class AttemptRunPhase extends PokemonPhase { }); globalScene.pushPhase(new BattleEndPhase(false)); + + if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { + globalScene.pushPhase(new SelectBiomePhase()); + } + globalScene.pushPhase(new NewBattlePhase()); } else { playerPokemon.turnData.failedRunAway = true; diff --git a/src/phases/mystery-encounter-phases.ts b/src/phases/mystery-encounter-phases.ts index 100be47e4e9..011dd26db92 100644 --- a/src/phases/mystery-encounter-phases.ts +++ b/src/phases/mystery-encounter-phases.ts @@ -27,6 +27,7 @@ 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): @@ -612,6 +613,10 @@ export class PostMysteryEncounterPhase extends Phase { */ continueEncounter() { const endPhase = () => { + if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { + globalScene.pushPhase(new SelectBiomePhase()); + } + globalScene.pushPhase(new NewBattlePhase()); this.end(); }; diff --git a/src/phases/select-biome-phase.ts b/src/phases/select-biome-phase.ts index 0ea2841a2d3..4811c4e6b8f 100644 --- a/src/phases/select-biome-phase.ts +++ b/src/phases/select-biome-phase.ts @@ -14,9 +14,10 @@ export class SelectBiomePhase extends BattlePhase { super.start(); const currentBiome = globalScene.arena.biomeType; + const nextWaveIndex = globalScene.currentBattle.waveIndex + 1; const setNextBiome = (nextBiome: Biome) => { - if (globalScene.currentBattle.waveIndex % 10 === 1) { + if (nextWaveIndex % 10 === 1) { globalScene.applyModifiers(MoneyInterestModifier, true); globalScene.unshiftPhase(new PartyHealPhase(false)); } @@ -25,13 +26,13 @@ export class SelectBiomePhase extends BattlePhase { }; if ( - (globalScene.gameMode.isClassic && globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex + 9)) || - (globalScene.gameMode.isDaily && globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) || - (globalScene.gameMode.hasShortBiomes && !(globalScene.currentBattle.waveIndex % 50)) + (globalScene.gameMode.isClassic && globalScene.gameMode.isWaveFinal(nextWaveIndex + 9)) || + (globalScene.gameMode.isDaily && globalScene.gameMode.isWaveFinal(nextWaveIndex)) || + (globalScene.gameMode.hasShortBiomes && !(nextWaveIndex % 50)) ) { setNextBiome(Biome.END); } else if (globalScene.gameMode.hasRandomBiomes) { - setNextBiome(this.generateNextBiome()); + setNextBiome(this.generateNextBiome(nextWaveIndex)); } else if (Array.isArray(biomeLinks[currentBiome])) { const biomes: Biome[] = (biomeLinks[currentBiome] as (Biome | [Biome, number])[]) .filter(b => !Array.isArray(b) || !randSeedInt(b[1])) @@ -59,14 +60,14 @@ export class SelectBiomePhase extends BattlePhase { } else if (biomeLinks.hasOwnProperty(currentBiome)) { setNextBiome(biomeLinks[currentBiome] as Biome); } else { - setNextBiome(this.generateNextBiome()); + setNextBiome(this.generateNextBiome(nextWaveIndex)); } } - generateNextBiome(): Biome { - if (!(globalScene.currentBattle.waveIndex % 50)) { + generateNextBiome(waveIndex: number): Biome { + if (!(waveIndex % 50)) { return Biome.END; } - return globalScene.generateRandomBiome(globalScene.currentBattle.waveIndex); + return globalScene.generateRandomBiome(waveIndex); } } diff --git a/src/phases/switch-biome-phase.ts b/src/phases/switch-biome-phase.ts index 2dd2a642f43..f708830318e 100644 --- a/src/phases/switch-biome-phase.ts +++ b/src/phases/switch-biome-phase.ts @@ -19,6 +19,10 @@ export class SwitchBiomePhase extends BattlePhase { return this.end(); } + // Before switching biomes, make sure to set the last encounter for other phases that need it too. + globalScene.lastEnemyTrainer = globalScene.currentBattle?.trainer ?? null; + globalScene.lastMysteryEncounter = globalScene.currentBattle?.mysteryEncounter; + globalScene.tweens.add({ targets: [globalScene.arenaEnemy, globalScene.lastEnemyTrainer], x: "+=300", diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index 17b29f654e2..6e1837a4749 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -15,6 +15,7 @@ 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 { /** If true, indicates that the phase is intended for EXP purposes only, and not to continue a battle to next phase */ @@ -111,6 +112,11 @@ export class VictoryPhase extends PokemonPhase { globalScene.pushPhase(new AddEnemyBuffModifierPhase()); } } + + if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { + globalScene.pushPhase(new SelectBiomePhase()); + } + globalScene.pushPhase(new NewBattlePhase()); } else { globalScene.currentBattle.battleType = BattleType.CLEAR; diff --git a/test/abilities/disguise.test.ts b/test/abilities/disguise.test.ts index aeaf8ea2363..0e62b8ad448 100644 --- a/test/abilities/disguise.test.ts +++ b/test/abilities/disguise.test.ts @@ -186,7 +186,7 @@ describe("Abilities - Disguise", () => { await game.toNextTurn(); game.move.select(Moves.SPLASH); await game.doKillOpponents(); - await game.phaseInterceptor.to("PartyHealPhase"); + await game.phaseInterceptor.to("QuietFormChangePhase"); expect(mimikyu1.formIndex).toBe(disguisedForm); }); From 389ad6ceb627ed11579e36009a4164ca7f037e52 Mon Sep 17 00:00:00 2001 From: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Date: Wed, 23 Apr 2025 13:10:01 -0500 Subject: [PATCH 48/52] [Tests][UI/UX] Add automated tests for the pokedex (#5637) * Remove unneeded fields from src/ui/filter-text.ts * Add setOverlayMode to phaseInterceptor * initialize pokemon starters before running tests * Add getWrappedText to mockText * Add initial pokedex test * Add test for wrapping cursor in pokedex view * Make pokedex use getPassiveAbility instead of checking passive map Allows for tests to mock passives * Add test for filtering double ability combinations * Add test for filtering by types * Mark failing test as TODO * Apply suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: Madmadness65 <59298170+Madmadness65@users.noreply.github.com> * Use ts-expect-error instead of ts-ignore in comments Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Add save for pokedex tests * Add test for filtering by cost reduction * Add test for filtering by shiny * Add tests for filtering by cost reductions * Fix typo in test name * Update test/ui/pokedex.test.ts Co-authored-by: Wlowscha <54003515+Wlowscha@users.noreply.github.com> * Update Mode import in pokedex test * Replace reference to Mode with UiMode --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: Madmadness65 <59298170+Madmadness65@users.noreply.github.com> Co-authored-by: Wlowscha <54003515+Wlowscha@users.noreply.github.com> --- src/ui/filter-text.ts | 15 +- src/ui/pokedex-ui-handler.ts | 20 +- test/testUtils/gameWrapper.ts | 6 + .../mocks/mocksContainer/mockText.ts | 9 + test/testUtils/phaseInterceptor.ts | 15 + test/testUtils/saves/data_pokedex_tests.prsv | 1 + test/testUtils/testFileInitialization.ts | 5 +- test/ui/pokedex.test.ts | 492 ++++++++++++++++++ 8 files changed, 545 insertions(+), 18 deletions(-) create mode 100644 test/testUtils/saves/data_pokedex_tests.prsv create mode 100644 test/ui/pokedex.test.ts diff --git a/src/ui/filter-text.ts b/src/ui/filter-text.ts index 8b13b76db31..7e27a806478 100644 --- a/src/ui/filter-text.ts +++ b/src/ui/filter-text.ts @@ -20,7 +20,6 @@ export class FilterText extends Phaser.GameObjects.Container { private window: Phaser.GameObjects.NineSlice; private labels: Phaser.GameObjects.Text[] = []; private selections: Phaser.GameObjects.Text[] = []; - private selectionStrings: string[] = []; private rows: FilterTextRow[] = []; public cursorObj: Phaser.GameObjects.Image; public numFilters = 0; @@ -112,8 +111,6 @@ export class FilterText extends Phaser.GameObjects.Container { this.selections.push(filterTypesSelection); this.add(filterTypesSelection); - this.selectionStrings.push(""); - this.calcFilterPositions(); this.numFilters++; @@ -122,7 +119,6 @@ export class FilterText extends Phaser.GameObjects.Container { resetSelection(index: number): void { this.selections[index].setText(this.defaultText); - this.selectionStrings[index] = ""; this.onChange(); } @@ -204,6 +200,17 @@ export class FilterText extends Phaser.GameObjects.Container { return this.selections[row].getWrappedText()[0]; } + /** + * Forcibly set the selection text for a specific filter row and then call the `onChange` function + * + * @param row - The filter row to set the text for + * @param value - The text to set for the filter row + */ + setValue(row: FilterTextRow, value: string) { + this.selections[row].setText(value); + this.onChange(); + } + /** * Find the nearest filter to the provided container on the y-axis * @param container the StarterContainer to compare position against diff --git a/src/ui/pokedex-ui-handler.ts b/src/ui/pokedex-ui-handler.ts index e9726031bf5..b1d0945de07 100644 --- a/src/ui/pokedex-ui-handler.ts +++ b/src/ui/pokedex-ui-handler.ts @@ -37,10 +37,9 @@ import { addWindow } from "./ui-theme"; import type { OptionSelectConfig } from "./abstact-option-select-ui-handler"; import { FilterText, FilterTextRow } from "./filter-text"; import { allAbilities } from "#app/data/data-lists"; -import { starterPassiveAbilities } from "#app/data/balance/passives"; import { allMoves } from "#app/data/moves/move"; import { speciesTmMoves } from "#app/data/balance/tms"; -import { pokemonPrevolutions, pokemonStarters } from "#app/data/balance/pokemon-evolutions"; +import { pokemonStarters } from "#app/data/balance/pokemon-evolutions"; import { Biome } from "#enums/biome"; import { globalScene } from "#app/global-scene"; @@ -174,7 +173,6 @@ export default class PokedexUiHandler extends MessageUiHandler { private scrollCursor: number; private oldCursor = -1; - private allSpecies: PokemonSpecies[] = []; private lastSpecies: PokemonSpecies; private speciesLoaded: Map = new Map(); private pokerusSpecies: PokemonSpecies[] = []; @@ -493,12 +491,11 @@ export default class PokedexUiHandler extends MessageUiHandler { for (const species of allSpecies) { this.speciesLoaded.set(species.speciesId, false); - this.allSpecies.push(species); } // Here code to declare 81 containers for (let i = 0; i < 81; i++) { - const pokemonContainer = new PokedexMonContainer(this.allSpecies[i]).setVisible(false); + const pokemonContainer = new PokedexMonContainer(allSpecies[i]).setVisible(false); const pos = calcStarterPosition(i); pokemonContainer.setPosition(pos.x, pos.y); this.iconAnimHandler.addOrUpdate(pokemonContainer.icon, PokemonIconAnimMode.NONE); @@ -1342,7 +1339,7 @@ export default class PokedexUiHandler extends MessageUiHandler { this.filteredPokemonData = []; - this.allSpecies.forEach(species => { + allSpecies.forEach(species => { const starterId = this.getStarterSpeciesId(species.speciesId); const currentDexAttr = this.getCurrentDexProps(species.speciesId); @@ -1412,12 +1409,11 @@ export default class PokedexUiHandler extends MessageUiHandler { // Ability filter const abilities = [species.ability1, species.ability2, species.abilityHidden].map(a => allAbilities[a].name); - const passiveId = starterPassiveAbilities.hasOwnProperty(species.speciesId) - ? species.speciesId - : starterPassiveAbilities.hasOwnProperty(starterId) - ? starterId - : pokemonPrevolutions[starterId]; - const passives = starterPassiveAbilities[passiveId]; + // get the passive ability for the species + const passives = [species.getPassiveAbility()]; + for (const form of species.forms) { + passives.push(form.getPassiveAbility()); + } const selectedAbility1 = this.filterText.getValue(FilterTextRow.ABILITY_1); const fitsFormAbility1 = species.forms.some(form => diff --git a/test/testUtils/gameWrapper.ts b/test/testUtils/gameWrapper.ts index 050e9f13257..9264b68d421 100644 --- a/test/testUtils/gameWrapper.ts +++ b/test/testUtils/gameWrapper.ts @@ -21,6 +21,8 @@ import KeyboardPlugin = Phaser.Input.Keyboard.KeyboardPlugin; import GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin; import EventEmitter = Phaser.Events.EventEmitter; import UpdateList = Phaser.GameObjects.UpdateList; +import { PokedexMonContainer } from "#app/ui/pokedex-mon-container"; +import MockContainer from "./mocks/mocksContainer/mockContainer"; // biome-ignore lint/style/noNamespaceImport: Necessary in order to mock the var import * as bypassLoginModule from "#app/global-vars/bypass-login"; @@ -61,6 +63,10 @@ export default class GameWrapper { } }; BattleScene.prototype.addPokemonIcon = () => new Phaser.GameObjects.Container(this.scene); + + // Pokedex container is not actually mocking container, but the sprites they contain are mocked. + // We need to mock the remove function to not throw an error when removing a sprite. + PokedexMonContainer.prototype.remove = MockContainer.prototype.remove; } setScene(scene: BattleScene) { diff --git a/test/testUtils/mocks/mocksContainer/mockText.ts b/test/testUtils/mocks/mocksContainer/mockText.ts index 552f8ff3ff8..1f3f0ad792f 100644 --- a/test/testUtils/mocks/mocksContainer/mockText.ts +++ b/test/testUtils/mocks/mocksContainer/mockText.ts @@ -308,5 +308,14 @@ export default class MockText implements MockGameObject { return this.list; } + /** + * Runs the word wrap algorithm on the text, then returns an array of the lines + */ + getWrappedText() { + // Returns the wrapped text. + // return this.phaserText.getWrappedText(); + return this.runWordWrap(this.text).split("\n"); + } + on(_event: string | symbol, _fn: Function, _context?: any) {} } diff --git a/test/testUtils/phaseInterceptor.ts b/test/testUtils/phaseInterceptor.ts index 3d56c513c00..b1d76ecd4a6 100644 --- a/test/testUtils/phaseInterceptor.ts +++ b/test/testUtils/phaseInterceptor.ts @@ -205,6 +205,7 @@ export default class PhaseInterceptor { private phaseFrom; private inProgress; private originalSetMode; + private originalSetOverlayMode; private originalSuperEnd; /** @@ -442,6 +443,7 @@ export default class PhaseInterceptor { */ initPhases() { this.originalSetMode = UI.prototype.setMode; + this.originalSetOverlayMode = UI.prototype.setOverlayMode; this.originalSuperEnd = Phase.prototype.end; UI.prototype.setMode = (mode, ...args) => this.setMode.call(this, mode, ...args); Phase.prototype.end = () => this.superEndPhase.call(this); @@ -508,6 +510,18 @@ export default class PhaseInterceptor { return ret; } + /** + * mock to set overlay mode + * @param mode - The {@linkcode Mode} to set. + * @param args - Additional arguments to pass to the original method. + */ + setOverlayMode(mode: UiMode, ...args: unknown[]): Promise { + const instance = this.scene.ui; + console.log("setOverlayMode", `${UiMode[mode]} (=${mode})`, args); + const ret = this.originalSetOverlayMode.apply(instance, [mode, ...args]); + return ret; + } + /** * Method to start the prompt handler. */ @@ -572,6 +586,7 @@ export default class PhaseInterceptor { phase.prototype.start = this.phases[phase.name].start; } UI.prototype.setMode = this.originalSetMode; + UI.prototype.setOverlayMode = this.originalSetOverlayMode; Phase.prototype.end = this.originalSuperEnd; clearInterval(this.promptInterval); clearInterval(this.interval); diff --git a/test/testUtils/saves/data_pokedex_tests.prsv b/test/testUtils/saves/data_pokedex_tests.prsv new file mode 100644 index 00000000000..c55241760c4 --- /dev/null +++ b/test/testUtils/saves/data_pokedex_tests.prsv @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/test/testUtils/testFileInitialization.ts b/test/testUtils/testFileInitialization.ts index 414e34e024b..15635289e6f 100644 --- a/test/testUtils/testFileInitialization.ts +++ b/test/testUtils/testFileInitialization.ts @@ -3,7 +3,7 @@ import { initLoggedInUser } from "#app/account"; import { initAbilities } from "#app/data/abilities/ability"; import { initBiomes } from "#app/data/balance/biomes"; import { initEggMoves } from "#app/data/balance/egg-moves"; -import { initPokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; +import { initPokemonPrevolutions, initPokemonStarters } from "#app/data/balance/pokemon-evolutions"; import { initMoves } from "#app/data/moves/move"; import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters"; import { initPokemonForms } from "#app/data/pokemon-forms"; @@ -85,7 +85,6 @@ export function initTestFile() { HTMLCanvasElement.prototype.getContext = () => mockContext; // Initialize all of these things if and only if they have not been initialized yet - // initSpecies(); if (!wasInitialized) { wasInitialized = true; initI18n(); @@ -101,6 +100,8 @@ export function initTestFile() { initAbilities(); initLoggedInUser(); initMysteryEncounters(); + // init the pokemon starters for the pokedex + initPokemonStarters(); } manageListeners(); diff --git a/test/ui/pokedex.test.ts b/test/ui/pokedex.test.ts new file mode 100644 index 00000000000..41fb5e47f8c --- /dev/null +++ b/test/ui/pokedex.test.ts @@ -0,0 +1,492 @@ +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, type MockInstance, vi } from "vitest"; +import PokedexUiHandler from "#app/ui/pokedex-ui-handler"; +import { FilterTextRow } from "#app/ui/filter-text"; +import { allAbilities } from "#app/data/data-lists"; +import { Abilities } from "#enums/abilities"; +import { Species } from "#enums/species"; +import { allSpecies, getPokemonSpecies, type PokemonForm } from "#app/data/pokemon-species"; +import { Button } from "#enums/buttons"; +import { DropDownColumn } from "#app/ui/filter-bar"; +import type PokemonSpecies from "#app/data/pokemon-species"; +import { PokemonType } from "#enums/pokemon-type"; +import { UiMode } from "#enums/ui-mode"; + +/* +Information for the `data_pokedex_tests.psrv`: + +Caterpie - Shiny 0 +Rattata - Shiny 1 +Ekans - Shiny 2 + +Chikorita has enough candies to unlock passive +Cyndaquil has first cost reduction unlocked, enough candies to buy the second +Totodile has first cost reduction unlocked, not enough candies to buy the second +Treecko has both cost reduction unlocked +Torchic has enough candies to do anything +Mudkip has passive unlocked +Turtwig has enough candies to purchase an egg +*/ + +/** + * Return all permutations of elements from an array + */ +function permutations(array: T[], length: number): T[][] { + if (length === 0) { + return [[]]; + } + return array.flatMap((item, index) => + permutations([...array.slice(0, index), ...array.slice(index + 1)], length - 1).map(perm => [item, ...perm]), + ); +} + +describe("UI - Pokedex", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const mocks: MockInstance[] = []; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + while (mocks.length > 0) { + mocks.pop()?.mockRestore(); + } + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + }); + + /** + * Run the game to open the pokedex UI. + * @returns The handler for the pokedex UI. + */ + async function runToOpenPokedex(): Promise { + // Open the pokedex UI. + await game.runToTitle(); + + await game.phaseInterceptor.setOverlayMode(UiMode.POKEDEX); + + // Get the handler for the current UI. + const handler = game.scene.ui.getHandler(); + expect(handler).toBeInstanceOf(PokedexUiHandler); + + return handler as PokedexUiHandler; + } + + /** + * Compute a set of pokemon that have a specific ability in allAbilities + * @param ability - The ability to filter for + */ + function getSpeciesWithAbility(ability: Abilities): Set { + const speciesSet = new Set(); + for (const pkmn of allSpecies) { + if ( + [pkmn.ability1, pkmn.ability2, pkmn.getPassiveAbility(), pkmn.abilityHidden].includes(ability) || + pkmn.forms.some(form => + [form.ability1, form.ability2, form.abilityHidden, form.getPassiveAbility()].includes(ability), + ) + ) { + speciesSet.add(pkmn.speciesId); + } + } + return speciesSet; + } + + /** + * Compute a set of pokemon that have one of the specified type(s) + * + * Includes all forms of the pokemon + * @param types - The types to filter for + */ + function getSpeciesWithType(...types: PokemonType[]): Set { + const speciesSet = new Set(); + const tySet = new Set(types); + + // get the pokemon and its forms + outer: for (const pkmn of allSpecies) { + // @ts-expect-error We know that type2 might be null. + if (tySet.has(pkmn.type1) || tySet.has(pkmn.type2)) { + speciesSet.add(pkmn.speciesId); + continue; + } + for (const form of pkmn.forms) { + // @ts-expect-error We know that type2 might be null. + if (tySet.has(form.type1) || tySet.has(form.type2)) { + speciesSet.add(pkmn.speciesId); + continue outer; + } + } + } + return speciesSet; + } + + /** + * Create mocks for the abilities of a species. + * This is used to set the abilities of a species to a specific value. + * All abilities are optional. Not providing one will set it to NONE. + * + * This will override the ability of the pokemon species only, unless set forms is true + * + * @param species - The species to set the abilities for + * @param ability - The ability to set for the first ability + * @param ability2 - The ability to set for the second ability + * @param hidden - The ability to set for the hidden ability + * @param passive - The ability to set for the passive ability + * @param setForms - Whether to also overwrite the abilities for each of the species' forms (defaults to `true`) + */ + function createAbilityMocks( + species: Species, + { + ability = Abilities.NONE, + ability2 = Abilities.NONE, + hidden = Abilities.NONE, + passive = Abilities.NONE, + setForms = true, + }: { + ability?: Abilities; + ability2?: Abilities; + hidden?: Abilities; + passive?: Abilities; + setForms?: boolean; + }, + ) { + const pokemon = getPokemonSpecies(species); + const checks: [PokemonSpecies | PokemonForm] = [pokemon]; + if (setForms) { + checks.push(...pokemon.forms); + } + for (const p of checks) { + mocks.push(vi.spyOn(p, "ability1", "get").mockReturnValue(ability)); + mocks.push(vi.spyOn(p, "ability2", "get").mockReturnValue(ability2)); + mocks.push(vi.spyOn(p, "abilityHidden", "get").mockReturnValue(hidden)); + mocks.push(vi.spyOn(p, "getPassiveAbility").mockReturnValue(passive)); + } + } + + /*************************** + * Tests for Filters * + ***************************/ + + it("should filter to show only the pokemon with an ability when filtering by ability", async () => { + // await game.importData("test/testUtils/saves/everything.prsv"); + const pokedexHandler = await runToOpenPokedex(); + + // Get name of overgrow + const overgrow = allAbilities[Abilities.OVERGROW].name; + + // @ts-expect-error `filterText` is private + pokedexHandler.filterText.setValue(FilterTextRow.ABILITY_1, overgrow); + + // filter all species to be the pokemon that have overgrow + const overgrowSpecies = getSpeciesWithAbility(Abilities.OVERGROW); + // @ts-expect-error - `filteredPokemonData` is private + const filteredSpecies = new Set(pokedexHandler.filteredPokemonData.map(pokemon => pokemon.species.speciesId)); + + expect(filteredSpecies).toEqual(overgrowSpecies); + }); + + it("should filter to show only pokemon with ability and passive when filtering by 2 abilities", async () => { + // Setup mocks for the ability and passive combinations + const whitelist: Species[] = []; + const blacklist: Species[] = []; + + const filter_ab1 = Abilities.OVERGROW; + const filter_ab2 = Abilities.ADAPTABILITY; + const ab1_instance = allAbilities[filter_ab1]; + const ab2_instance = allAbilities[filter_ab2]; + + // Create a species with passive set and each "ability" field + const baseObj = { + ability: Abilities.BALL_FETCH, + ability2: Abilities.NONE, + hidden: Abilities.BLAZE, + passive: Abilities.TORRENT, + }; + + // Mock pokemon to have the exhaustive combination of the two selected abilities + const attrs: (keyof typeof baseObj)[] = ["ability", "ability2", "hidden", "passive"]; + for (const [idx, value] of permutations(attrs, 2).entries()) { + createAbilityMocks(Species.BULBASAUR + idx, { + ...baseObj, + [value[0]]: filter_ab1, + [value[1]]: filter_ab2, + }); + if (value.includes("passive")) { + whitelist.push(Species.BULBASAUR + idx); + } else { + blacklist.push(Species.BULBASAUR + idx); + } + } + + const pokedexHandler = await runToOpenPokedex(); + + // @ts-expect-error `filterText` is private + pokedexHandler.filterText.setValue(FilterTextRow.ABILITY_1, ab1_instance.name); + // @ts-expect-error `filterText` is private + pokedexHandler.filterText.setValue(FilterTextRow.ABILITY_2, ab2_instance.name); + + let whiteListCount = 0; + // @ts-expect-error `filteredPokemonData` is private + for (const species of pokedexHandler.filteredPokemonData) { + expect(blacklist, "entry must have one of the abilities as a passive").not.toContain(species.species.speciesId); + + const rawAbility = [species.species.ability1, species.species.ability2, species.species.abilityHidden]; + const rawPassive = species.species.getPassiveAbility(); + + const c1 = rawPassive === ab1_instance.id && rawAbility.includes(ab2_instance.id); + const c2 = c1 || (rawPassive === ab2_instance.id && rawAbility.includes(ab1_instance.id)); + + expect(c2, "each filtered entry should have the ability and passive combination").toBe(true); + if (whitelist.includes(species.species.speciesId)) { + whiteListCount++; + } + } + + expect(whiteListCount).toBe(whitelist.length); + }); + + it("should filter to show only the pokemon with a type when filtering by a single type", async () => { + const pokedexHandler = await runToOpenPokedex(); + + // @ts-expect-error - `filterBar` is private + pokedexHandler.filterBar.getFilter(DropDownColumn.TYPES).toggleOptionState(PokemonType.NORMAL + 1); + + const expectedPokemon = getSpeciesWithType(PokemonType.NORMAL); + // @ts-expect-error - `filteredPokemonData` is private + const filteredPokemon = new Set(pokedexHandler.filteredPokemonData.map(pokemon => pokemon.species.speciesId)); + + expect(filteredPokemon).toEqual(expectedPokemon); + }); + + // Todo: Pokemon with a mega that adds a type do not show up in the filter, e.g. pinsir. + it.todo("should show only the pokemon with one of the types when filtering by multiple types", async () => { + const pokedexHandler = await runToOpenPokedex(); + + // @ts-expect-error - `filterBar` is private + pokedexHandler.filterBar.getFilter(DropDownColumn.TYPES).toggleOptionState(PokemonType.NORMAL + 1); + // @ts-expect-error - `filterBar` is private + pokedexHandler.filterBar.getFilter(DropDownColumn.TYPES).toggleOptionState(PokemonType.FLYING + 1); + + const expectedPokemon = getSpeciesWithType(PokemonType.NORMAL, PokemonType.FLYING); + // @ts-expect-error - `filteredPokemonData` is private + const filteredPokemon = new Set(pokedexHandler.filteredPokemonData.map(pokemon => pokemon.species.speciesId)); + + expect(filteredPokemon).toEqual(expectedPokemon); + }); + + it("filtering for unlockable cost reduction only shows species with sufficient candies", async () => { + // load the save file + await game.importData("./test/testUtils/saves/data_pokedex_tests.prsv"); + const pokedexHandler = await runToOpenPokedex(); + + // @ts-expect-error - `filterBar` is private + const filter = pokedexHandler.filterBar.getFilter(DropDownColumn.UNLOCKS); + + // Cycling 4 times to get to the "can unlock" for cost reduction + for (let i = 0; i < 4; i++) { + // index 1 is the cost reduction + filter.toggleOptionState(1); + } + + const expectedPokemon = new Set([ + Species.CHIKORITA, + Species.CYNDAQUIL, + Species.TORCHIC, + Species.TURTWIG, + Species.EKANS, + Species.MUDKIP, + ]); + expect( + // @ts-expect-error - `filteredPokemonData` is private + pokedexHandler.filteredPokemonData.every(pokemon => + expectedPokemon.has(pokedexHandler.getStarterSpeciesId(pokemon.species.speciesId)), + ), + ).toBe(true); + }); + + it("filtering by passive unlocked only shows species that have their passive", async () => { + await game.importData("./test/testUtils/saves/data_pokedex_tests.prsv"); + const pokedexHandler = await runToOpenPokedex(); + + // @ts-expect-error - `filterBar` is private + const filter = pokedexHandler.filterBar.getFilter(DropDownColumn.UNLOCKS); + + filter.toggleOptionState(0); // cycle to Passive: Yes + + expect( + // @ts-expect-error - `filteredPokemonData` is private + pokedexHandler.filteredPokemonData.every( + pokemon => pokedexHandler.getStarterSpeciesId(pokemon.species.speciesId) === Species.MUDKIP, + ), + ).toBe(true); + }); + + it("filtering for pokemon that can unlock passive shows only species with sufficient candies", async () => { + await game.importData("./test/testUtils/saves/data_pokedex_tests.prsv"); + const pokedexHandler = await runToOpenPokedex(); + + // @ts-expect-error - `filterBar` is private + const filter = pokedexHandler.filterBar.getFilter(DropDownColumn.UNLOCKS); + + // Cycling 4 times to get to the "can unlock" for passive + const expectedPokemon = new Set([ + Species.EKANS, + Species.CHIKORITA, + Species.CYNDAQUIL, + Species.TORCHIC, + Species.TURTWIG, + ]); + + // cycling twice to get to the "can unlock" for passive + filter.toggleOptionState(0); + filter.toggleOptionState(0); + + expect( + // @ts-expect-error - `filteredPokemonData` is private + pokedexHandler.filteredPokemonData.every(pokemon => + expectedPokemon.has(pokedexHandler.getStarterSpeciesId(pokemon.species.speciesId)), + ), + ).toBe(true); + }); + + it("filtering for pokemon that have any cost reduction shows only the species that have unlocked a cost reduction", async () => { + await game.importData("./test/testUtils/saves/data_pokedex_tests.prsv"); + const pokedexHandler = await runToOpenPokedex(); + + const expectedPokemon = new Set([Species.TREECKO, Species.CYNDAQUIL, Species.TOTODILE]); + + // @ts-expect-error - `filterBar` is private + const filter = pokedexHandler.filterBar.getFilter(DropDownColumn.UNLOCKS); + // Cycle 1 time for cost reduction + filter.toggleOptionState(1); + + expect( + // @ts-expect-error - `filteredPokemonData` is private + pokedexHandler.filteredPokemonData.every(pokemon => + expectedPokemon.has(pokedexHandler.getStarterSpeciesId(pokemon.species.speciesId)), + ), + ).toBe(true); + }); + + it("filtering for pokemon that have a single cost reduction shows only the species that have unlocked a single cost reduction", async () => { + await game.importData("./test/testUtils/saves/data_pokedex_tests.prsv"); + const pokedexHandler = await runToOpenPokedex(); + + const expectedPokemon = new Set([Species.CYNDAQUIL, Species.TOTODILE]); + + // @ts-expect-error - `filterBar` is private + const filter = pokedexHandler.filterBar.getFilter(DropDownColumn.UNLOCKS); + // Cycle 2 times for one cost reduction + filter.toggleOptionState(1); + filter.toggleOptionState(1); + + expect( + // @ts-expect-error - `filteredPokemonData` is private + pokedexHandler.filteredPokemonData.every(pokemon => + expectedPokemon.has(pokedexHandler.getStarterSpeciesId(pokemon.species.speciesId)), + ), + ).toBe(true); + }); + + it("filtering for pokemon that have two cost reductions sorts only shows the species that have unlocked both cost reductions", async () => { + await game.importData("./test/testUtils/saves/data_pokedex_tests.prsv"); + const pokedexHandler = await runToOpenPokedex(); + + // @ts-expect-error - `filterBar` is private + const filter = pokedexHandler.filterBar.getFilter(DropDownColumn.UNLOCKS); + // Cycle 3 time for two cost reductions + filter.toggleOptionState(1); + filter.toggleOptionState(1); + filter.toggleOptionState(1); + + expect( + // @ts-expect-error - `filteredPokemonData` is private + pokedexHandler.filteredPokemonData.every( + pokemon => pokedexHandler.getStarterSpeciesId(pokemon.species.speciesId) === Species.TREECKO, + ), + ).toBe(true); + }); + + it("filtering by shiny status shows the caught pokemon with the selected shiny tier", async () => { + await game.importData("./test/testUtils/saves/data_pokedex_tests.prsv"); + const pokedexHandler = await runToOpenPokedex(); + // @ts-expect-error - `filterBar` is private + const filter = pokedexHandler.filterBar.getFilter(DropDownColumn.CAUGHT); + filter.toggleOptionState(3); + + // @ts-expect-error - `filteredPokemonData` is private + let filteredPokemon = pokedexHandler.filteredPokemonData.map(pokemon => pokemon.species.speciesId); + + // Red shiny + expect(filteredPokemon.length).toBe(1); + expect(filteredPokemon[0], "tier 1 shiny").toBe(Species.CATERPIE); + + // tier 2 shiny + filter.toggleOptionState(3); + filter.toggleOptionState(2); + + // @ts-expect-error - `filteredPokemonData` is private + filteredPokemon = pokedexHandler.filteredPokemonData.map(pokemon => pokemon.species.speciesId); + expect(filteredPokemon.length).toBe(1); + expect(filteredPokemon[0], "tier 2 shiny").toBe(Species.RATTATA); + + filter.toggleOptionState(2); + filter.toggleOptionState(1); + // @ts-expect-error - `filteredPokemonData` is private + filteredPokemon = pokedexHandler.filteredPokemonData.map(pokemon => pokemon.species.speciesId); + expect(filteredPokemon.length).toBe(1); + expect(filteredPokemon[0], "tier 3 shiny").toBe(Species.EKANS); + + // filter by no shiny + filter.toggleOptionState(1); + filter.toggleOptionState(4); + + // @ts-expect-error - `filteredPokemonData` is private + filteredPokemon = pokedexHandler.filteredPokemonData.map(pokemon => pokemon.species.speciesId); + expect(filteredPokemon.length).toBe(27); + expect(filteredPokemon, "not shiny").not.toContain(Species.CATERPIE); + expect(filteredPokemon, "not shiny").not.toContain(Species.RATTATA); + expect(filteredPokemon, "not shiny").not.toContain(Species.EKANS); + }); + + /**************************** + * Tests for UI Input * + ****************************/ + + // TODO: fix cursor wrapping + it.todo( + "should wrap the cursor to the top when moving to an empty entry when there are more than 81 pokemon", + async () => { + const pokedexHandler = await runToOpenPokedex(); + + // Filter by gen 2 so we can pan a specific amount. + // @ts-expect-error `filterBar` is private + pokedexHandler.filterBar.getFilter(DropDownColumn.GEN).options[2].toggleOptionState(); + pokedexHandler.updateStarters(); + // @ts-expect-error - `filteredPokemonData` is private + expect(pokedexHandler.filteredPokemonData.length, "pokemon in gen2").toBe(100); + + // Let's try to pan to the right to see what the pokemon it points to is. + + // pan to the right once and down 11 times + pokedexHandler.processInput(Button.RIGHT); + // Nab the pokemon that is selected for comparison later. + + // @ts-expect-error - `lastSpecies` is private + const selectedPokemon = pokedexHandler.lastSpecies.speciesId; + for (let i = 0; i < 11; i++) { + pokedexHandler.processInput(Button.DOWN); + } + + // @ts-expect-error `lastSpecies` is private + expect(selectedPokemon).toEqual(pokedexHandler.lastSpecies.speciesId); + }, + ); +}); From b49c994d2d6eb09cbce708b29462c6e46848e695 Mon Sep 17 00:00:00 2001 From: AJ Fontaine <36677462+Fontbane@users.noreply.github.com> Date: Wed, 23 Apr 2025 15:48:04 -0400 Subject: [PATCH 49/52] [Balance][Refactor] Move fixed boss waves enum to file, adjust GL templates (#5689) * Move fixed boss waves enum to file, adjust GL templates * Move post return to default case * Address comment --------- Co-authored-by: Wlowscha <54003515+Wlowscha@users.noreply.github.com> --- src/battle.ts | 24 +------- src/data/challenge.ts | 3 +- src/data/trainers/TrainerPartyTemplate.ts | 73 +++++++++++++++++------ src/enums/fixed-boss-waves.ts | 22 +++++++ src/phases/victory-phase.ts | 2 +- 5 files changed, 80 insertions(+), 44 deletions(-) create mode 100644 src/enums/fixed-boss-waves.ts diff --git a/src/battle.ts b/src/battle.ts index 6630d53bd67..07e520d6bc0 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -31,29 +31,7 @@ import type { CustomModifierSettings } from "#app/modifier/modifier-type"; import { ModifierTier } from "#app/modifier/modifier-tier"; import type { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { BattleType } from "#enums/battle-type"; - -export enum ClassicFixedBossWaves { - TOWN_YOUNGSTER = 5, - RIVAL_1 = 8, - RIVAL_2 = 25, - EVIL_GRUNT_1 = 35, - RIVAL_3 = 55, - EVIL_GRUNT_2 = 62, - EVIL_GRUNT_3 = 64, - EVIL_ADMIN_1 = 66, - RIVAL_4 = 95, - EVIL_GRUNT_4 = 112, - EVIL_ADMIN_2 = 114, - EVIL_BOSS_1 = 115, - RIVAL_5 = 145, - EVIL_BOSS_2 = 165, - ELITE_FOUR_1 = 182, - ELITE_FOUR_2 = 184, - ELITE_FOUR_3 = 186, - ELITE_FOUR_4 = 188, - CHAMPION = 190, - RIVAL_6 = 195, -} +import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; export enum BattlerIndex { ATTACKER = -1, diff --git a/src/data/challenge.ts b/src/data/challenge.ts index f786152ca3d..7388f397c7e 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -8,7 +8,8 @@ import { speciesStarterCosts } from "#app/data/balance/starters"; import type Pokemon from "#app/field/pokemon"; import { PokemonMove } from "#app/field/pokemon"; import type { FixedBattleConfig } from "#app/battle"; -import { ClassicFixedBossWaves, getRandomTrainerFunc } from "#app/battle"; +import { getRandomTrainerFunc } from "#app/battle"; +import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; import { BattleType } from "#enums/battle-type"; import Trainer, { TrainerVariant } from "#app/field/trainer"; import { PokemonType } from "#enums/pokemon-type"; diff --git a/src/data/trainers/TrainerPartyTemplate.ts b/src/data/trainers/TrainerPartyTemplate.ts index 5d02ffdc6af..e4c8ddf4c58 100644 --- a/src/data/trainers/TrainerPartyTemplate.ts +++ b/src/data/trainers/TrainerPartyTemplate.ts @@ -1,6 +1,8 @@ import { startingWave } from "#app/starting-wave"; import { globalScene } from "#app/global-scene"; import { PartyMemberStrength } from "#enums/party-member-strength"; +import { GameModes } from "#app/game-mode"; +import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves"; export class TrainerPartyTemplate { public size: number; @@ -165,6 +167,11 @@ export const trainerPartyTemplates = { new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER), ), GYM_LEADER_5: new TrainerPartyCompoundTemplate( + new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE), + new TrainerPartyTemplate(1, PartyMemberStrength.STRONG), + new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER), + ), + GYM_LEADER_6: new TrainerPartyCompoundTemplate( new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(2, PartyMemberStrength.STRONG), new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER), @@ -222,19 +229,18 @@ export const trainerPartyTemplates = { */ export function getEvilGruntPartyTemplate(): TrainerPartyTemplate { const waveIndex = globalScene.currentBattle?.waveIndex; - if (waveIndex < 40) { - return trainerPartyTemplates.TWO_AVG; + switch (waveIndex) { + case ClassicFixedBossWaves.EVIL_GRUNT_1: + return trainerPartyTemplates.TWO_AVG; + case ClassicFixedBossWaves.EVIL_GRUNT_2: + return trainerPartyTemplates.THREE_AVG; + case ClassicFixedBossWaves.EVIL_GRUNT_3: + return trainerPartyTemplates.TWO_AVG_ONE_STRONG; + case ClassicFixedBossWaves.EVIL_ADMIN_1: + return trainerPartyTemplates.GYM_LEADER_4; // 3avg 1 strong 1 stronger + default: + return trainerPartyTemplates.GYM_LEADER_6; // 3 avg 2 strong 1 stronger } - if (waveIndex < 63) { - return trainerPartyTemplates.THREE_AVG; - } - if (waveIndex < 65) { - return trainerPartyTemplates.TWO_AVG_ONE_STRONG; - } - if (waveIndex < 112) { - return trainerPartyTemplates.GYM_LEADER_4; // 3avg 1 strong 1 stronger - } - return trainerPartyTemplates.GYM_LEADER_5; // 3 avg 2 strong 1 stronger } export function getWavePartyTemplate(...templates: TrainerPartyTemplate[]) { @@ -245,11 +251,40 @@ export function getWavePartyTemplate(...templates: TrainerPartyTemplate[]) { } export function getGymLeaderPartyTemplate() { - return getWavePartyTemplate( - trainerPartyTemplates.GYM_LEADER_1, - trainerPartyTemplates.GYM_LEADER_2, - trainerPartyTemplates.GYM_LEADER_3, - trainerPartyTemplates.GYM_LEADER_4, - trainerPartyTemplates.GYM_LEADER_5, - ); + const { currentBattle, gameMode } = globalScene; + switch (gameMode.modeId) { + case GameModes.DAILY: + if (currentBattle?.waveIndex <= 20) { + return trainerPartyTemplates.GYM_LEADER_2 + } + return trainerPartyTemplates.GYM_LEADER_3; + case GameModes.CHALLENGE: // In the future, there may be a ChallengeType to call here. For now, use classic's. + case GameModes.CLASSIC: + if (currentBattle?.waveIndex <= 20) { + return trainerPartyTemplates.GYM_LEADER_1; // 1 avg 1 strong + } + else if (currentBattle?.waveIndex <= 30) { + return trainerPartyTemplates.GYM_LEADER_2; // 1 avg 1 strong 1 stronger + } + else if (currentBattle?.waveIndex <= 60) { // 50 and 60 + return trainerPartyTemplates.GYM_LEADER_3; // 2 avg 1 strong 1 stronger + } + else if (currentBattle?.waveIndex <= 80) { + return trainerPartyTemplates.GYM_LEADER_4; // 3 avg 1 strong 1 stronger + } + else if (currentBattle?.waveIndex <= 90) { + return trainerPartyTemplates.GYM_LEADER_5; // 4 avg 1 strong 1 stronger + } + // 110+ + return trainerPartyTemplates.GYM_LEADER_6; // 3 avg 2 strong 1 stronger + default: + return getWavePartyTemplate( + trainerPartyTemplates.GYM_LEADER_1, + trainerPartyTemplates.GYM_LEADER_2, + trainerPartyTemplates.GYM_LEADER_3, + trainerPartyTemplates.GYM_LEADER_4, + trainerPartyTemplates.GYM_LEADER_5, + trainerPartyTemplates.GYM_LEADER_6, + ); + } } diff --git a/src/enums/fixed-boss-waves.ts b/src/enums/fixed-boss-waves.ts new file mode 100644 index 00000000000..623d9035472 --- /dev/null +++ b/src/enums/fixed-boss-waves.ts @@ -0,0 +1,22 @@ +export enum ClassicFixedBossWaves { + TOWN_YOUNGSTER = 5, + RIVAL_1 = 8, + RIVAL_2 = 25, + EVIL_GRUNT_1 = 35, + RIVAL_3 = 55, + EVIL_GRUNT_2 = 62, + EVIL_GRUNT_3 = 64, + EVIL_ADMIN_1 = 66, + RIVAL_4 = 95, + EVIL_GRUNT_4 = 112, + EVIL_ADMIN_2 = 114, + EVIL_BOSS_1 = 115, + RIVAL_5 = 145, + EVIL_BOSS_2 = 165, + ELITE_FOUR_1 = 182, + ELITE_FOUR_2 = 184, + ELITE_FOUR_3 = 186, + ELITE_FOUR_4 = 188, + CHAMPION = 190, + RIVAL_6 = 195 +} diff --git a/src/phases/victory-phase.ts b/src/phases/victory-phase.ts index 6e1837a4749..1204877fec2 100644 --- a/src/phases/victory-phase.ts +++ b/src/phases/victory-phase.ts @@ -1,5 +1,5 @@ import type { BattlerIndex } from "#app/battle"; -import { ClassicFixedBossWaves } from "#app/battle"; +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"; From c7265543bdd7d5ee53af596d40ca05c36d48a931 Mon Sep 17 00:00:00 2001 From: Blitzy <118096277+Blitz425@users.noreply.github.com> Date: Wed, 23 Apr 2025 16:15:45 -0500 Subject: [PATCH 50/52] [Balance] Update Transistor to Gen IX version (#5700) Update ability.ts --- src/data/abilities/ability.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index d8b648ebe82..b018a87a08d 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -7227,7 +7227,7 @@ export function initAbilities() { new Ability(Abilities.CURIOUS_MEDICINE, 8) .attr(PostSummonClearAllyStatStagesAbAttr), new Ability(Abilities.TRANSISTOR, 8) - .attr(MoveTypePowerBoostAbAttr, PokemonType.ELECTRIC), + .attr(MoveTypePowerBoostAbAttr, PokemonType.ELECTRIC, 1.3), new Ability(Abilities.DRAGONS_MAW, 8) .attr(MoveTypePowerBoostAbAttr, PokemonType.DRAGON), new Ability(Abilities.CHILLING_NEIGH, 8) @@ -7412,4 +7412,4 @@ export function initAbilities() { .unreplaceable() // TODO is this true? .attr(ConfusionOnStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) ); -} \ No newline at end of file +} From 5de567a3db84d791641c39ac0bbbfbb7a19320b6 Mon Sep 17 00:00:00 2001 From: AJ Fontaine <36677462+Fontbane@users.noreply.github.com> Date: Wed, 23 Apr 2025 17:18:03 -0400 Subject: [PATCH 51/52] [Balance] Wave 90 gym leader has 5 mons (#5699) 5 mons on wave 90 --- src/data/trainers/TrainerPartyTemplate.ts | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/data/trainers/TrainerPartyTemplate.ts b/src/data/trainers/TrainerPartyTemplate.ts index e4c8ddf4c58..1952bcc179e 100644 --- a/src/data/trainers/TrainerPartyTemplate.ts +++ b/src/data/trainers/TrainerPartyTemplate.ts @@ -167,11 +167,6 @@ export const trainerPartyTemplates = { new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER), ), GYM_LEADER_5: new TrainerPartyCompoundTemplate( - new TrainerPartyTemplate(4, PartyMemberStrength.AVERAGE), - new TrainerPartyTemplate(1, PartyMemberStrength.STRONG), - new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER), - ), - GYM_LEADER_6: new TrainerPartyCompoundTemplate( new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(2, PartyMemberStrength.STRONG), new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER), @@ -239,7 +234,7 @@ export function getEvilGruntPartyTemplate(): TrainerPartyTemplate { case ClassicFixedBossWaves.EVIL_ADMIN_1: return trainerPartyTemplates.GYM_LEADER_4; // 3avg 1 strong 1 stronger default: - return trainerPartyTemplates.GYM_LEADER_6; // 3 avg 2 strong 1 stronger + return trainerPartyTemplates.GYM_LEADER_5; // 3 avg 2 strong 1 stronger } } @@ -269,14 +264,11 @@ export function getGymLeaderPartyTemplate() { else if (currentBattle?.waveIndex <= 60) { // 50 and 60 return trainerPartyTemplates.GYM_LEADER_3; // 2 avg 1 strong 1 stronger } - else if (currentBattle?.waveIndex <= 80) { + else if (currentBattle?.waveIndex <= 90) { // 80 and 90 return trainerPartyTemplates.GYM_LEADER_4; // 3 avg 1 strong 1 stronger } - else if (currentBattle?.waveIndex <= 90) { - return trainerPartyTemplates.GYM_LEADER_5; // 4 avg 1 strong 1 stronger - } // 110+ - return trainerPartyTemplates.GYM_LEADER_6; // 3 avg 2 strong 1 stronger + return trainerPartyTemplates.GYM_LEADER_5; // 3 avg 2 strong 1 stronger default: return getWavePartyTemplate( trainerPartyTemplates.GYM_LEADER_1, @@ -284,7 +276,6 @@ export function getGymLeaderPartyTemplate() { trainerPartyTemplates.GYM_LEADER_3, trainerPartyTemplates.GYM_LEADER_4, trainerPartyTemplates.GYM_LEADER_5, - trainerPartyTemplates.GYM_LEADER_6, ); } } From 75400a39ed16e2f86e9c82b930827a3590cf6b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=AAs=20Sim=C3=B5es?= Date: Wed, 23 Apr 2025 22:29:44 +0100 Subject: [PATCH 52/52] [Bug] [UI/UX] Status moves now play a No Effect Message Against Immune Type Pokemon (#5533) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix #5085 Moves dont play a No Effect Message Against Immune Type When using non-volatile status move like: Will-O-Wisp, Thunder Wave, Toxic, or Poison Gas against a Pokémon whose type is immune to that Status condition, no "It doesn't affect" message plays. My proposed fixes: In move.ts: Removed a redudant if statement in StatusEffectAttr class In pokemon.ts: Renamed the "messageIsImmune" function to "queueImmuneMessage" --- src/data/moves/move.ts | 9 +-------- src/field/pokemon.ts | 45 ++++++++++++++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 5d57bb6dc49..903b2726676 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -2459,14 +2459,7 @@ export class StatusEffectAttr extends MoveEffectAttr { const statusCheck = moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance; if (statusCheck) { const pokemon = this.selfTarget ? user : target; - if (pokemon.status && !this.overrideStatus) { - return false; - } - - if (user !== target && target.isSafeguarded(user)) { - if (move.category === MoveCategory.STATUS) { - globalScene.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(target) })); - } + if (user !== target && move.category === MoveCategory.STATUS && !target.canSetStatus(this.effect, false, false, user, true)) { return false; } if (((!pokemon.status || this.overrideStatus) || (pokemon.status.effect === this.effect && moveChance < 0)) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index d565a590792..2de8cc150c9 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -248,6 +248,7 @@ import { PLAYER_PARTY_MAX_SIZE } from "#app/constants"; import { CustomPokemonData } from "#app/data/custom-pokemon-data"; import { SwitchType } from "#enums/switch-type"; import { SpeciesFormKey } from "#enums/species-form-key"; +import {getStatusEffectOverlapText } from "#app/data/status-effect"; import { BASE_HIDDEN_ABILITY_CHANCE, BASE_SHINY_CHANCE, @@ -5364,6 +5365,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ); } + queueImmuneMessage(quiet: boolean, effect?: StatusEffect): void { + if (!effect || quiet) { + return; + } + const message = effect && this.status?.effect === effect + ? getStatusEffectOverlapText(effect ?? StatusEffect.NONE, getPokemonNameWithAffix(this)) + : i18next.t("abilityTriggers:moveImmunity", { + pokemonNameWithAffix: getPokemonNameWithAffix(this), + }); + globalScene.queueMessage(message); + } + /** * Checks if a status effect can be applied to the Pokemon. * @@ -5382,6 +5395,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ): boolean { if (effect !== StatusEffect.FAINT) { if (overrideStatus ? this.status?.effect === effect : this.status) { + this.queueImmuneMessage(quiet, effect); return false; } if ( @@ -5389,18 +5403,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { !ignoreField && globalScene.arena.terrain?.terrainType === TerrainType.MISTY ) { + this.queueImmuneMessage(quiet, effect); return false; } } - if ( - sourcePokemon && - sourcePokemon !== this && - this.isSafeguarded(sourcePokemon) - ) { - return false; - } - const types = this.getTypes(true, true); switch (effect) { @@ -5429,17 +5436,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - return true; + return true; }); if (this.isOfType(PokemonType.POISON) || this.isOfType(PokemonType.STEEL)) { if (poisonImmunity.includes(true)) { + this.queueImmuneMessage(quiet, effect); return false; } } break; case StatusEffect.PARALYSIS: if (this.isOfType(PokemonType.ELECTRIC)) { + this.queueImmuneMessage(quiet, effect); return false; } break; @@ -5448,6 +5457,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.isGrounded() && globalScene.arena.terrain?.terrainType === TerrainType.ELECTRIC ) { + this.queueImmuneMessage(quiet, effect); return false; } break; @@ -5460,11 +5470,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { globalScene.arena.weather.weatherType, )) ) { + this.queueImmuneMessage(quiet, effect); return false; } break; case StatusEffect.BURN: if (this.isOfType(PokemonType.FIRE)) { + this.queueImmuneMessage(quiet, effect); return false; } break; @@ -5499,6 +5511,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } + if ( + sourcePokemon && + sourcePokemon !== this && + this.isSafeguarded(sourcePokemon) + ) { + if(!quiet){ + globalScene.queueMessage( + i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(this) + })); + } + return false; + } + return true; } @@ -5510,7 +5535,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { sourceText: string | null = null, overrideStatus?: boolean ): boolean { - if (!this.canSetStatus(effect, asPhase, overrideStatus, sourcePokemon)) { + if (!this.canSetStatus(effect, false, overrideStatus, sourcePokemon)) { return false; } if (this.isFainted() && effect !== StatusEffect.FAINT) {