diff --git a/biome.jsonc b/biome.jsonc index 277edf89366..27ce10b8629 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -5,7 +5,7 @@ */ { - "$schema": "https://biomejs.dev/schemas/2.2.5/schema.json", + "$schema": "https://biomejs.dev/schemas/2.3.2/schema.json", "vcs": { "enabled": true, "clientKind": "git", @@ -35,8 +35,6 @@ // TODO: lint css and html? "!**/*.css", "!**/*.html", - // TODO: enable linting this file - "!**/src/data/moves/move.ts", // this file is too big "!**/src/data/balance/tm-species-map.ts" ] @@ -204,7 +202,8 @@ "noDocumentCookie": "off", // Firefox has minimal support for the "Cookie Store API" "noConstantBinaryExpressions": "error", "noTsIgnore": "error", - "useIterableCallbackReturn": "warn" // TODO: Refactor and change to error + "useIterableCallbackReturn": "warn", // TODO: Refactor and change to error + "noNonNullAssertedOptionalChain": "warn" // TODO: Refactor and change to error }, "complexity": { "useWhile": "error", @@ -233,7 +232,6 @@ "options": { "max": 7 } }, "noShadow": "warn", // TODO: refactor and make "error" - "noNonNullAssertedOptionalChain": "warn", // TODO: refactor and make "error" "noDuplicateDependencies": "error", "noImportCycles": "error", // TODO: Change to error once promises are used properly diff --git a/package.json b/package.json index 59d0a5f877a..9550c0a9fc7 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "update-submodules:remote": "pnpm update-locales:remote && pnpm update-assets:remote" }, "devDependencies": { - "@biomejs/biome": "2.2.5", + "@biomejs/biome": "2.3.2", "@ls-lint/ls-lint": "2.3.1", "@types/crypto-js": "^4.2.0", "@types/jsdom": "^27.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 133ee8b85b7..c93f37b5f0b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,8 +46,8 @@ importers: version: 1.80.16(graphology-types@0.24.8) devDependencies: '@biomejs/biome': - specifier: 2.2.5 - version: 2.2.5 + specifier: 2.3.2 + version: 2.3.2 '@ls-lint/ls-lint': specifier: 2.3.1 version: 2.3.1 @@ -200,55 +200,55 @@ packages: resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} - '@biomejs/biome@2.2.5': - resolution: {integrity: sha512-zcIi+163Rc3HtyHbEO7CjeHq8DjQRs40HsGbW6vx2WI0tg8mYQOPouhvHSyEnCBAorfYNnKdR64/IxO7xQ5faw==} + '@biomejs/biome@2.3.2': + resolution: {integrity: sha512-8e9tzamuDycx7fdrcJ/F/GDZ8SYukc5ud6tDicjjFqURKYFSWMl0H0iXNXZEGmcmNUmABgGuHThPykcM41INgg==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.2.5': - resolution: {integrity: sha512-MYT+nZ38wEIWVcL5xLyOhYQQ7nlWD0b/4mgATW2c8dvq7R4OQjt/XGXFkXrmtWmQofaIM14L7V8qIz/M+bx5QQ==} + '@biomejs/cli-darwin-arm64@2.3.2': + resolution: {integrity: sha512-4LECm4kc3If0JISai4c3KWQzukoUdpxy4fRzlrPcrdMSRFksR9ZoXK7JBcPuLBmd2SoT4/d7CQS33VnZpgBjew==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.2.5': - resolution: {integrity: sha512-FLIEl73fv0R7dI10EnEiZLw+IMz3mWLnF95ASDI0kbx6DDLJjWxE5JxxBfmG+udz1hIDd3fr5wsuP7nwuTRdAg==} + '@biomejs/cli-darwin-x64@2.3.2': + resolution: {integrity: sha512-jNMnfwHT4N3wi+ypRfMTjLGnDmKYGzxVr1EYAPBcauRcDnICFXN81wD6wxJcSUrLynoyyYCdfW6vJHS/IAoTDA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.2.5': - resolution: {integrity: sha512-5Ov2wgAFwqDvQiESnu7b9ufD1faRa+40uwrohgBopeY84El2TnBDoMNXx6iuQdreoFGjwW8vH6k68G21EpNERw==} + '@biomejs/cli-linux-arm64-musl@2.3.2': + resolution: {integrity: sha512-2Zz4usDG1GTTPQnliIeNx6eVGGP2ry5vE/v39nT73a3cKN6t5H5XxjcEoZZh62uVZvED7hXXikclvI64vZkYqw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-arm64@2.2.5': - resolution: {integrity: sha512-5DjiiDfHqGgR2MS9D+AZ8kOfrzTGqLKywn8hoXpXXlJXIECGQ32t+gt/uiS2XyGBM2XQhR6ztUvbjZWeccFMoQ==} + '@biomejs/cli-linux-arm64@2.3.2': + resolution: {integrity: sha512-amnqvk+gWybbQleRRq8TMe0rIv7GHss8mFJEaGuEZYWg1Tw14YKOkeo8h6pf1c+d3qR+JU4iT9KXnBKGON4klw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-x64-musl@2.2.5': - resolution: {integrity: sha512-AVqLCDb/6K7aPNIcxHaTQj01sl1m989CJIQFQEaiQkGr2EQwyOpaATJ473h+nXDUuAcREhccfRpe/tu+0wu0eQ==} + '@biomejs/cli-linux-x64-musl@2.3.2': + resolution: {integrity: sha512-gzB19MpRdTuOuLtPpFBGrV3Lq424gHyq2lFj8wfX9tvLMLdmA/R9C7k/mqBp/spcbWuHeIEKgEs3RviOPcWGBA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-linux-x64@2.2.5': - resolution: {integrity: sha512-fq9meKm1AEXeAWan3uCg6XSP5ObA6F/Ovm89TwaMiy1DNIwdgxPkNwxlXJX8iM6oRbFysYeGnT0OG8diCWb9ew==} + '@biomejs/cli-linux-x64@2.3.2': + resolution: {integrity: sha512-8BG/vRAhFz1pmuyd24FQPhNeueLqPtwvZk6yblABY2gzL2H8fLQAF/Z2OPIc+BPIVPld+8cSiKY/KFh6k81xfA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-win32-arm64@2.2.5': - resolution: {integrity: sha512-xaOIad4wBambwJa6mdp1FigYSIF9i7PCqRbvBqtIi9y29QtPVQ13sDGtUnsRoe6SjL10auMzQ6YAe+B3RpZXVg==} + '@biomejs/cli-win32-arm64@2.3.2': + resolution: {integrity: sha512-lCruqQlfWjhMlOdyf5pDHOxoNm4WoyY2vZ4YN33/nuZBRstVDuqPPjS0yBkbUlLEte11FbpW+wWSlfnZfSIZvg==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.2.5': - resolution: {integrity: sha512-F/jhuXCssPFAuciMhHKk00xnCAxJRS/pUzVfXYmOMUp//XW7mO6QeCjsjvnm8L4AO/dG2VOB0O+fJPiJ2uXtIw==} + '@biomejs/cli-win32-x64@2.3.2': + resolution: {integrity: sha512-6Ee9P26DTb4D8sN9nXxgbi9Dw5vSOfH98M7UlmkjKB2vtUbrRqCbZiNfryGiwnPIpd6YUoTl7rLVD2/x1CyEHQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] @@ -2175,39 +2175,39 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@biomejs/biome@2.2.5': + '@biomejs/biome@2.3.2': optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.2.5 - '@biomejs/cli-darwin-x64': 2.2.5 - '@biomejs/cli-linux-arm64': 2.2.5 - '@biomejs/cli-linux-arm64-musl': 2.2.5 - '@biomejs/cli-linux-x64': 2.2.5 - '@biomejs/cli-linux-x64-musl': 2.2.5 - '@biomejs/cli-win32-arm64': 2.2.5 - '@biomejs/cli-win32-x64': 2.2.5 + '@biomejs/cli-darwin-arm64': 2.3.2 + '@biomejs/cli-darwin-x64': 2.3.2 + '@biomejs/cli-linux-arm64': 2.3.2 + '@biomejs/cli-linux-arm64-musl': 2.3.2 + '@biomejs/cli-linux-x64': 2.3.2 + '@biomejs/cli-linux-x64-musl': 2.3.2 + '@biomejs/cli-win32-arm64': 2.3.2 + '@biomejs/cli-win32-x64': 2.3.2 - '@biomejs/cli-darwin-arm64@2.2.5': + '@biomejs/cli-darwin-arm64@2.3.2': optional: true - '@biomejs/cli-darwin-x64@2.2.5': + '@biomejs/cli-darwin-x64@2.3.2': optional: true - '@biomejs/cli-linux-arm64-musl@2.2.5': + '@biomejs/cli-linux-arm64-musl@2.3.2': optional: true - '@biomejs/cli-linux-arm64@2.2.5': + '@biomejs/cli-linux-arm64@2.3.2': optional: true - '@biomejs/cli-linux-x64-musl@2.2.5': + '@biomejs/cli-linux-x64-musl@2.3.2': optional: true - '@biomejs/cli-linux-x64@2.2.5': + '@biomejs/cli-linux-x64@2.3.2': optional: true - '@biomejs/cli-win32-arm64@2.2.5': + '@biomejs/cli-win32-arm64@2.3.2': optional: true - '@biomejs/cli-win32-x64@2.2.5': + '@biomejs/cli-win32-x64@2.3.2': optional: true '@bundled-es-modules/cookie@2.0.1': diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index cbdfe780382..8b5e80792bd 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -1,8 +1,5 @@ -import type { BattlerTag } from "#data/battler-tags"; -import { AbAttrParamsWithCancel, PreAttackModifyPowerAbAttrParams } from "#abilities/ability"; -import { - applyAbAttrs -} from "#abilities/apply-ab-attrs"; +import type { AbAttrParamsWithCancel, PreAttackModifyPowerAbAttrParams } from "#abilities/ability"; +import { applyAbAttrs } from "#abilities/apply-ab-attrs"; import { loggedInUser } from "#app/account"; import type { GameMode } from "#app/game-mode"; import { globalScene } from "#app/global-scene"; @@ -19,19 +16,14 @@ import { ShellTrapTag, StockpilingTag, SubstituteTag, - SupremeOverlordTag, + type SupremeOverlordTag, TrappedTag, TypeBoostTag, } from "#data/battler-tags"; import { getBerryEffectFunc } from "#data/berry"; import { allAbilities, allMoves } from "#data/data-lists"; import { SpeciesFormChangeRevertWeatherFormTrigger } from "#data/form-change-triggers"; -import { DelayedAttackTag } from "#data/positional-tags/positional-tag"; -import { - getNonVolatileStatusEffects, - getStatusEffectHealText, - isNonVolatileStatusEffect, -} from "#data/status-effect"; +import { getNonVolatileStatusEffects, getStatusEffectHealText, isNonVolatileStatusEffect } from "#data/status-effect"; import { TerrainType } from "#data/terrain"; import { getTypeDamageMultiplier } from "#data/type"; import { AbilityId } from "#enums/ability-id"; @@ -47,10 +39,12 @@ import { FieldPosition } from "#enums/field-position"; import { HitResult } from "#enums/hit-result"; import { ModifierPoolType } from "#enums/modifier-pool-type"; import { ChargeAnim } from "#enums/move-anims-common"; -import { MoveCategory, MoveDamageCategory } from "#enums/move-category"; +import { MoveCategory, type MoveDamageCategory } from "#enums/move-category"; import { MoveEffectTrigger } from "#enums/move-effect-trigger"; import { MoveFlags } from "#enums/move-flags"; import { MoveId } from "#enums/move-id"; +import { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier"; +import { MovePriorityInBracket } from "#enums/move-priority-in-bracket"; import { MoveResult } from "#enums/move-result"; import { MoveTarget } from "#enums/move-target"; import { isVirtual, MoveUseMode } from "#enums/move-use-mode"; @@ -58,13 +52,7 @@ import { MultiHitType } from "#enums/multi-hit-type"; import { MAX_POKEMON_TYPE, PokemonType } from "#enums/pokemon-type"; import { PositionalTagType } from "#enums/positional-tag-type"; import { SpeciesId } from "#enums/species-id"; -import { - BATTLE_STATS, - type BattleStat, - type EffectiveStat, - getStatKey, - Stat, -} from "#enums/stat"; +import { BATTLE_STATS, type BattleStat, type EffectiveStat, getStatKey, Stat } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; import { SwitchType } from "#enums/switch-type"; import { WeatherType } from "#enums/weather-type"; @@ -79,41 +67,66 @@ import { PreserveBerryModifier, } from "#modifiers/modifier"; import { applyMoveAttrs } from "#moves/apply-attrs"; -import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSketchMoves, invalidSleepTalkMoves } from "#moves/invalid-moves"; -import { consecutiveUseRestriction, counterAttackConditionBoth, counterAttackConditionPhysical, counterAttackConditionSpecial, failAgainstFinalBossCondition, FailIfInsufficientHpCondition, failIfTargetNotAttackingCondition, failTeleportCondition, FirstMoveCondition, gravityUseRestriction, lastResortCondition, MoveCondition, MoveRestriction, upperHandCondition } from "#moves/move-condition"; +import { + invalidAssistMoves, + invalidCopycatMoves, + invalidMetronomeMoves, + invalidMirrorMoveMoves, + invalidSketchMoves, + invalidSleepTalkMoves, +} from "#moves/invalid-moves"; +import { + consecutiveUseRestriction, + counterAttackConditionBoth, + counterAttackConditionPhysical, + counterAttackConditionSpecial, + FailIfInsufficientHpCondition, + FirstMoveCondition, + failAgainstFinalBossCondition, + failIfTargetNotAttackingCondition, + failTeleportCondition, + gravityUseRestriction, + lastResortCondition, + MoveCondition, + MoveRestriction, + upperHandCondition, +} from "#moves/move-condition"; import { frenzyMissFunc, getCounterAttackTarget, getMoveTargets } from "#moves/move-utils"; import { PokemonMove } from "#moves/pokemon-move"; -import { MovePhase } from "#phases/move-phase"; -import { PokemonHealPhase } from "#phases/pokemon-heal-phase"; +import type { MovePhase } from "#phases/move-phase"; +import type { Constructor } from "#types/common"; import type { Localizable } from "#types/locales"; -import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindString, MoveMessageFunc } from "#types/move-types"; +import type { + ChargingMove, + MoveAttrMap, + MoveAttrString, + MoveClassMap, + MoveKindString, + MoveMessageFunc, +} from "#types/move-types"; import type { TurnMove } from "#types/turn-move"; import type { AbstractConstructor } from "#types/type-helpers"; +import { coerceArray } from "#utils/array"; import { applyChallenges } from "#utils/challenge-utils"; import { BooleanHolder, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common"; -import type { Constructor } from "#types/common"; -import { coerceArray } from "#utils/array"; import { getEnumValues } from "#utils/enums"; -import { areAllies } from "#utils/pokemon-utils"; +import { areAllies, canSpeciesTera, willTerastallize } from "#utils/pokemon-utils"; +import { inSpeedOrder } from "#utils/speed-order-generator"; import { toCamelCase, toTitleCase } from "#utils/strings"; import i18next from "i18next"; -import { MovePhaseTimingModifier } from "#enums/move-phase-timing-modifier"; -import { inSpeedOrder } from "#utils/speed-order-generator"; -import { canSpeciesTera, willTerastallize } from "#utils/pokemon-utils"; -import { MovePriorityInBracket } from "#enums/move-priority-in-bracket"; /** * A function used to conditionally determine execution of a given {@linkcode MoveAttr}. * Conventionally returns `true` for success and `false` for failure. -*/ + */ export type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean; export type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean; export abstract class Move implements Localizable { public id: MoveId; public name: string; - private _type: PokemonType; - private _category: MoveCategory; + private readonly _type: PokemonType; + private readonly _category: MoveCategory; public moveTarget: MoveTarget; public power: number; public accuracy: number; @@ -133,11 +146,11 @@ export abstract class Move implements Localizable { * * Different from {@linkcode restrictions}, which are checked when the move is selected */ - private conditions: MoveCondition[] = []; + private readonly conditions: MoveCondition[] = []; /** * Move failure conditions that occur during the second sequence (after move message but before the move is recorded as the last move used) */ - private conditionsSeq2: MoveCondition[] = []; + private readonly conditionsSeq2: MoveCondition[] = []; /** * Move failure conditions that occur during the third sequence (after accuracy and before move effects). * @@ -174,29 +187,40 @@ export abstract class Move implements Localizable { * * @see {@link https://www.smogon.com/forums/threads/sword-shield-battle-mechanics-research.3655528/page-54#post-8548957} */ - private conditionsSeq3: MoveCondition[] = []; + private readonly conditionsSeq3: MoveCondition[] = []; /** * Conditions that must be false for a move to be able to be selected. * * @remarks Different from {@linkcode conditions}, which is checked when the move is invoked */ - private restrictions: MoveRestriction[] = []; + private readonly restrictions: MoveRestriction[] = []; /** The move's {@linkcode MoveFlags} */ - private flags: number = 0; - private nameAppend: string = ""; + private flags = 0; + private nameAppend = ""; /** * Check if the move is of the given subclass without requiring `instanceof`. - * - * ! Does _not_ work for {@linkcode ChargingAttackMove} and {@linkcode ChargingSelfStatusMove} subclasses. For those, - * use {@linkcode isChargingMove} instead. + * @remarks + * Does _not_ work for {@linkcode ChargingAttackMove} and {@linkcode ChargingSelfStatusMove} subclasses. + * For those, use {@linkcode isChargingMove} instead. * * @param moveKind - The string name of the move to check against * @returns Whether this move is of the provided type. */ public abstract is(moveKind: K): this is MoveClassMap[K]; - constructor(id: MoveId, type: PokemonType, category: MoveCategory, defaultMoveTarget: MoveTarget, power: number, accuracy: number, pp: number, chance: number, priority: number, generation: number) { + constructor( + id: MoveId, + type: PokemonType, + category: MoveCategory, + defaultMoveTarget: MoveTarget, + power: number, + accuracy: number, + pp: number, + chance: number, + priority: number, + generation: number, + ) { this.id = id; this._type = type; this._category = category; @@ -226,11 +250,11 @@ export abstract class Move implements Localizable { } localize(): void { - const i18nKey = toCamelCase(MoveId[this.id]) + const i18nKey = toCamelCase(MoveId[this.id]); if (this.id === MoveId.NONE) { this.name = ""; - this.effect = "" + this.effect = ""; return; } @@ -243,7 +267,7 @@ export abstract class Move implements Localizable { * @param attrType - The name of a {@linkcode MoveAttr} to search for * @returns An array containing all attributes matching `attrType`, or an empty array if none match. */ - getAttrs(attrType: T): (MoveAttrMap[T])[] { + getAttrs(attrType: T): MoveAttrMap[T][] { const targetAttr = MoveAttrs[attrType]; if (!targetAttr) { return []; @@ -262,7 +286,7 @@ export abstract class Move implements Localizable { if (!targetAttr) { return false; } - return this.attrs.some((attr) => attr instanceof targetAttr); + return this.attrs.some(attr => attr instanceof targetAttr); } /** @@ -416,18 +440,18 @@ export abstract class Move implements Localizable { * @returns Whether this Move will hit the target's Substitute (assuming one exists). */ 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)) { + if ( + [MoveTarget.USER, MoveTarget.USER_SIDE, MoveTarget.ENEMY_SIDE, MoveTarget.BOTH_SIDES].includes(this.moveTarget) + || !target?.getTag(BattlerTagType.SUBSTITUTE) + ) { return false; } const bypassed = new BooleanHolder(false); // TODO: Allow this to be simulated - applyAbAttrs("InfiltratorAbAttr", {pokemon: user, bypassed}); + applyAbAttrs("InfiltratorAbAttr", { pokemon: user, bypassed }); - return !bypassed.value - && !this.hasFlag(MoveFlags.SOUND_BASED) - && !this.hasFlag(MoveFlags.IGNORE_SUBSTITUTE); + return !bypassed.value && !this.hasFlag(MoveFlags.SOUND_BASED) && !this.hasFlag(MoveFlags.IGNORE_SUBSTITUTE); } /** @@ -467,7 +491,12 @@ export abstract class Move implements Localizable { * @param conditionSeq - The sequence number where the failure check occurs; default `4` * @returns `this` for method chaining */ - public restriction(restriction: UserMoveConditionFunc, i18nkey: string, alsoCondition?: boolean, conditionSeq?: number): this; + public restriction( + restriction: UserMoveConditionFunc, + i18nkey: string, + alsoCondition?: boolean, + conditionSeq?: number, + ): this; /** * Adds a restriction condition to this move. * The move will not be selectable if at least 1 of its restrictions is met. @@ -499,7 +528,7 @@ export abstract class Move implements Localizable { break; default: conditionArray = this.conditions; - } + } conditionArray.push(new MoveCondition((user, _, move) => !restriction(user, move))); } @@ -563,7 +592,7 @@ export abstract class Move implements Localizable { * @param setFlag - Whether the move should make contact; default `true` * @returns `this` */ - makesContact(setFlag: boolean = true): this { + makesContact(setFlag = true): this { this.setFlag(MoveFlags.MAKES_CONTACT, setFlag); return this; } @@ -786,7 +815,12 @@ export abstract class Move implements Localizable { * @returns boolean * @see {@linkcode hasFlag} */ - doesFlagEffectApply({ flag, user, target, isFollowUp = false }: { + doesFlagEffectApply({ + flag, + user, + target, + isFollowUp = false, + }: { flag: MoveFlags; user: Pokemon; target?: Pokemon; @@ -802,7 +836,7 @@ export abstract class Move implements Localizable { case MoveFlags.IGNORE_ABILITIES: if (user.hasAbilityWithAttr("MoveAbilityBypassAbAttr")) { const abilityEffectsIgnored = new BooleanHolder(false); - applyAbAttrs("MoveAbilityBypassAbAttr", {pokemon: user, cancelled: abilityEffectsIgnored, move: this}); + applyAbAttrs("MoveAbilityBypassAbAttr", { pokemon: user, cancelled: abilityEffectsIgnored, move: this }); if (abilityEffectsIgnored.value) { return true; } @@ -811,18 +845,22 @@ export abstract class Move implements Localizable { } return this.hasFlag(MoveFlags.IGNORE_ABILITIES) && !isFollowUp; case MoveFlags.IGNORE_PROTECT: - if (user.hasAbilityWithAttr("IgnoreProtectOnContactAbAttr") - && this.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user })) { + if ( + user.hasAbilityWithAttr("IgnoreProtectOnContactAbAttr") + && this.doesFlagEffectApply({ flag: MoveFlags.MAKES_CONTACT, user }) + ) { 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"))) + target?.getTag(SemiInvulnerableTag) + || !( + target?.getTag(BattlerTagType.MAGIC_COAT) + || (!this.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user, target }) + && target?.hasAbilityWithAttr("ReflectStatusMoveAbAttr")) + ) ) { return false; } @@ -840,7 +878,7 @@ export abstract class Move implements Localizable { * @param sequence - The sequence number where the condition check occurs, or `-1` to check all; defaults to 4. Pass -1 to check all * @returns boolean: false if any of the apply()'s return false, else true */ - applyConditions(user: Pokemon, target: Pokemon, sequence: -1 | 2 | 3 | 4 = 4): boolean { + applyConditions(user: Pokemon, target: Pokemon, sequence: -1 | 2 | 3 | 4 = 4): boolean { let conditionsArray: MoveCondition[]; switch (sequence) { case -1: @@ -859,7 +897,6 @@ export abstract class Move implements Localizable { return conditionsArray.every(cond => cond.apply(user, target, this)); } - /** * Determine whether the move is restricted from being selected due to its own requirements. * @@ -933,7 +970,9 @@ export abstract class Move implements Localizable { for (const attr of this.attrs) { // conditionals to check if the move is self targeting (if so then you are applying the move to yourself, not the target) - score += attr.getTargetBenefitScore(user, !attr.selfTarget ? target : user, move) * (target !== user && attr.selfTarget ? -1 : 1); + score += + attr.getTargetBenefitScore(user, attr.selfTarget ? user : target, move) + * (target !== user && attr.selfTarget ? -1 : 1); } return score; @@ -946,11 +985,17 @@ export abstract class Move implements Localizable { * @param target {@linkcode Pokemon} The Pokémon being targeted by the move. * @returns The calculated accuracy of the move. */ - calculateBattleAccuracy(user: Pokemon, target: Pokemon, simulated: boolean = false) { + calculateBattleAccuracy(user: Pokemon, target: Pokemon, simulated = false) { const moveAccuracy = new NumberHolder(this.accuracy); applyMoveAttrs("VariableAccuracyAttr", user, target, this, moveAccuracy); - applyAbAttrs("WonderSkinAbAttr", {pokemon: target, opponent: user, move: this, simulated, accuracy: moveAccuracy}); + applyAbAttrs("WonderSkinAbAttr", { + pokemon: target, + opponent: user, + move: this, + simulated, + accuracy: moveAccuracy, + }); if (moveAccuracy.value === -1) { return moveAccuracy.value; @@ -984,7 +1029,7 @@ export abstract class Move implements Localizable { * @param target {@linkcode Pokemon} The Pokémon being targeted by the move. * @returns The calculated power of the move. */ - calculateBattlePower(source: Pokemon, target: Pokemon, simulated: boolean = false): number { + calculateBattlePower(source: Pokemon, target: Pokemon, simulated = false): number { if (this.category === MoveCategory.STATUS) { return -1; } @@ -996,7 +1041,14 @@ export abstract class Move implements Localizable { const typeChangeMovePowerMultiplier = new NumberHolder(1); const typeChangeHolder = new NumberHolder(this.type); - applyAbAttrs("MoveTypeChangeAbAttr", {pokemon: source, opponent: target, move: this, simulated: true, moveType: typeChangeHolder, power: typeChangeMovePowerMultiplier}); + applyAbAttrs("MoveTypeChangeAbAttr", { + pokemon: source, + opponent: target, + move: this, + simulated: true, + moveType: typeChangeHolder, + power: typeChangeMovePowerMultiplier, + }); const abAttrParams: PreAttackModifyPowerAbAttrParams = { pokemon: source, @@ -1004,46 +1056,54 @@ export abstract class Move implements Localizable { simulated, power, move: this, - } + }; applyAbAttrs("VariableMovePowerAbAttr", abAttrParams); const ally = source.getAlly(); if (ally != null) { - applyAbAttrs("AllyMoveCategoryPowerBoostAbAttr", {...abAttrParams, pokemon: ally}); + applyAbAttrs("AllyMoveCategoryPowerBoostAbAttr", { ...abAttrParams, pokemon: ally }); } // Non-priority, single-hit moves of the user's Tera Type are always a bare minimum of 60 power const sourceTeraType = source.getTeraType(); - if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr("MultiHitAttr") && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) { + if ( + source.isTerastallized + && sourceTeraType === this.type + && power.value < 60 + && this.priority <= 0 + && !this.hasAttr("MultiHitAttr") + && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id) + ) { power.value = 60; } const fieldAuras = new Set( - globalScene.getField(true) - .map((p) => p.getAbilityAttrs("FieldMoveTypePowerBoostAbAttr").filter(attr => { + globalScene.getField(true).flatMap(p => + p.getAbilityAttrs("FieldMoveTypePowerBoostAbAttr").filter(attr => { const condition = attr.getCondition(); - return (!condition || condition(p)); - })) - .flat(), + return !condition || condition(p); + }), + ), ); for (const aura of fieldAuras) { // TODO: Refactor the fieldAura attribute so that its apply method is not directly called - aura.apply({pokemon: source, simulated, opponent: target, move: this, power}); + aura.apply({ pokemon: source, simulated, opponent: target, move: this, power }); } for (const p of source.getAlliesGenerator()) { - applyAbAttrs("UserFieldMoveTypePowerBoostAbAttr", {pokemon: p, opponent: target, move: this, simulated, power}); + applyAbAttrs("UserFieldMoveTypePowerBoostAbAttr", { pokemon: p, opponent: target, move: this, simulated, power }); } power.value *= typeChangeMovePowerMultiplier.value; - const typeBoost = source.findTag(t => t instanceof TypeBoostTag && t.boostedType === typeChangeHolder.value) as TypeBoostTag; + const typeBoost = source.findTag( + t => t instanceof TypeBoostTag && t.boostedType === typeChangeHolder.value, + ) as TypeBoostTag; if (typeBoost) { power.value *= typeBoost.boostValue; } - if (!this.hasAttr("TypelessAttr")) { globalScene.arena.applyTags(WeakenMoveTypeTag, typeChangeHolder.value, power); globalScene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, typeChangeHolder.value, power); @@ -1058,10 +1118,10 @@ export abstract class Move implements Localizable { return power.value; } - getPriority(user: Pokemon, simulated: boolean = true) { + getPriority(user: Pokemon, simulated = true) { const priority = new NumberHolder(this.priority); applyMoveAttrs("IncrementMovePriorityAttr", user, null, this, priority); - applyAbAttrs("ChangeMovePriorityAbAttr", {pokemon: user, simulated, move: this, priority}); + applyAbAttrs("ChangeMovePriorityAbAttr", { pokemon: user, simulated, move: this, priority }); return priority.value; } @@ -1071,7 +1131,12 @@ export abstract class Move implements Localizable { return MovePriorityInBracket.FIRST; } const modifierHolder = new NumberHolder(MovePriorityInBracket.NORMAL); - applyAbAttrs("ChangeMovePriorityInBracketAbAttr", { pokemon: user, simulated, move: this, priority: modifierHolder }); + applyAbAttrs("ChangeMovePriorityInBracketAbAttr", { + pokemon: user, + simulated, + move: this, + priority: modifierHolder, + }); return modifierHolder.value; } @@ -1120,48 +1185,39 @@ export abstract class Move implements Localizable { * Currently used for {@link https://bulbapedia.bulbagarden.net/wiki/Parental_Bond_(Ability) | Parental Bond} * and {@linkcode PokemonMultiHitModifier | Multi Lens}. * @param user - The {@linkcode Pokemon} using the move - * @param restrictSpread - Whether the enhancing effect should ignore multi-target moves; default `false` + * @param restrictSpread - (Default `false`) Whether the enhancing effect should ignore multi-target moves + * @param target - (Optional) The targeted pokemon, used for Pollen Puff * @returns Whether this Move can be given additional strikes. */ // TODO: Remove target parameter used solely to circumvent Pollen Puff shenanigans - the entire move needs to be fixed anyhow - public canBeMultiStrikeEnhanced(user: Pokemon, restrictSpread: boolean = false, target?: Pokemon | null): boolean { - // Multi-strike enhancers... - - // ...cannot enhance charging or 2-turn moves + public canBeMultiStrikeEnhanced(user: Pokemon, restrictSpread = false, target?: Pokemon | null): boolean { if (this.isChargingMove()) { return false; } - - // ...cannot enhance moves hitting multiple targets unless specified + const { targets, multiple } = getMoveTargets(user, this.id); if (restrictSpread && multiple && targets.length > 1) { return false; - }; + } - // ...cannot enhance status moves, including ally-targeting Pollen Puff if ( - this.category === MoveCategory.STATUS - || (target != null && user.getMoveCategory(target, this) === MoveCategory.STATUS)) { + this.category === MoveCategory.STATUS + || (target != null && user.getMoveCategory(target, this) === MoveCategory.STATUS) + ) { return false; } - // ...cannot enhance multi-hit or sacrificial moves - const exceptAttrs: MoveAttrString[] = [ - "MultiHitAttr", - "SacrificialAttr", - "SacrificialAttrOnHit" - ]; + const exceptAttrs: readonly MoveAttrString[] = ["MultiHitAttr", "SacrificialAttr", "SacrificialAttrOnHit"]; if (exceptAttrs.some(attr => this.hasAttr(attr))) { return false; } - // ...and cannot enhance these specific moves - const exceptMoves: MoveId[] = [ + const exceptMoves: readonly MoveId[] = [ MoveId.FLING, MoveId.UPROAR, MoveId.ROLLOUT, MoveId.ICE_BALL, - MoveId.ENDEAVOR + MoveId.ENDEAVOR, ]; if (exceptMoves.includes(this.id)) { return false; @@ -1172,14 +1228,23 @@ export abstract class Move implements Localizable { } export class AttackMove extends Move { - /** This field does not exist at runtime and must not be used. + /** + * This field does not exist at runtime and must not be used. * Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called. */ - declare private _: never; - override is(moveKind: K): this is MoveClassMap[K] { - return moveKind === "AttackMove"; - } - constructor(id: MoveId, type: PokemonType, category: MoveCategory, power: number, accuracy: number, pp: number, chance: number, priority: number, generation: number) { + private declare _: never; + + constructor( + id: MoveId, + type: PokemonType, + category: MoveCategory, + power: number, + accuracy: number, + pp: number, + chance: number, + priority: number, + generation: number, + ) { super(id, type, category, MoveTarget.NEAR_OTHER, power, accuracy, pp, chance, priority, generation); // > All damaging Fire-type moves can... thaw a frozen target, regardless of whether or not they have a chance to burn. @@ -1189,6 +1254,10 @@ export class AttackMove extends Move { } } + public override is(moveKind: K): this is MoveClassMap[K] { + return moveKind === "AttackMove"; + } + /** * Compute the benefit score of this move based on the offensive stat used and the move's power. * @param user The Pokemon using the move @@ -1203,7 +1272,8 @@ 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 [thisStat, offStat]: EffectiveStat[] = + this.category === MoveCategory.PHYSICAL ? [Stat.ATK, Stat.SPATK] : [Stat.SPATK, Stat.ATK]; const statHolder = new NumberHolder(user.getEffectiveStat(thisStat, target)); const offStatValue = user.getEffectiveStat(offStat, target); applyMoveAttrs("VariableAtkAttr", user, target, move, statHolder); @@ -1227,8 +1297,16 @@ export class StatusMove extends Move { /** This field does not exist at runtime and must not be used. * Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called. */ - declare private _: never; - constructor(id: MoveId, type: PokemonType, accuracy: number, pp: number, chance: number, priority: number, generation: number) { + private declare _: never; + constructor( + id: MoveId, + type: PokemonType, + accuracy: number, + pp: number, + chance: number, + priority: number, + generation: number, + ) { super(id, type, MoveCategory.STATUS, MoveTarget.NEAR_OTHER, -1, accuracy, pp, chance, priority, generation); } @@ -1241,8 +1319,16 @@ export class SelfStatusMove extends Move { /** This field does not exist at runtime and must not be used. * Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called. */ - declare private _: never; - constructor(id: MoveId, type: PokemonType, accuracy: number, pp: number, chance: number, priority: number, generation: number) { + private declare _: never; + constructor( + id: MoveId, + type: PokemonType, + accuracy: number, + pp: number, + chance: number, + priority: number, + generation: number, + ) { super(id, type, MoveCategory.STATUS, MoveTarget.USER, -1, accuracy, pp, chance, priority, generation); } @@ -1251,9 +1337,9 @@ export class SelfStatusMove extends Move { } } -type SubMove = AbstractConstructor +type SubMove = AbstractConstructor; -function ChargeMove(Base: TBase, nameAppend: string) { +function ChargeMove(Base: TBase, _nameAppend: string) { // NB: This cannot be made into a oneline return abstract class Charging extends Base { /** The animation to play during the move's charging phase */ @@ -1269,9 +1355,9 @@ function ChargeMove(Base: TBase, nameAppend: string) { } /** - * Sets the text to be displayed during this move's charging phase. - * References to the user Pokemon should be written as "{USER}", and - * references to the target Pokemon should be written as "{TARGET}". + * Sets the text to be displayed during this move's charging phase. \ + * References to the user Pokemon should be written as `"{USER}"`, and + * references to the target Pokemon should be written as `"{TARGET}"`. * @param chargeText the text to set * @returns this {@linkcode Move} (for chaining API purposes) */ @@ -1286,9 +1372,10 @@ function ChargeMove(Base: TBase, nameAppend: string) { * @param target the {@linkcode Pokemon} targeted by this move (optional) */ showChargeText(user: Pokemon, target?: Pokemon): void { - globalScene.phaseManager.queueMessage(this._chargeText - .replace("{USER}", getPokemonNameWithAffix(user)) - .replace("{TARGET}", getPokemonNameWithAffix(target)) + globalScene.phaseManager.queueMessage( + this._chargeText + .replace("{USER}", getPokemonNameWithAffix(user)) + .replace("{TARGET}", getPokemonNameWithAffix(target)), ); } @@ -1298,7 +1385,7 @@ function ChargeMove(Base: TBase, nameAppend: string) { * @returns Array of attributes that match `attrType`, or an empty array if * no matches are found. */ - getChargeAttrs(attrType: T): (MoveAttrMap[T])[] { + getChargeAttrs(attrType: T): MoveAttrMap[T][] { const targetAttr = MoveAttrs[attrType]; if (!targetAttr) { return []; @@ -1316,7 +1403,7 @@ function ChargeMove(Base: TBase, nameAppend: string) { if (!targetAttr) { return false; } - return this.chargeAttrs.some((attr) => attr instanceof targetAttr); + return this.chargeAttrs.some(attr => attr instanceof targetAttr); } /** @@ -1331,7 +1418,7 @@ function ChargeMove(Base: TBase, nameAppend: string) { return this; } - }; + } return Charging; } @@ -1359,7 +1446,7 @@ export abstract class MoveAttr { return this instanceof targetAttr; } - constructor(selfTarget: boolean = false) { + constructor(selfTarget = false) { this.selfTarget = selfTarget; } @@ -1373,7 +1460,7 @@ export abstract class MoveAttr { * @param args Set of unique arguments needed by this attribute * @returns true if application of the ability succeeds */ - apply(user: Pokemon | null, target: Pokemon | null, move: Move, args: any[]): boolean { + apply(_user: Pokemon | null, _target: Pokemon | null, _move: Move, _args: any[]): boolean { return true; } @@ -1393,7 +1480,7 @@ export abstract class MoveAttr { * @param move {@linkcode Move} with this attribute * @returns the string representing failure of this {@linkcode Move} */ - getFailedText(user: Pokemon, target: Pokemon, move: Move): string | undefined { + getFailedText(_user: Pokemon, _target: Pokemon, _move: Move): string | undefined { return; } @@ -1402,7 +1489,7 @@ export abstract class MoveAttr { * @see {@linkcode EnemyPokemon.getNextMove} * @virtual */ - getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getUserBenefitScore(_user: Pokemon, _target: Pokemon, _move: Move): number { return 0; } @@ -1411,7 +1498,7 @@ export abstract class MoveAttr { * @see {@linkcode EnemyPokemon.getNextMove} * @virtual */ - getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getTargetBenefitScore(_user: Pokemon, _target: Pokemon, _move: Move): number { return 0; } } @@ -1451,7 +1538,7 @@ export class MoveEffectAttr extends MoveAttr { * Defines when this effect should trigger in the move's effect order. * @defaultValue {@linkcode MoveEffectTrigger.POST_APPLY} */ - public get trigger () { + public get trigger() { return this.options?.trigger ?? MoveEffectTrigger.POST_APPLY; } @@ -1460,7 +1547,7 @@ export class MoveEffectAttr extends MoveAttr { * multi-hit moves. * @defaultValue `false` */ - public get firstHitOnly () { + public get firstHitOnly() { return this.options?.firstHitOnly ?? false; } @@ -1469,7 +1556,7 @@ export class MoveEffectAttr extends MoveAttr { * multi-hit moves. * @defaultValue `false` */ - public get lastHitOnly () { + public get lastHitOnly() { return this.options?.lastHitOnly ?? false; } @@ -1478,7 +1565,7 @@ export class MoveEffectAttr extends MoveAttr { * for the first time when targeting multiple {@linkcode Pokemon}. * @defaultValue `false` */ - public get firstTargetOnly () { + public get firstTargetOnly() { return this.options?.firstTargetOnly ?? false; } @@ -1486,7 +1573,7 @@ export class MoveEffectAttr extends MoveAttr { * If defined, overrides the move's base chance for this * secondary effect to trigger. */ - public get effectChanceOverride () { + public get effectChanceOverride() { return this.options?.effectChanceOverride; } @@ -1498,13 +1585,13 @@ export class MoveEffectAttr extends MoveAttr { * @param user - The {@linkcode Pokemon} using the move * @param target - The {@linkcode Pokemon} being targeted by the move, or {@linkcode user} if the move is * {@linkcode selfTarget | self-targeting} - * @param move - The {@linkcode Move} being used + * @param _move - The {@linkcode Move} being used * @param _args - Set of unique arguments needed by this attribute * @returns `true` if basic application of this `MoveAttr`s effects should be possible */ // TODO: Decouple this check from the `apply` step // TODO: Make non-damaging moves fail by default if none of their attributes can apply - canApply(user: Pokemon, target: Pokemon, move: Move, _args?: any[]) { + canApply(user: Pokemon, target: Pokemon, _move: Move, _args?: any[]) { return !(this.selfTarget ? user : target).isFainted(); } @@ -1522,10 +1609,15 @@ export class MoveEffectAttr extends MoveAttr { * @param selfEffect `true` if move targets user. * @returns Move effect chance value. */ - getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): number { + getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: boolean, showAbility?: boolean): number { const moveChance = new NumberHolder(this.effectChanceOverride ?? move.chance); - applyAbAttrs("MoveEffectChanceMultiplierAbAttr", {pokemon: user, simulated: !showAbility, chance: moveChance, move}); + applyAbAttrs("MoveEffectChanceMultiplierAbAttr", { + pokemon: user, + simulated: !showAbility, + chance: moveChance, + move, + }); if ((!move.hasAttr("FlinchAttr") || moveChance.value <= move.chance) && !move.hasAttr("SecretPowerAttr")) { const userSide = user.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; @@ -1533,7 +1625,7 @@ export class MoveEffectAttr extends MoveAttr { } if (!selfEffect) { - applyAbAttrs("IgnoreMoveEffectsAbAttr", {pokemon: target, move, simulated: !showAbility, chance: moveChance}); + applyAbAttrs("IgnoreMoveEffectsAbAttr", { pokemon: target, move, simulated: !showAbility, chance: moveChance }); } return moveChance.value; } @@ -1556,7 +1648,7 @@ export class MoveHeaderAttr extends MoveAttr { */ export class MessageHeaderAttr extends MoveHeaderAttr { /** The message to display, or a function producing one. */ - private message: string | MoveMessageFunc; + private readonly message: string | MoveMessageFunc; constructor(message: string | MoveMessageFunc) { super(); @@ -1564,9 +1656,7 @@ export class MessageHeaderAttr extends MoveHeaderAttr { } apply(user: Pokemon, target: Pokemon, move: Move): boolean { - const message = typeof this.message === "string" - ? this.message - : this.message(user, target, move); + const message = typeof this.message === "string" ? this.message : this.message(user, target, move); if (message) { globalScene.phaseManager.queueMessage(message); @@ -1581,14 +1671,14 @@ export class MessageHeaderAttr extends MoveHeaderAttr { * @see {@linkcode MoveHeaderAttr} */ export class AddBattlerTagHeaderAttr extends MoveHeaderAttr { - private tagType: BattlerTagType; + private readonly tagType: BattlerTagType; constructor(tagType: BattlerTagType) { super(); this.tagType = tagType; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { user.addTag(this.tagType); return true; } @@ -1614,7 +1704,7 @@ export class BeakBlastHeaderAttr extends AddBattlerTagHeaderAttr { */ export class PreMoveMessageAttr extends MoveAttr { /** The message to display or a function returning one */ - private message: string | MoveMessageFunc; + private readonly message: string | MoveMessageFunc; /** * Create a new {@linkcode PreMoveMessageAttr} to display a message before move execution. @@ -1629,9 +1719,7 @@ export class PreMoveMessageAttr extends MoveAttr { } apply(user: Pokemon, target: Pokemon, move: Move): boolean { - const message = typeof this.message === "function" - ? this.message(user, target, move) - : this.message; + const message = typeof this.message === "function" ? this.message(user, target, move) : this.message; // TODO: Consider changing if/when MoveAttr `apply` return values become significant if (message) { @@ -1674,8 +1762,8 @@ export class PreUseInterruptAttr extends MoveAttr { } currentPhase.cancel(); globalScene.phaseManager.queueMessage( - typeof this.message === "string" ? this.message : this.message(user, target, move) - ) + typeof this.message === "string" ? this.message : this.message(user, target, move), + ); return true; } @@ -1687,9 +1775,7 @@ export class PreUseInterruptAttr extends MoveAttr { */ override getFailedText(user: Pokemon, target: Pokemon, move: Move): string | undefined { if (this.message && this.conditionFunc(user, target, move)) { - return typeof this.message === "string" - ? this.message - : this.message(user, target, move); + return typeof this.message === "string" ? this.message : this.message(user, target, move); } } } @@ -1698,10 +1784,10 @@ export class PreUseInterruptAttr extends MoveAttr { * Attribute for Status moves that take attack type effectiveness * into consideration (i.e. {@linkcode https://bulbapedia.bulbagarden.net/wiki/Thunder_Wave_(move) | Thunder Wave}) */ -export class RespectAttackTypeImmunityAttr extends MoveAttr { } +export class RespectAttackTypeImmunityAttr extends MoveAttr {} export class IgnoreOpponentStatStagesAttr extends MoveAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { (args[0] as BooleanHolder).value = true; return true; @@ -1709,31 +1795,31 @@ export class IgnoreOpponentStatStagesAttr extends MoveAttr { } export class HighCritAttr extends MoveAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { (args[0] as NumberHolder).value++; return true; } - getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getUserBenefitScore(_user: Pokemon, _target: Pokemon, _move: Move): number { return 3; } } export class CritOnlyAttr extends MoveAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { (args[0] as BooleanHolder).value = true; return true; } - getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getUserBenefitScore(_user: Pokemon, _target: Pokemon, _move: Move): number { return 5; } } export class FixedDamageAttr extends MoveAttr { - private damage: number; + private readonly damage: number; constructor(damage: number) { super(); @@ -1747,7 +1833,7 @@ export class FixedDamageAttr extends MoveAttr { return true; } - getDamage(user: Pokemon, target: Pokemon, move: Move): number { + getDamage(_user: Pokemon, _target: Pokemon, _move: Move): number { return this.damage; } } @@ -1757,7 +1843,7 @@ export class UserHpDamageAttr extends FixedDamageAttr { super(0); } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { (args[0] as NumberHolder).value = user.hp; return true; @@ -1774,9 +1860,13 @@ export class TargetHalfHpDamageAttr extends FixedDamageAttr { super(0); } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { // first, determine if the hit is coming from multi lens or not - const lensCount = user.getHeldItems().find(i => i instanceof PokemonMultiHitModifier)?.getStackCount() ?? 0; + 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 NumberHolder).value = toDmgValue(target.hp / 2); @@ -1785,6 +1875,11 @@ export class TargetHalfHpDamageAttr extends FixedDamageAttr { // figure out what hit # we're on switch (user.turnData.hitCount - user.turnData.hitsLeft) { + case lensCount + 1: + // parental bond added hit; calc damage as normal + (args[0] as NumberHolder).value = toDmgValue(target.hp / 2); + return true; + // biome-ignore lint/suspicious/noFallthroughSwitchClause: intentional? case 0: // first hit of move; update initialHp tracker this.initialHp = target.hp; @@ -1792,15 +1887,11 @@ export class TargetHalfHpDamageAttr extends FixedDamageAttr { // multi lens added hit; use initialHp tracker to ensure correct damage (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 NumberHolder).value = toDmgValue(target.hp / 2); - return true; } } - getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { - return target.getHpRatio() > 0.5 ? Math.floor(((target.getHpRatio() - 0.5) * -24) + 4) : -20; + getTargetBenefitScore(_user: Pokemon, target: Pokemon, _move: Move): number { + return target.getHpRatio() > 0.5 ? Math.floor((target.getHpRatio() - 0.5) * -24 + 4) : -20; } } @@ -1809,14 +1900,14 @@ export class MatchHpAttr extends FixedDamageAttr { super(0); } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { (args[0] as NumberHolder).value = target.hp - user.hp; return true; } getCondition(): MoveConditionFunc { - return (user, target, move) => user.hp <= target.hp; + return (user, target, _move) => user.hp <= target.hp; } // TODO @@ -1827,8 +1918,8 @@ export class MatchHpAttr extends FixedDamageAttr { export class CounterDamageAttr extends FixedDamageAttr { /** The damage category of counter attacks to process, or `undefined` for either */ - private moveFilter?: MoveDamageCategory; - private multiplier: number; + private readonly moveFilter?: MoveDamageCategory; + private readonly multiplier: number; /** * @param multiplier - The damage multiplier to apply to the total damage received @@ -1840,15 +1931,16 @@ export class CounterDamageAttr extends FixedDamageAttr { this.multiplier = multiplier; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const damage = user.turnData.attacksReceived.find(ar => { - const category = allMoves[ar.move].category; - return ( - category !== MoveCategory.STATUS - && !areAllies(user.getBattlerIndex(), ar.sourceBattlerIndex) - && (this.moveFilter === undefined || category === this.moveFilter) - ) - })?.damage ?? 0; + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { + const damage = + user.turnData.attacksReceived.find(ar => { + const category = allMoves[ar.move].category; + return ( + category !== MoveCategory.STATUS + && !areAllies(user.getBattlerIndex(), ar.sourceBattlerIndex) + && (this.moveFilter === undefined || category === this.moveFilter) + ); + })?.damage ?? 0; (args[0] as NumberHolder).value = toDmgValue(damage * this.multiplier); return true; } @@ -1858,8 +1950,8 @@ export class CounterDamageAttr extends FixedDamageAttr { * Attribute for counter-like moves to redirect the move to a different target */ export class CounterRedirectAttr extends MoveAttr { - declare private moveFilter?: MoveDamageCategory; - constructor(moveFilter? : MoveDamageCategory) { + private declare moveFilter?: MoveDamageCategory; + constructor(moveFilter?: MoveDamageCategory) { super(); if (moveFilter !== undefined) { this.moveFilter = moveFilter; @@ -1873,15 +1965,13 @@ export class CounterRedirectAttr extends MoveAttr { * @param move - The move being used * @param args - args[0] holds the battler index of the target that the move will be redirected to */ - override apply(user: Pokemon, target: Pokemon | null, move: Move, args: [NumberHolder, ...any[]]): boolean { + override apply(user: Pokemon, _target: Pokemon | null, _move: Move, args: [NumberHolder, ...any[]]): boolean { const desiredTarget = getCounterAttackTarget(user, this.moveFilter); if (desiredTarget !== null && desiredTarget !== BattlerIndex.ATTACKER) { // check if the target is still alive - if ( - globalScene.currentBattle.double && - !globalScene.getField()[desiredTarget]?.isActive(true) - ) { - const targetField = desiredTarget >= BattlerIndex.ENEMY ? globalScene.getEnemyField() : globalScene.getPlayerField(); + if (globalScene.currentBattle.double && !globalScene.getField()[desiredTarget]?.isActive(true)) { + const targetField = + desiredTarget >= BattlerIndex.ENEMY ? globalScene.getEnemyField() : globalScene.getPlayerField(); args[0].value = targetField.find(p => p.hp > 0)?.getBattlerIndex() ?? BattlerIndex.ATTACKER; } else { args[0].value = desiredTarget; @@ -1897,7 +1987,7 @@ export class LevelDamageAttr extends FixedDamageAttr { super(0); } - getDamage(user: Pokemon, target: Pokemon, move: Move): number { + getDamage(user: Pokemon, _target: Pokemon, _move: Move): number { return user.level; } } @@ -1907,7 +1997,7 @@ export class RandomLevelDamageAttr extends FixedDamageAttr { super(0); } - getDamage(user: Pokemon, target: Pokemon, move: Move): number { + getDamage(user: Pokemon, _target: Pokemon, _move: Move): number { return toDmgValue(user.level * (user.randBattleSeedIntRange(50, 150) * 0.01)); } } @@ -1920,17 +2010,17 @@ export class ModifiedDamageAttr extends MoveAttr { return true; } - getModifiedDamage(user: Pokemon, target: Pokemon, move: Move, damage: number): number { + getModifiedDamage(_user: Pokemon, _target: Pokemon, _move: Move, damage: number): number { return damage; } } export class SurviveDamageAttr extends ModifiedDamageAttr { - getModifiedDamage(user: Pokemon, target: Pokemon, move: Move, damage: number): number { + getModifiedDamage(_user: Pokemon, target: Pokemon, _move: Move, damage: number): number { return Math.min(damage, target.hp - 1); } - getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getUserBenefitScore(_user: Pokemon, target: Pokemon, _move: Move): number { return target.hp > 1 ? 0 : -20; } } @@ -1940,18 +2030,16 @@ export class SurviveDamageAttr extends ModifiedDamageAttr { */ export class MessageAttr extends MoveEffectAttr { /** The message to display, either as a string or a function returning one. */ - private message: string | MoveMessageFunc; + private readonly message: string | MoveMessageFunc; constructor(message: string | MoveMessageFunc, options?: MoveEffectAttrOptions) { // TODO: Do we need to respect `selfTarget` if we're just displaying text? - super(false, options) + super(false, options); this.message = message; } override apply(user: Pokemon, target: Pokemon, move: Move): boolean { - const message = typeof this.message === "function" - ? this.message(user, target, move) - : this.message; + const message = typeof this.message === "function" ? this.message(user, target, move) : this.message; // TODO: Consider changing if/when MoveAttr `apply` return values become significant if (message) { @@ -1963,11 +2051,11 @@ export class MessageAttr extends MoveEffectAttr { } export class RecoilAttr extends MoveEffectAttr { - private useHp: boolean; - private damageRatio: number; - private unblockable: boolean; + private readonly useHp: boolean; + private readonly damageRatio: number; + private readonly unblockable: boolean; - constructor(useHp: boolean = false, damageRatio: number = 0.25, unblockable: boolean = false) { + constructor(useHp = false, damageRatio = 0.25, unblockable = false) { super(true, { lastHitOnly: true }); this.useHp = useHp; @@ -1982,7 +2070,7 @@ export class RecoilAttr extends MoveEffectAttr { const cancelled = new BooleanHolder(false); if (!this.unblockable) { - const abAttrParams: AbAttrParamsWithCancel = {pokemon: user, cancelled}; + const abAttrParams: AbAttrParamsWithCancel = { pokemon: user, cancelled }; applyAbAttrs("BlockRecoilDamageAttr", abAttrParams); applyAbAttrs("BlockNonDirectDamageAbAttr", abAttrParams); } @@ -1992,11 +2080,14 @@ export class RecoilAttr extends MoveEffectAttr { } // Chloroblast and Struggle should not deal recoil damage if the move was not successful - if (this.useHp && [ MoveResult.FAIL, MoveResult.MISS ].includes(user.getLastXMoves(1)[0]?.result ?? MoveResult.FAIL)) { + if ( + this.useHp + && [MoveResult.FAIL, MoveResult.MISS].includes(user.getLastXMoves(1)[0]?.result ?? MoveResult.FAIL) + ) { return false; } - const damageValue = (!this.useHp ? user.turnData.totalDamageDealt : user.getMaxHp()) * this.damageRatio; + const damageValue = (this.useHp ? user.getMaxHp() : user.turnData.totalDamageDealt) * this.damageRatio; const minValue = user.turnData.totalDamageDealt ? 1 : 0; const recoilDamage = toDmgValue(damageValue, minValue); if (!recoilDamage) { @@ -2008,18 +2099,19 @@ export class RecoilAttr extends MoveEffectAttr { } user.damageAndUpdate(recoilDamage, { result: HitResult.INDIRECT, ignoreSegments: true }); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:hitWithRecoil", { pokemonName: getPokemonNameWithAffix(user) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:hitWithRecoil", { pokemonName: getPokemonNameWithAffix(user) }), + ); user.turnData.damageTaken += recoilDamage; return true; } - getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { - return Math.floor((move.power / 5) / -4); + getUserBenefitScore(_user: Pokemon, _target: Pokemon, move: Move): number { + return Math.floor(move.power / 5 / -4); } } - /** * Attribute used for moves which self KO the user regardless if the move hits a target */ @@ -2035,10 +2127,10 @@ export class SacrificialAttr extends MoveEffectAttr { * @param move {@linkcode Move} with this attribute * @param args N/A * @returns true if the function succeeds - **/ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + */ + apply(user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { user.damageAndUpdate(user.hp, { result: HitResult.INDIRECT, ignoreSegments: true }); - user.turnData.damageTaken += user.hp; + user.turnData.damageTaken += user.hp; return true; } @@ -2066,7 +2158,7 @@ export class SacrificialAttrOnHit extends MoveEffectAttr { * @param move {@linkcode Move} with this attribute * @param args N/A * @returns true if the function succeeds - **/ + */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { // If the move fails to hit a target, then the user does not faint and the function returns false if (!super.apply(user, target, move, args)) { @@ -2111,10 +2203,12 @@ export class HalfSacrificialAttr extends MoveEffectAttr { const cancelled = new BooleanHolder(false); // Check to see if the Pokemon has an ability that blocks non-direct damage - applyAbAttrs("BlockNonDirectDamageAbAttr", {pokemon: user, cancelled}); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon: user, cancelled }); if (!cancelled.value) { user.damageAndUpdate(toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT, ignoreSegments: true }); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", { pokemonName: getPokemonNameWithAffix(user) })); // Queue recoil message + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:cutHpPowerUpMove", { pokemonName: getPokemonNameWithAffix(user) }), + ); // Queue recoil message } return true; } @@ -2123,7 +2217,9 @@ export class HalfSacrificialAttr extends MoveEffectAttr { if (user.isBoss()) { return -10; } - return Math.ceil(((1 - user.getHpRatio() / 2) * 10 - 10) * (target.getAttackTypeEffectiveness(move.type, user) - 0.5)); + return Math.ceil( + ((1 - user.getHpRatio() / 2) * 10 - 10) * (target.getAttackTypeEffectiveness(move.type, user) - 0.5), + ); } } @@ -2132,9 +2228,9 @@ export class HalfSacrificialAttr extends MoveEffectAttr { */ export class AddSubstituteAttr extends MoveEffectAttr { /** The ratio of the user's max HP that is required to apply this effect */ - private hpCost: number; + private readonly hpCost: number; /** Whether the damage taken should be rounded up (Shed Tail rounds up) */ - private roundUp: boolean; + private readonly roundUp: boolean; constructor(hpCost: number, roundUp: boolean) { super(true); @@ -2156,13 +2252,15 @@ export class AddSubstituteAttr extends MoveEffectAttr { return false; } - const damageTaken = this.roundUp ? Math.ceil(user.getMaxHp() * this.hpCost) : Math.floor(user.getMaxHp() * this.hpCost); + const damageTaken = this.roundUp + ? Math.ceil(user.getMaxHp() * this.hpCost) + : Math.floor(user.getMaxHp() * this.hpCost); user.damageAndUpdate(damageTaken, { result: HitResult.INDIRECT, ignoreSegments: true, ignoreFaintPhase: true }); user.addTag(BattlerTagType.SUBSTITUTE, 0, move.id, user.id); return true; } - getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getUserBenefitScore(user: Pokemon, _target: Pokemon, _move: Move): number { if (user.isBoss()) { return -10; } @@ -2170,7 +2268,10 @@ export class AddSubstituteAttr extends MoveEffectAttr { } getCondition(): MoveConditionFunc { - return (user, _target, _move) => !user.getTag(SubstituteTag) && user.hp > (this.roundUp ? Math.ceil(user.getMaxHp() * this.hpCost) : Math.floor(user.getMaxHp() * this.hpCost)) && user.getMaxHp() > 1; + return (user, _target, _move) => + !user.getTag(SubstituteTag) + && user.hp > (this.roundUp ? Math.ceil(user.getMaxHp() * this.hpCost) : Math.floor(user.getMaxHp() * this.hpCost)) + && user.getMaxHp() > 1; } /** @@ -2181,7 +2282,8 @@ export class AddSubstituteAttr extends MoveEffectAttr { getFailedText(user: Pokemon, _target: Pokemon, _move: Move): string | undefined { if (user.getTag(SubstituteTag)) { return i18next.t("moveTriggers:substituteOnOverlap", { pokemonName: getPokemonNameWithAffix(user) }); - } else if (user.hp <= Math.floor(user.getMaxHp() / 4) || user.getMaxHp() === 1) { + } + if (user.hp <= Math.floor(user.getMaxHp() / 4) || user.getMaxHp() === 1) { return i18next.t("moveTriggers:substituteNotEnoughHp"); } } @@ -2191,14 +2293,16 @@ export class AddSubstituteAttr extends MoveEffectAttr { * Heals the user or target by {@linkcode healRatio} depending on the value of {@linkcode selfTarget} */ export class HealAttr extends MoveEffectAttr { - constructor( - /** The percentage of {@linkcode Stat.HP} to heal. */ - private healRatio: number, - /** Whether to display a healing animation when healing the target; default `false` */ - private showAnim = false, - selfTarget = true - ) { + /** The percentage of {@linkcode Stat.HP} to heal. */ + private readonly healRatio: number; + /** Whether to display a healing animation when healing the target; default `false` */ + private readonly showAnim: boolean; + + constructor(healRatio: number, showAnim = false, selfTarget = true) { super(selfTarget); + + this.healRatio = healRatio; + this.showAnim = showAnim; } override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { @@ -2211,12 +2315,18 @@ export class HealAttr extends MoveEffectAttr { * This heals the target and shows the appropriate message. */ protected addHealPhase(target: Pokemon, healRatio: number) { - globalScene.phaseManager.unshiftNew("PokemonHealPhase", target.getBattlerIndex(), - toDmgValue(target.getMaxHp() * healRatio), i18next.t("moveTriggers:healHp", { pokemonName: getPokemonNameWithAffix(target) }), true, !this.showAnim); + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + target.getBattlerIndex(), + toDmgValue(target.getMaxHp() * healRatio), + i18next.t("moveTriggers:healHp", { pokemonName: getPokemonNameWithAffix(target) }), + true, + !this.showAnim, + ); } override getTargetBenefitScore(user: Pokemon, target: Pokemon, _move: Move): number { - const score = ((1 - (this.selfTarget ? user : target).getHpRatio()) * 20) - this.healRatio * 10; + const score = (1 - (this.selfTarget ? user : target).getHpRatio()) * 20 - this.healRatio * 10; return Math.round(score / (1 - this.healRatio / 2)); } @@ -2232,9 +2342,11 @@ export class HealAttr extends MoveEffectAttr { // TOOD: Fix this in PR#6276 const phaseManager = globalScene.phaseManager; if (phaseManager.getCurrentPhase().is("MovePhase")) { - phaseManager.queueMessage(i18next.t("battle:hpIsFull", { - pokemonName: getPokemonNameWithAffix(healedPokemon), - })) + phaseManager.queueMessage( + i18next.t("battle:hpIsFull", { + pokemonName: getPokemonNameWithAffix(healedPokemon), + }), + ); } return false; } @@ -2247,7 +2359,7 @@ export class HealAttr extends MoveEffectAttr { * Used for {@linkcode MoveId.REST}. */ export class RestAttr extends HealAttr { - private duration: number; + private readonly duration: number; constructor(duration: number) { super(1, true); @@ -2255,24 +2367,29 @@ export class RestAttr extends HealAttr { } override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const wasSet = user.trySetStatus(StatusEffect.SLEEP, user, this.duration, null, true, true, - i18next.t("moveTriggers:restBecameHealthy", { - pokemonName: getPokemonNameWithAffix(user), - })); + const wasSet = user.trySetStatus( + StatusEffect.SLEEP, + user, + this.duration, + null, + true, + true, + i18next.t("moveTriggers:restBecameHealthy", { + pokemonName: getPokemonNameWithAffix(user), + }), + ); return wasSet && super.apply(user, target, move, args); } override addHealPhase(user: Pokemon): void { - globalScene.phaseManager.unshiftNew("PokemonHealPhase", user.getBattlerIndex(), user.getMaxHp(), null) + globalScene.phaseManager.unshiftNew("PokemonHealPhase", user.getBattlerIndex(), user.getMaxHp(), null); } // TODO: change after HealAttr is changed to fail move override getCondition(): MoveConditionFunc { return (user, target, move) => - super.canApply(user, target, move, []) - // Intentionally suppress messages here as we display generic fail msg - // TODO: This might have order-of-operation jank - && user.canSetStatus(StatusEffect.SLEEP, true, true, user) + super.canApply(user, target, move, []) // Intentionally suppress messages here as we display generic fail msg // TODO: This might have order-of-operation jank + && user.canSetStatus(StatusEffect.SLEEP, true, true, user); } } @@ -2281,9 +2398,9 @@ export class RestAttr extends HealAttr { */ export class PartyStatusCureAttr extends MoveEffectAttr { /** Message to display after using move */ - private message: string | null; + private readonly message: string | null; /** Skips mons with this ability, ie. Soundproof */ - private abilityCondition: AbilityId; + private readonly abilityCondition: AbilityId; constructor(message: string | null, abilityCondition: AbilityId) { super(); @@ -2293,10 +2410,10 @@ export class PartyStatusCureAttr extends MoveEffectAttr { } //The same as MoveEffectAttr.canApply, except it doesn't check for the target's HP. - canApply(user: Pokemon, target: Pokemon, move: Move, args: any[]) { + canApply(user: Pokemon, target: Pokemon, move: Move, _args: any[]) { const isTargetValid = - (this.selfTarget && user.hp && !user.getTag(BattlerTagType.FRENZY)) || - (!this.selfTarget && (!target.getTag(BattlerTagType.PROTECTED) || move.hasFlag(MoveFlags.IGNORE_PROTECT))); + (this.selfTarget && user.hp && !user.getTag(BattlerTagType.FRENZY)) + || (!this.selfTarget && (!target.getTag(BattlerTagType.PROTECTED) || move.hasFlag(MoveFlags.IGNORE_PROTECT))); return !!isTargetValid; } @@ -2305,7 +2422,9 @@ export class PartyStatusCureAttr extends MoveEffectAttr { return false; } const partyPokemon = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); - partyPokemon.forEach(p => this.cureStatus(p, user.id)); + for (const p of partyPokemon) { + this.cureStatus(p, user.id); + } if (this.message) { globalScene.phaseManager.queueMessage(this.message); @@ -2320,16 +2439,25 @@ export class PartyStatusCureAttr extends MoveEffectAttr { * @param userId The ID of the (move) {@linkcode Pokemon | user}. */ public cureStatus(pokemon: Pokemon, userId: number) { - if (!pokemon.isOnField() || pokemon.id === userId) { // user always cures its own status, regardless of ability + if (!pokemon.isOnField() || pokemon.id === userId) { + // user always cures its own status, regardless of ability pokemon.resetStatus(false); pokemon.updateInfo(); - } else if (!pokemon.hasAbility(this.abilityCondition)) { + } else if (pokemon.hasAbility(this.abilityCondition)) { + // TODO: Ability displays should be handled by the ability + globalScene.phaseManager.queueAbilityDisplay( + pokemon, + pokemon.getPassiveAbility()?.id === this.abilityCondition, + true, + ); + globalScene.phaseManager.queueAbilityDisplay( + pokemon, + pokemon.getPassiveAbility()?.id === this.abilityCondition, + false, + ); + } else { pokemon.resetStatus(); pokemon.updateInfo(); - } else { - // TODO: Ability displays should be handled by the ability - globalScene.phaseManager.queueAbilityDisplay(pokemon, pokemon.getPassiveAbility()?.id === this.abilityCondition, true); - globalScene.phaseManager.queueAbilityDisplay(pokemon, pokemon.getPassiveAbility()?.id === this.abilityCondition, false); } } } @@ -2353,23 +2481,25 @@ export class FlameBurstAttr extends MoveEffectAttr { * @param args - n/a * @returns A boolean indicating whether the effect was successfully applied. */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { const targetAlly = target.getAlly(); const cancelled = new BooleanHolder(false); if (targetAlly != null) { - applyAbAttrs("BlockNonDirectDamageAbAttr", {pokemon: targetAlly, cancelled}); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon: targetAlly, cancelled }); } if (cancelled.value || !targetAlly || targetAlly.switchOutStatus) { return false; } - targetAlly.damageAndUpdate(Math.max(1, Math.floor(1 / 16 * targetAlly.getMaxHp())), { result: HitResult.INDIRECT }); + targetAlly.damageAndUpdate(Math.max(1, Math.floor((1 / 16) * targetAlly.getMaxHp())), { + result: HitResult.INDIRECT, + }); return true; } - getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getTargetBenefitScore(_user: Pokemon, target: Pokemon, _move: Move): number { return target.getAlly() != null ? -5 : 0; } } @@ -2403,12 +2533,13 @@ export class SacrificialFullRestoreAttr extends SacrificialAttr { return true; } - getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getUserBenefitScore(_user: Pokemon, _target: Pokemon, _move: Move): number { return -20; } getCondition(): MoveConditionFunc { - return (user, _target, _move) => globalScene.getPlayerParty().filter(p => p.isActive()).length > globalScene.currentBattle.getBattlerCount(); + return (_user, _target, _move) => + globalScene.getPlayerParty().filter(p => p.isActive()).length > globalScene.currentBattle.getBattlerCount(); } } @@ -2432,7 +2563,7 @@ export class IgnoreWeatherTypeDebuffAttr extends MoveAttr { * @param args [0] {@linkcode NumberHolder} for arenaAttackTypeMultiplier * @returns true if the function succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { 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) { @@ -2447,7 +2578,7 @@ export abstract class WeatherHealAttr extends HealAttr { super(0.5); } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { let healRatio = 0.5; if (!globalScene.arena.weather?.isEffectSuppressed()) { const weatherType = globalScene.arena.weather?.weatherType || WeatherType.NONE; @@ -2496,13 +2627,19 @@ export class SandHealAttr extends WeatherHealAttr { */ export class BoostHealAttr extends HealAttr { /** Healing received when {@linkcode condition} is false */ - private normalHealRatio: number; + private readonly normalHealRatio: number; /** Healing received when {@linkcode condition} is true */ - private boostedHealRatio: number; + private readonly boostedHealRatio: number; /** The lambda expression to check against when boosting the healing value */ - private condition?: MoveConditionFunc; + private readonly condition?: MoveConditionFunc; - constructor(normalHealRatio: number = 0.5, boostedHealRatio: number = 2 / 3, showAnim?: boolean, selfTarget?: boolean, condition?: MoveConditionFunc) { + constructor( + normalHealRatio = 0.5, + boostedHealRatio: number = 2 / 3, + showAnim?: boolean, + selfTarget?: boolean, + condition?: MoveConditionFunc, + ) { super(normalHealRatio, showAnim, selfTarget); this.normalHealRatio = normalHealRatio; this.boostedHealRatio = boostedHealRatio; @@ -2516,8 +2653,10 @@ export class BoostHealAttr extends HealAttr { * @param args N/A * @returns true if the move was successful */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const healRatio: number = (this.condition ? this.condition(user, target, move) : false) ? this.boostedHealRatio : this.normalHealRatio; + apply(user: Pokemon, target: Pokemon, move: Move, _args: any[]): boolean { + const healRatio: number = (this.condition ? this.condition(user, target, move) : false) + ? this.boostedHealRatio + : this.normalHealRatio; this.addHealPhase(target, healRatio); return true; } @@ -2546,8 +2685,8 @@ export class HealOnAllyAttr extends HealAttr { */ // TODO: Make Strength Sap its own attribute that extends off of this one export class HitHealAttr extends MoveEffectAttr { - private healRatio: number; - private healStat: EffectiveStat | null; + private readonly healRatio: number; + private readonly healStat: EffectiveStat | null; constructor(healRatio?: number | null, healStat?: EffectiveStat) { super(true); @@ -2565,7 +2704,7 @@ export class HitHealAttr extends MoveEffectAttr { * @param args N/A * @returns true if the function succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { if (target.hasAbilityWithAttr("ReverseDrainAbAttr")) { return false; } @@ -2592,13 +2731,15 @@ export class HitHealAttr extends MoveEffectAttr { getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { if (this.healStat) { const healAmount = target.getEffectiveStat(this.healStat); - return Math.floor(Math.max(0, (Math.min(1, (healAmount + user.hp) / user.getMaxHp() - 0.33))) / user.getHpRatio()); + return Math.floor(Math.max(0, Math.min(1, (healAmount + user.hp) / user.getMaxHp() - 0.33)) / user.getHpRatio()); } - return Math.floor(Math.max((1 - user.getHpRatio()) - 0.33, 0) * (move.power / 4)); + return Math.floor(Math.max(1 - user.getHpRatio() - 0.33, 0) * (move.power / 4)); } public getHealAmount(user: Pokemon, target: Pokemon): number { - return (this.healStat) ? target.getEffectiveStat(this.healStat) : toDmgValue(user.turnData.singleHitDamageDealt * this.healRatio); + return this.healStat + ? target.getEffectiveStat(this.healStat) + : toDmgValue(user.turnData.singleHitDamageDealt * this.healRatio); } } @@ -2609,11 +2750,11 @@ export class HitHealAttr extends MoveEffectAttr { */ export class IncrementMovePriorityAttr extends MoveAttr { /** The condition for a move's priority being incremented */ - private moveIncrementFunc: (pokemon: Pokemon, target:Pokemon, move: Move) => boolean; + private readonly moveIncrementFunc: (pokemon: Pokemon, target: Pokemon, move: Move) => boolean; /** The amount to increment priority by, if condition passes. */ - private increaseAmount: number; + private readonly increaseAmount: number; - constructor(moveIncrementFunc: (pokemon: Pokemon, target:Pokemon, move: Move) => boolean, increaseAmount = 1) { + constructor(moveIncrementFunc: (pokemon: Pokemon, target: Pokemon, move: Move) => boolean, increaseAmount = 1) { super(); this.moveIncrementFunc = moveIncrementFunc; @@ -2681,7 +2822,7 @@ export class MultiHitAttr extends MoveAttr { return true; } - getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getTargetBenefitScore(_user: Pokemon, _target: Pokemon, _move: Move): number { return -5; } @@ -2693,22 +2834,22 @@ export class MultiHitAttr extends MoveAttr { * @param target {@linkcode Pokemon} targeted by the attack * @returns The number of hits this attack should deal */ - getHitCount(user: Pokemon, target: Pokemon): number { + getHitCount(user: Pokemon, _target: Pokemon): number { switch (this.multiHitType) { - case MultiHitType._2_TO_5: - { + case MultiHitType._2_TO_5: { const rand = user.randBattleSeedInt(20); const hitValue = new NumberHolder(rand); - applyAbAttrs("MaxMultiHitAbAttr", {pokemon: user, hits: hitValue}); + applyAbAttrs("MaxMultiHitAbAttr", { pokemon: user, hits: hitValue }); if (hitValue.value >= 13) { return 2; - } else if (hitValue.value >= 6) { - return 3; - } else if (hitValue.value >= 3) { - return 4; - } else { - return 5; } + if (hitValue.value >= 6) { + return 3; + } + if (hitValue.value >= 3) { + return 4; + } + return 5; } case MultiHitType._2: return 2; @@ -2716,12 +2857,16 @@ export class MultiHitAttr extends MoveAttr { return 3; case MultiHitType._10: return 10; - case MultiHitType.BEAT_UP: + case MultiHitType.BEAT_UP: { const party = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); // No status means the ally pokemon can contribute to Beat Up return party.reduce((total, pokemon) => { - return total + (pokemon.id === user.id ? 1 : pokemon?.status && pokemon.status.effect !== StatusEffect.NONE ? 0 : 1); + return ( + total + + (pokemon.id === user.id ? 1 : pokemon?.status && pokemon.status.effect !== StatusEffect.NONE ? 0 : 1) + ); }, 0); + } } } @@ -2734,7 +2879,14 @@ export class MultiHitAttr extends MoveAttr { * @param maxMultiHit - Whether the move should always hit the maximum number of times, e.g. due to {@linkcode AbilityId.SKILL_LINK | Skill Link} (default: `false`) * @param ignoreAcc - `true` if the move should ignore accuracy checks, e.g. due to {@linkcode AbilityId.NO_GUARD | No Guard} (default: `false`) */ - calculateExpectedHitCount(move: Move, { ignoreAcc = false, maxMultiHit = false, partySize = 1 }: {ignoreAcc?: boolean, maxMultiHit?: boolean, partySize?: number} = {}): number { + calculateExpectedHitCount( + move: Move, + { + ignoreAcc = false, + maxMultiHit = false, + partySize = 1, + }: { ignoreAcc?: boolean; maxMultiHit?: boolean; partySize?: number } = {}, + ): number { let expectedHits: number; switch (this.multiHitType) { case MultiHitType._2_TO_5: @@ -2760,22 +2912,26 @@ export class MultiHitAttr extends MoveAttr { const acc = move.accuracy / 100; if (move.hasFlag(MoveFlags.CHECK_ALL_HITS) && !maxMultiHit) { // N.B. No moves should be the _2_TO_5 variant and have the CHECK_ALL_HITS flag. - return acc * (1 - Math.pow(acc, expectedHits)) / (1 - acc); + return (acc * (1 - Math.pow(acc, expectedHits))) / (1 - acc); } - return expectedHits *= acc; + return (expectedHits *= acc); } } export class ChangeMultiHitTypeAttr extends MoveAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { //const hitType = args[0] as Utils.NumberHolder; return false; } } export class WaterShurikenMultiHitTypeAttr extends ChangeMultiHitTypeAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - if (user.species.speciesId === SpeciesId.GRENINJA && user.hasAbility(AbilityId.BATTLE_BOND) && user.formIndex === 2) { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { + if ( + user.species.speciesId === SpeciesId.GRENINJA + && user.hasAbility(AbilityId.BATTLE_BOND) + && user.formIndex === 2 + ) { (args[0] as NumberHolder).value = MultiHitType._3; return true; } @@ -2792,7 +2948,7 @@ export class StatusEffectAttr extends MoveEffectAttr { this.effect = effect; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, move: Move, _args: any[]): boolean { const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); const statusCheck = moveChance < 0 || moveChance === 100 || user.randBattleSeedInt(100) < moveChance; if (!statusCheck) { @@ -2802,10 +2958,8 @@ export class StatusEffectAttr extends MoveEffectAttr { // non-status moves don't play sound effects for failures const quiet = move.category !== MoveCategory.STATUS; - if ( - target.trySetStatus(this.effect, user, undefined, null, false, quiet) - ) { - applyAbAttrs("ConfusionOnStatusEffectAbAttr", {pokemon: user, opponent: target, move, effect: this.effect}); + if (target.trySetStatus(this.effect, user, undefined, null, false, quiet)) { + applyAbAttrs("ConfusionOnStatusEffectAbAttr", { pokemon: user, opponent: target, move, effect: this.effect }); return true; } return false; @@ -2840,7 +2994,7 @@ export class MultiStatusEffectAttr extends StatusEffectAttr { getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { const moveChance = this.getMoveChance(user, target, move, this.selfTarget, false); - const score = (moveChance < 0) ? -10 : Math.floor(moveChance * -0.1); + const score = moveChance < 0 ? -10 : Math.floor(moveChance * -0.1); const pokemon = this.selfTarget ? user : target; return !pokemon.status && pokemon.canSetStatus(this.effect, true, false, user) ? score : 0; @@ -2861,8 +3015,8 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr { * @returns - Whether the effect was successfully applied to the target. */ apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { - const statusToApply = user.status?.effect ?? - (user.hasAbility(AbilityId.COMATOSE) ? StatusEffect.SLEEP : StatusEffect.NONE); + const statusToApply = + user.status?.effect ?? (user.hasAbility(AbilityId.COMATOSE) ? StatusEffect.SLEEP : StatusEffect.NONE); // Bang is justified as condition func returns early if no status is found if (!target.trySetStatus(statusToApply, user)) { @@ -2872,7 +3026,7 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr { if (user.status) { // Add tag to user to heal its status effect after the move ends (unless we have comatose); // occurs after move use to ensure correct Synchronize timing - user.addTag(BattlerTagType.PSYCHO_SHIFT) + user.addTag(BattlerTagType.PSYCHO_SHIFT); } return true; @@ -2884,17 +3038,17 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr { return false; } - const statusToApply = user.status?.effect ?? (user.hasAbility(AbilityId.COMATOSE) ? StatusEffect.SLEEP : StatusEffect.NONE); + const statusToApply = + user.status?.effect ?? (user.hasAbility(AbilityId.COMATOSE) ? StatusEffect.SLEEP : StatusEffect.NONE); return !!statusToApply && target.canSetStatus(statusToApply, false, false, user); - } + }; } - getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getTargetBenefitScore(user: Pokemon, target: Pokemon, _move: Move): number { const statusToApply = - user.status?.effect ?? - (user.hasAbility(AbilityId.COMATOSE) ? StatusEffect.SLEEP : StatusEffect.NONE); + user.status?.effect ?? (user.hasAbility(AbilityId.COMATOSE) ? StatusEffect.SLEEP : StatusEffect.NONE); - // TODO: Give this a positive user benefit score + // TODO: Give this a positive user benefit score return !target.status?.effect && statusToApply && target.canSetStatus(statusToApply, true, false, user) ? -10 : 0; } } @@ -2904,49 +3058,63 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr { * Used for {@linkcode MoveId.THIEF} and {@linkcode MoveId.COVET}. */ export class StealHeldItemChanceAttr extends MoveEffectAttr { - private chance: number; + private readonly chance: number; constructor(chance: number) { super(false); this.chance = chance; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { const rand = randSeedFloat(); if (rand > this.chance) { return false; } - const heldItems = this.getTargetHeldItems(target).filter((i) => i.isTransferable); - if (!heldItems.length) { + const heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferable); + if (heldItems.length === 0) { return false; } - const poolType = target.isPlayer() ? ModifierPoolType.PLAYER : target.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD; - const highestItemTier = heldItems.map((m) => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is the bang after tier correct? - const tierHeldItems = heldItems.filter((m) => m.type.getOrInferTier(poolType) === highestItemTier); + const poolType = target.isPlayer() + ? ModifierPoolType.PLAYER + : target.hasTrainer() + ? ModifierPoolType.TRAINER + : ModifierPoolType.WILD; + const highestItemTier = heldItems + .map(m => m.type.getOrInferTier(poolType)) + .reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is the bang after tier correct? + const tierHeldItems = heldItems.filter(m => m.type.getOrInferTier(poolType) === highestItemTier); const stolenItem = tierHeldItems[user.randBattleSeedInt(tierHeldItems.length)]; if (!globalScene.tryTransferHeldItemModifier(stolenItem, user, false)) { return false; } - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:stoleItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: stolenItem.type.name })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:stoleItem", { + pokemonName: getPokemonNameWithAffix(user), + targetName: getPokemonNameWithAffix(target), + itemName: stolenItem.type.name, + }), + ); return true; } getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] { - return globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier - && m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier[]; + return globalScene.findModifiers( + m => m instanceof PokemonHeldItemModifier && m.pokemonId === target.id, + target.isPlayer(), + ) as PokemonHeldItemModifier[]; } - getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getUserBenefitScore(_user: Pokemon, target: Pokemon, _move: Move): number { const heldItems = this.getTargetHeldItems(target); - return heldItems.length ? 5 : 0; + return heldItems.length > 0 ? 5 : 0; } - getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getTargetBenefitScore(_user: Pokemon, target: Pokemon, _move: Move): number { const heldItems = this.getTargetHeldItems(target); - return heldItems.length ? -5 : 0; + return heldItems.length > 0 ? -5 : 0; } } @@ -2958,11 +3126,10 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr { * "If the Pokémon is knocked out by the attack, Sticky Hold does not protect the held item." */ export class RemoveHeldItemAttr extends MoveEffectAttr { - /** Optional restriction for item pool to berries only; i.e. Incinerate */ - private berriesOnly: boolean; + private readonly berriesOnly: boolean; - constructor(berriesOnly: boolean = false) { + constructor(berriesOnly = false) { super(false); this.berriesOnly = berriesOnly; } @@ -2975,15 +3142,16 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { * @param args N/A * @returns `true` if an item was able to be 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) + 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; } // Check for abilities that block item theft // TODO: This should not trigger if the target would faint beforehand const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockItemTheftAbAttr", {pokemon: target, cancelled}); + applyAbAttrs("BlockItemTheftAbAttr", { pokemon: target, cancelled }); if (cancelled.value) { return false; @@ -2997,7 +3165,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { heldItems = heldItems.filter(m => m instanceof BerryModifier && m.pokemonId === target.id, target.isPlayer()); } - if (!heldItems.length) { + if (heldItems.length === 0) { return false; } @@ -3008,27 +3176,41 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { globalScene.updateModifiers(target.isPlayer()); if (this.berriesOnly) { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:incineratedItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: removedItem.type.name })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:incineratedItem", { + pokemonName: getPokemonNameWithAffix(user), + targetName: getPokemonNameWithAffix(target), + itemName: removedItem.type.name, + }), + ); } else { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:knockedOffItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: removedItem.type.name })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:knockedOffItem", { + pokemonName: getPokemonNameWithAffix(user), + targetName: getPokemonNameWithAffix(target), + itemName: removedItem.type.name, + }), + ); } return true; } getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] { - return globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier - && m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier[]; + return globalScene.findModifiers( + m => m instanceof PokemonHeldItemModifier && m.pokemonId === target.id, + target.isPlayer(), + ) as PokemonHeldItemModifier[]; } - getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getUserBenefitScore(_user: Pokemon, target: Pokemon, _move: Move): number { const heldItems = this.getTargetHeldItems(target); - return heldItems.length ? 5 : 0; + return heldItems.length > 0 ? 5 : 0; } - getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getTargetBenefitScore(_user: Pokemon, target: Pokemon, _move: Move): number { const heldItems = this.getTargetHeldItems(target); - return heldItems.length ? -5 : 0; + return heldItems.length > 0 ? -5 : 0; } } @@ -3037,6 +3219,7 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { */ export class EatBerryAttr extends MoveEffectAttr { protected chosenBerry: BerryModifier; + // biome-ignore lint/complexity/noUselessConstructor: this removes the `options` param from the superclass constructor(selfTarget: boolean) { super(selfTarget); } @@ -3077,8 +3260,10 @@ export class EatBerryAttr extends MoveEffectAttr { } getTargetHeldBerries(target: Pokemon): BerryModifier[] { - return globalScene.findModifiers(m => m instanceof BerryModifier - && (m as BerryModifier).pokemonId === target.id, target.isPlayer()) as BerryModifier[]; + return globalScene.findModifiers( + m => m instanceof BerryModifier && (m as BerryModifier).pokemonId === target.id, + target.isPlayer(), + ) as BerryModifier[]; } reduceBerryModifier(target: Pokemon) { @@ -3088,7 +3273,6 @@ export class EatBerryAttr extends MoveEffectAttr { globalScene.updateModifiers(target.isPlayer()); } - /** * Internal function to apply berry effects. * @@ -3097,11 +3281,11 @@ export class EatBerryAttr extends MoveEffectAttr { * @param updateHarvest - Whether to prevent harvest from tracking berries; * defaults to whether `consumer` equals `berryOwner` (i.e. consuming own berry). */ - protected eatBerry(consumer: Pokemon, berryOwner: Pokemon = consumer, updateHarvest = consumer === berryOwner) { - // consumer eats berry, owner triggers unburden and similar effects + protected eatBerry(consumer: Pokemon, berryOwner: Pokemon = consumer, updateHarvest = consumer === berryOwner) { + // consumer eats berry, owner triggers unburden and similar effects getBerryEffectFunc(this.chosenBerry.berryType)(consumer); - applyAbAttrs("PostItemLostAbAttr", {pokemon: berryOwner}); - applyAbAttrs("HealFromBerryUseAbAttr", {pokemon: consumer}); + applyAbAttrs("PostItemLostAbAttr", { pokemon: berryOwner }); + applyAbAttrs("HealFromBerryUseAbAttr", { pokemon: consumer }); consumer.recordEatenBerry(this.chosenBerry.berryType, updateHarvest); } } @@ -3123,10 +3307,10 @@ export class StealEatBerryAttr extends EatBerryAttr { * @param args N/A * @returns `true` if the function succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { // check for abilities that block item theft const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockItemTheftAbAttr", {pokemon: target, cancelled}); + applyAbAttrs("BlockItemTheftAbAttr", { pokemon: target, cancelled }); if (cancelled.value === true) { return false; } @@ -3140,8 +3324,12 @@ export class StealEatBerryAttr extends EatBerryAttr { // pick a random berry and eat it this.chosenBerry = heldBerries[user.randBattleSeedInt(heldBerries.length)]; - applyAbAttrs("PostItemLostAbAttr", {pokemon: target}); - const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name }); + applyAbAttrs("PostItemLostAbAttr", { pokemon: target }); + const message = i18next.t("battle:stealEatBerry", { + pokemonName: user.name, + targetName: target.name, + berryName: this.chosenBerry.type.name, + }); globalScene.phaseManager.queueMessage(message); this.reduceBerryModifier(target); this.eatBerry(user, target); @@ -3179,13 +3367,19 @@ export class HealStatusEffectAttr extends MoveEffectAttr { // Special edge case for shield dust blocking Sparkling Aria curing burn const moveTargets = getMoveTargets(user, move.id); - if (target.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") && move.id === MoveId.SPARKLING_ARIA && moveTargets.targets.length === 1) { + if ( + target.hasAbilityWithAttr("IgnoreMoveEffectsAbAttr") + && move.id === MoveId.SPARKLING_ARIA + && moveTargets.targets.length === 1 + ) { return false; } const pokemon = this.selfTarget ? user : target; if (pokemon.status && this.effects.includes(pokemon.status.effect)) { - globalScene.phaseManager.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); + globalScene.phaseManager.queueMessage( + getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon)), + ); pokemon.resetStatus(); pokemon.updateInfo(); @@ -3199,7 +3393,7 @@ export class HealStatusEffectAttr extends MoveEffectAttr { return this.effects.includes(effect); } - getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getUserBenefitScore(user: Pokemon, _target: Pokemon, _move: Move): number { return user.status ? 10 : 0; } } @@ -3211,13 +3405,13 @@ export class HealStatusEffectAttr extends MoveEffectAttr { */ // TODO: Give this `userSleptOrComatoseCondition` by default export class BypassSleepAttr extends MoveAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: [BooleanHolder, ...any[]]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: [BooleanHolder, ...any[]]): boolean { const bypassSleep = args[0]; if (bypassSleep.value) { return false; } bypassSleep.value = true; - return true + return true; } /** @@ -3226,7 +3420,7 @@ export class BypassSleepAttr extends MoveAttr { * @param target * @param move */ - getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getUserBenefitScore(user: Pokemon, _target: Pokemon, _move: Move): number { return user.status?.effect === StatusEffect.SLEEP ? 200 : -10; } } @@ -3243,7 +3437,7 @@ export class BypassBurnDamageReductionAttr extends MoveAttr { * @param args [0] {@linkcode BooleanHolder} for burnDamageReductionCancelled * @returns true if the function succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { (args[0] as BooleanHolder).value = true; return true; @@ -3251,7 +3445,7 @@ export class BypassBurnDamageReductionAttr extends MoveAttr { } export class WeatherChangeAttr extends MoveEffectAttr { - private weatherType: WeatherType; + private readonly weatherType: WeatherType; constructor(weatherType: WeatherType) { super(); @@ -3259,17 +3453,19 @@ export class WeatherChangeAttr extends MoveEffectAttr { this.weatherType = weatherType; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { return globalScene.arena.trySetWeather(this.weatherType, user); } getCondition(): MoveConditionFunc { - return (user, target, move) => !globalScene.arena.weather || (globalScene.arena.weather.weatherType !== this.weatherType && !globalScene.arena.weather.isImmutable()); + return (_user, _target, _move) => + !globalScene.arena.weather + || (globalScene.arena.weather.weatherType !== this.weatherType && !globalScene.arena.weather.isImmutable()); } } export class ClearWeatherAttr extends MoveEffectAttr { - private weatherType: WeatherType; + private readonly weatherType: WeatherType; constructor(weatherType: WeatherType) { super(); @@ -3277,7 +3473,7 @@ export class ClearWeatherAttr extends MoveEffectAttr { this.weatherType = weatherType; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { if (globalScene.arena.weather?.weatherType === this.weatherType) { return globalScene.arena.trySetWeather(WeatherType.NONE, user); } @@ -3287,7 +3483,7 @@ export class ClearWeatherAttr extends MoveEffectAttr { } export class TerrainChangeAttr extends MoveEffectAttr { - private terrainType: TerrainType; + private readonly terrainType: TerrainType; constructor(terrainType: TerrainType) { super(); @@ -3295,32 +3491,29 @@ export class TerrainChangeAttr extends MoveEffectAttr { this.terrainType = terrainType; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { return globalScene.arena.trySetTerrain(this.terrainType, true, user); } getCondition(): MoveConditionFunc { - return (user, target, move) => !globalScene.arena.terrain || (globalScene.arena.terrain.terrainType !== this.terrainType); + return (_user, _target, _move) => + !globalScene.arena.terrain || globalScene.arena.terrain.terrainType !== this.terrainType; } - getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getUserBenefitScore(_user: Pokemon, _target: Pokemon, _move: Move): number { // TODO: Expand on this return globalScene.arena.terrain ? 0 : 6; } } export class ClearTerrainAttr extends MoveEffectAttr { - constructor() { - super(); - } - - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { return globalScene.arena.trySetTerrain(TerrainType.NONE, true, user); } } export class OneHitKOAttr extends MoveAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { if (target.isBossImmune()) { return false; } @@ -3331,9 +3524,9 @@ export class OneHitKOAttr extends MoveAttr { } getCondition(): MoveConditionFunc { - return (user, target, move) => { + return (user, target, _move) => { const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockOneHitKOAbAttr", {pokemon: target, cancelled}); + applyAbAttrs("BlockOneHitKOAbAttr", { pokemon: target, cancelled }); return !cancelled.value && user.level >= target.level; }; } @@ -3361,7 +3554,7 @@ export class InstantChargeAttr extends MoveAttr { * - `[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 { + override apply(user: Pokemon, _target: Pokemon | null, move: Move, args: any[]): boolean { const instantCharge = args[0]; if (!(instantCharge instanceof BooleanHolder)) { return false; @@ -3381,15 +3574,13 @@ export class InstantChargeAttr extends MoveAttr { */ export class WeatherInstantChargeAttr extends InstantChargeAttr { constructor(weatherTypes: WeatherType[]) { - super((user, move) => { + super((_user, _move) => { const currentWeather = globalScene.arena.weather; if (currentWeather?.weatherType == null) { return false; - } else { - return !currentWeather?.isEffectSuppressed() - && weatherTypes.includes(currentWeather?.weatherType); } + return !currentWeather?.isEffectSuppressed() && weatherTypes.includes(currentWeather?.weatherType); }); } } @@ -3401,7 +3592,7 @@ abstract class OverrideMoveEffectAttr extends MoveAttr { /** This field does not exist at runtime and must not be used. * Its sole purpose is to ensure that typescript is able to properly narrow when the `is` method is called. */ - declare private _: never; + private declare _: never; /** * Apply the move attribute to override other effects of this move. * @param user - The {@linkcode Pokemon} using the move @@ -3412,7 +3603,12 @@ abstract class OverrideMoveEffectAttr extends MoveAttr { * `[1]`: The {@linkcode MoveUseMode} dictating how this move was used. * @returns `true` if the move effect was successfully overridden. */ - public override apply(_user: Pokemon, _target: Pokemon, _move: Move, _args: [overridden: BooleanHolder, useMode: MoveUseMode]): boolean { + public override apply( + _user: Pokemon, + _target: Pokemon, + _move: Move, + _args: [overridden: BooleanHolder, useMode: MoveUseMode], + ): boolean { return true; } } @@ -3443,7 +3639,12 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { this.chargeKey = chargeKey; } - public override apply(user: Pokemon, target: Pokemon, move: Move, args: [overridden: BooleanHolder, useMode: MoveUseMode]): boolean { + public override apply( + user: Pokemon, + target: Pokemon, + move: Move, + args: [overridden: BooleanHolder, useMode: MoveUseMode], + ): boolean { const useMode = args[1]; if (useMode === MoveUseMode.DELAYED_ATTACK) { // don't trigger if already queueing an indirect attack @@ -3456,28 +3657,30 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { // Display the move animation to foresee an attack globalScene.phaseManager.unshiftNew("MoveAnimPhase", new MoveChargeAnim(this.chargeAnim, move.id, user)); - globalScene.phaseManager.queueMessage( - i18next.t( - this.chargeKey, - { pokemonName: getPokemonNameWithAffix(user) } - ) - ) + globalScene.phaseManager.queueMessage(i18next.t(this.chargeKey, { pokemonName: getPokemonNameWithAffix(user) })); - user.pushMoveHistory({move: move.id, targets: [target.getBattlerIndex()], result: MoveResult.OTHER, useMode, turn: globalScene.currentBattle.turn}) + user.pushMoveHistory({ + move: move.id, + targets: [target.getBattlerIndex()], + result: MoveResult.OTHER, + useMode, + turn: globalScene.currentBattle.turn, + }); // Queue up an attack on the given slot globalScene.arena.positionalTagManager.addTag({ tagType: PositionalTagType.DELAYED_ATTACK, sourceId: user.id, targetIndex: target.getBattlerIndex(), sourceMove: move.id, - turnCount: 3 - }) + turnCount: 3, + }); return true; } public override getCondition(): MoveConditionFunc { // Check the arena if another similar attack is active and affecting the same slot - return (_user, target) => globalScene.arena.positionalTagManager.canAddTag(PositionalTagType.DELAYED_ATTACK, target.getBattlerIndex()) + return (_user, target) => + globalScene.arena.positionalTagManager.canAddTag(PositionalTagType.DELAYED_ATTACK, target.getBattlerIndex()); } } @@ -3500,7 +3703,8 @@ export class WishAttr extends MoveEffectAttr { public override getCondition(): MoveConditionFunc { // Check the arena if another similar move is active and affecting the same slot - return (_user, target) => globalScene.arena.positionalTagManager.canAddTag(PositionalTagType.WISH, target.getBattlerIndex()) + return (_user, target) => + globalScene.arena.positionalTagManager.canAddTag(PositionalTagType.WISH, target.getBattlerIndex()); } } @@ -3523,7 +3727,7 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr { * effects should be overridden this turn. * @returns `true` if base move effects were overridden; `false` otherwise */ - override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + override apply(user: Pokemon, _target: Pokemon, move: Move, args: any[]): boolean { if (user.turnData.combiningPledge) { // "The two moves have become one!\nIt's a combined move!" globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:combiningPledge")); @@ -3532,17 +3736,21 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr { const overridden = args[0] as BooleanHolder; - const allyMovePhase = globalScene.phaseManager.getMovePhase((phase) => phase.pokemon.isPlayer() === user.isPlayer()); + const allyMovePhase = globalScene.phaseManager.getMovePhase(phase => phase.pokemon.isPlayer() === user.isPlayer()); if (allyMovePhase) { const allyMove = allyMovePhase.move.getMove(); if (allyMove !== move && allyMove.hasAttr("AwaitCombinedPledgeAttr")) { - [ user, allyMovePhase.pokemon ].forEach((p) => p.turnData.combiningPledge = move.id); + for (const p of [user, allyMovePhase.pokemon]) { + p.turnData.combiningPledge = move.id; + } // "{userPokemonName} is waiting for {allyPokemonName}'s move..." - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:awaitingPledge", { - userPokemonName: getPokemonNameWithAffix(user), - allyPokemonName: getPokemonNameWithAffix(allyMovePhase.pokemon) - })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:awaitingPledge", { + userPokemonName: getPokemonNameWithAffix(user), + allyPokemonName: getPokemonNameWithAffix(allyMovePhase.pokemon), + }), + ); // Move the ally's MovePhase (if needed) so that the ally moves next globalScene.phaseManager.forceMoveNext((phase: MovePhase) => phase.pokemon === user.getAlly()); @@ -3561,9 +3769,9 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr { */ interface StatStageChangeAttrOptions extends MoveEffectAttrOptions { /** If defined, needs to be met in order for the stat change to apply */ - condition?: MoveConditionFunc, + condition?: MoveConditionFunc; /** `true` to display a message */ - showMessage?: boolean + showMessage?: boolean; } /** @@ -3594,7 +3802,7 @@ export class StatStageChangeAttr extends MoveEffectAttr { * The condition required for the stat stage change to apply. * Defaults to `null` (i.e. no condition required). */ - private get condition () { + private get condition() { return this.options?.condition ?? null; } @@ -3602,7 +3810,7 @@ export class StatStageChangeAttr extends MoveEffectAttr { * `true` to display a message for the stat change. * @defaultValue `true` */ - private get showMessage () { + private get showMessage() { return this.options?.showMessage ?? true; } @@ -3622,7 +3830,14 @@ export class StatStageChangeAttr extends MoveEffectAttr { const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); if (moveChance < 0 || moveChance === 100 || user.randBattleSeedInt(100) < moveChance) { const stages = this.getLevels(user); - globalScene.phaseManager.unshiftNew("StatStageChangePhase", (this.selfTarget ? user : target).getBattlerIndex(), this.selfTarget, this.stats, stages, this.showMessage); + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + (this.selfTarget ? user : target).getBattlerIndex(), + this.selfTarget, + this.stats, + stages, + this.showMessage, + ); return true; } @@ -3633,7 +3848,7 @@ export class StatStageChangeAttr extends MoveEffectAttr { return this.stages; } - getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getTargetBenefitScore(user: Pokemon, target: Pokemon, _move: Move): number { let ret = 0; const moveLevels = this.getLevels(user); for (const stat of this.stats) { @@ -3648,29 +3863,41 @@ export class StatStageChangeAttr extends MoveEffectAttr { switch (stat) { case Stat.ATK: if (this.selfTarget) { - noEffect = !user.getMoveset().find(m => {const mv = m.getMove(); return mv.is("AttackMove") && mv.category === MoveCategory.PHYSICAL;} ); + noEffect = !user.getMoveset().find(m => { + const mv = m.getMove(); + return mv.is("AttackMove") && mv.category === MoveCategory.PHYSICAL; + }); } break; case Stat.DEF: if (!this.selfTarget) { - noEffect = !user.getMoveset().find(m => {const mv = m.getMove(); return mv.is("AttackMove") && mv.category === MoveCategory.PHYSICAL;} ); + noEffect = !user.getMoveset().find(m => { + const mv = m.getMove(); + return mv.is("AttackMove") && mv.category === MoveCategory.PHYSICAL; + }); } break; case Stat.SPATK: if (this.selfTarget) { - noEffect = !user.getMoveset().find(m => {const mv = m.getMove(); return mv.is("AttackMove") && mv.category === MoveCategory.PHYSICAL;} ); + noEffect = !user.getMoveset().find(m => { + const mv = m.getMove(); + return mv.is("AttackMove") && mv.category === MoveCategory.PHYSICAL; + }); } break; case Stat.SPDEF: if (!this.selfTarget) { - noEffect = !user.getMoveset().find(m => {const mv = m.getMove(); return mv.is("AttackMove") && mv.category === MoveCategory.PHYSICAL;} ); + noEffect = !user.getMoveset().find(m => { + const mv = m.getMove(); + return mv.is("AttackMove") && mv.category === MoveCategory.PHYSICAL; + }); } break; } if (noEffect) { continue; } - ret += (levels * 4) + (levels > 0 ? -2 : 2); + ret += levels * 4 + (levels > 0 ? -2 : 2); } return ret; } @@ -3718,18 +3945,18 @@ export class SecretPowerAttr extends MoveEffectAttr { private determineTerrainEffect(terrain: TerrainType): MoveEffectAttr { let secondaryEffect: MoveEffectAttr; switch (terrain) { - case TerrainType.ELECTRIC: - default: - secondaryEffect = new StatusEffectAttr(StatusEffect.PARALYSIS, false); - break; case TerrainType.MISTY: - secondaryEffect = new StatStageChangeAttr([ Stat.SPATK ], -1, false); + secondaryEffect = new StatStageChangeAttr([Stat.SPATK], -1, false); break; case TerrainType.GRASSY: secondaryEffect = new StatusEffectAttr(StatusEffect.SLEEP, false); break; case TerrainType.PSYCHIC: - secondaryEffect = new StatStageChangeAttr([ Stat.SPD ], -1, false); + secondaryEffect = new StatStageChangeAttr([Stat.SPD], -1, false); + break; + case TerrainType.ELECTRIC: + default: + secondaryEffect = new StatusEffectAttr(StatusEffect.PARALYSIS, false); break; } return secondaryEffect; @@ -3767,7 +3994,7 @@ export class SecretPowerAttr extends MoveEffectAttr { case BiomeId.MOUNTAIN: case BiomeId.TEMPLE: case BiomeId.RUINS: - secondaryEffect = new StatStageChangeAttr([ Stat.SPD ], -1, false); + secondaryEffect = new StatStageChangeAttr([Stat.SPD], -1, false); break; case BiomeId.ICE_CAVE: case BiomeId.SNOWY_FOREST: @@ -3777,19 +4004,19 @@ export class SecretPowerAttr extends MoveEffectAttr { secondaryEffect = new StatusEffectAttr(StatusEffect.BURN, false); break; case BiomeId.FAIRY_CAVE: - secondaryEffect = new StatStageChangeAttr([ Stat.SPATK ], -1, false); + secondaryEffect = new StatStageChangeAttr([Stat.SPATK], -1, false); break; case BiomeId.DESERT: case BiomeId.CONSTRUCTION_SITE: case BiomeId.BEACH: case BiomeId.ISLAND: case BiomeId.BADLANDS: - secondaryEffect = new StatStageChangeAttr([ Stat.ACC ], -1, false); + secondaryEffect = new StatStageChangeAttr([Stat.ACC], -1, false); break; case BiomeId.SEA: case BiomeId.LAKE: case BiomeId.SEABED: - secondaryEffect = new StatStageChangeAttr([ Stat.ATK ], -1, false); + secondaryEffect = new StatStageChangeAttr([Stat.ATK], -1, false); break; case BiomeId.CAVE: case BiomeId.WASTELAND: @@ -3799,7 +4026,7 @@ export class SecretPowerAttr extends MoveEffectAttr { secondaryEffect = new AddBattlerTagAttr(BattlerTagType.FLINCHED, false, true); break; case BiomeId.END: - secondaryEffect = new StatStageChangeAttr([ Stat.DEF ], -1, false); + secondaryEffect = new StatStageChangeAttr([Stat.DEF], -1, false); break; case BiomeId.TOWN: case BiomeId.METROPOLIS: @@ -3817,12 +4044,19 @@ export class SecretPowerAttr extends MoveEffectAttr { } export class PostVictoryStatStageChangeAttr extends MoveAttr { - private stats: BattleStat[]; - private stages: number; - private condition?: MoveConditionFunc; - private showMessage: boolean; + private readonly stats: BattleStat[]; + private readonly stages: number; + private readonly condition?: MoveConditionFunc; + private readonly showMessage: boolean; - constructor(stats: BattleStat[], stages: number, selfTarget?: boolean, condition?: MoveConditionFunc, showMessage: boolean = true, firstHitOnly: boolean = false) { + constructor( + stats: BattleStat[], + stages: number, + _selfTarget?: boolean, + condition?: MoveConditionFunc, + showMessage = true, + _firstHitOnly = false, + ) { super(); this.stats = stats; this.stages = stages; @@ -3839,15 +4073,17 @@ export class PostVictoryStatStageChangeAttr extends MoveAttr { } export class AcupressureStatStageChangeAttr extends MoveEffectAttr { - constructor() { - super(); - } - - override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const randStats = BATTLE_STATS.filter((s) => target.getStatStage(s) < 6); + override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { + const randStats = BATTLE_STATS.filter(s => target.getStatStage(s) < 6); if (randStats.length > 0) { - const boostStat = [ randStats[user.randBattleSeedInt(randStats.length)] ]; - globalScene.phaseManager.unshiftNew("StatStageChangePhase", target.getBattlerIndex(), this.selfTarget, boostStat, 2); + const boostStat = [randStats[user.randBattleSeedInt(randStats.length)]]; + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + target.getBattlerIndex(), + this.selfTarget, + boostStat, + 2, + ); return true; } return false; @@ -3856,10 +4092,10 @@ export class AcupressureStatStageChangeAttr extends MoveEffectAttr { export class GrowthStatStageChangeAttr extends StatStageChangeAttr { constructor() { - super([ Stat.ATK, Stat.SPATK ], 1, true); + super([Stat.ATK, Stat.SPATK], 1, true); } - getLevels(user: Pokemon): number { + getLevels(_user: Pokemon): number { if (!globalScene.arena.weather?.isEffectSuppressed()) { const weatherType = globalScene.arena.weather?.weatherType; if (weatherType === WeatherType.SUNNY || weatherType === WeatherType.HARSH_SUN) { @@ -3871,10 +4107,15 @@ export class GrowthStatStageChangeAttr extends StatStageChangeAttr { } export class CutHpStatStageBoostAttr extends StatStageChangeAttr { - private cutRatio: number; - private messageCallback: ((user: Pokemon) => void) | undefined; + private readonly cutRatio: number; + private readonly messageCallback: ((user: Pokemon) => void) | undefined; - constructor(stat: BattleStat[], levels: number, cutRatio: number, messageCallback?: ((user: Pokemon) => void) | undefined) { + constructor( + stat: BattleStat[], + levels: number, + cutRatio: number, + messageCallback?: ((user: Pokemon) => void) | undefined, + ) { super(stat, levels, true); this.cutRatio = cutRatio; @@ -3905,7 +4146,7 @@ export class OrderUpStatBoostAttr extends MoveEffectAttr { super(true); } - override apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean { + override apply(user: Pokemon, _target: Pokemon, _move: Move, _args?: any[]): boolean { const commandedTag = user.getTag(CommandedTag); if (!commandedTag) { return false; @@ -3924,7 +4165,13 @@ export class OrderUpStatBoostAttr extends MoveEffectAttr { break; } - globalScene.phaseManager.unshiftNew("StatStageChangePhase", user.getBattlerIndex(), this.selfTarget, [ increasedStat ], 1); + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + user.getBattlerIndex(), + this.selfTarget, + [increasedStat], + 1, + ); return true; } } @@ -3947,7 +4194,12 @@ export class CopyStatsAttr extends MoveEffectAttr { } target.updateInfo(); user.updateInfo(); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:copiedStatChanges", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:copiedStatChanges", { + pokemonName: getPokemonNameWithAffix(user), + targetName: getPokemonNameWithAffix(target), + }), + ); return true; } @@ -3966,14 +4218,16 @@ export class InvertStatsAttr extends MoveEffectAttr { target.updateInfo(); user.updateInfo(); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:invertStats", { pokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:invertStats", { pokemonName: getPokemonNameWithAffix(target) }), + ); return true; } } export class ResetStatsAttr extends MoveEffectAttr { - private targetAllPokemon: boolean; + private readonly targetAllPokemon: boolean; constructor(targetAllPokemon: boolean) { super(); this.targetAllPokemon = targetAllPokemon; @@ -3983,11 +4237,16 @@ export class ResetStatsAttr extends MoveEffectAttr { 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)); + for (const p of activePokemon) { + this.resetStats(p); + } globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:statEliminated")); - } else { // Affects only the single target when Clear Smog is used + } else { + // Affects only the single target when Clear Smog is used this.resetStats(target); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:resetStats", { pokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:resetStats", { pokemonName: getPokemonNameWithAffix(target) }), + ); } return true; } @@ -4006,7 +4265,7 @@ export class ResetStatsAttr extends MoveEffectAttr { */ export class SwapStatStagesAttr extends MoveEffectAttr { /** The stat stages to be swapped between the user and the target */ - private stats: readonly BattleStat[]; + private readonly stats: readonly BattleStat[]; constructor(stats: readonly BattleStat[]) { super(); @@ -4023,7 +4282,7 @@ export class SwapStatStagesAttr extends MoveEffectAttr { * @param args N/A * @returns true if attribute application succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any []): boolean { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (super.apply(user, target, move, args)) { for (const s of this.stats) { const temp = user.getStatStage(s); @@ -4035,13 +4294,17 @@ export class SwapStatStagesAttr extends MoveEffectAttr { user.updateInfo(); if (this.stats.length === 7) { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:switchedStatChanges", { pokemonName: getPokemonNameWithAffix(user) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:switchedStatChanges", { pokemonName: getPokemonNameWithAffix(user) }), + ); } else if (this.stats.length === 2) { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:switchedTwoStatChanges", { - pokemonName: getPokemonNameWithAffix(user), - firstStat: i18next.t(getStatKey(this.stats[0])), - secondStat: i18next.t(getStatKey(this.stats[1])) - })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:switchedTwoStatChanges", { + pokemonName: getPokemonNameWithAffix(user), + firstStat: i18next.t(getStatKey(this.stats[0])), + secondStat: i18next.t(getStatKey(this.stats[1])), + }), + ); } return true; } @@ -4056,7 +4319,7 @@ export class HpSplitAttr extends MoveEffectAttr { } const hpValue = Math.floor((target.hp + user.hp) / 2); - [ user, target ].forEach((p) => { + [user, target].forEach(p => { if (p.hp < hpValue) { const healing = p.heal(hpValue - p.hp); if (healing) { @@ -4076,7 +4339,7 @@ export class HpSplitAttr extends MoveEffectAttr { } export class VariablePowerAttr extends MoveAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { //const power = args[0] as Utils.NumberHolder; return false; } @@ -4091,9 +4354,9 @@ export class LessPPMorePowerAttr extends VariablePowerAttr { * @param args [0] {@linkcode NumberHolder} of power * @returns true if the function succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, move: Move, args: any[]): boolean { const ppMax = move.pp; - const ppUsed = user.moveset.find((m) => m.moveId === move.id)?.ppUsed ?? 0; + const ppUsed = user.moveset.find(m => m.moveId === move.id)?.ppUsed ?? 0; let ppRemains = ppMax - ppUsed; /** Reduce to 0 to avoid negative numbers if user has 1PP before attack and target has Ability.PRESSURE */ @@ -4125,7 +4388,7 @@ export class LessPPMorePowerAttr extends VariablePowerAttr { } export class MovePowerMultiplierAttr extends VariablePowerAttr { - private powerMultiplierFunc: (user: Pokemon, target: Pokemon, move: Move) => number; + private readonly powerMultiplierFunc: (user: Pokemon, target: Pokemon, move: Move) => number; constructor(powerMultiplier: (user: Pokemon, target: Pokemon, move: Move) => number) { super(); @@ -4158,13 +4421,12 @@ const beatUpFunc = (user: Pokemon, allyIndex: number): number => { if (pokemon.id !== user.id && pokemon?.status && pokemon.status.effect !== StatusEffect.NONE) { continue; } - return (pokemon.species.getBaseStat(Stat.ATK) / 10) + 5; + return pokemon.species.getBaseStat(Stat.ATK) / 10 + 5; } return 0; }; export class BeatUpAttr extends VariablePowerAttr { - /** * Gets the next party member to contribute to a Beat Up hit, and calculates the base power for it. * @param user Pokemon that used the move @@ -4173,7 +4435,7 @@ export class BeatUpAttr extends VariablePowerAttr { * @param args N/A * @returns true if the function succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const power = args[0] as NumberHolder; const party = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); @@ -4193,28 +4455,36 @@ export class BeatUpAttr extends VariablePowerAttr { * than using a seed offset */ function doublePowerChanceMessageFunc(chance: number) { - return (user: Pokemon, target: Pokemon, move: Move) => { - let message: string = ""; - globalScene.executeWithSeedOffset(() => { - const rand = randSeedInt(100); - if (rand < chance) { - message = i18next.t("moveTriggers:goingAllOutForAttack", { pokemonName: getPokemonNameWithAffix(user) }); - } - }, globalScene.currentBattle.turn << 6, globalScene.waveSeed); + return (user: Pokemon, _target: Pokemon, _move: Move) => { + let message = ""; + globalScene.executeWithSeedOffset( + () => { + const rand = randSeedInt(100); + if (rand < chance) { + message = i18next.t("moveTriggers:goingAllOutForAttack", { pokemonName: getPokemonNameWithAffix(user) }); + } + }, + globalScene.currentBattle.turn << 6, + globalScene.waveSeed, + ); return message; }; } export class DoublePowerChanceAttr extends VariablePowerAttr { - private chance: number; + private readonly chance: number; constructor(chance: number) { - super(false) - this.chance = chance + super(false); + this.chance = chance; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { let rand = 0; - globalScene.executeWithSeedOffset(() => rand = randSeedInt(100), globalScene.currentBattle.turn << 6, globalScene.waveSeed); + globalScene.executeWithSeedOffset( + () => (rand = randSeedInt(100)), + globalScene.currentBattle.turn << 6, + globalScene.waveSeed, + ); if (rand < this.chance) { const power = args[0] as NumberHolder; power.value *= 2; @@ -4227,20 +4497,18 @@ export class DoublePowerChanceAttr extends VariablePowerAttr { export abstract class ConsecutiveUsePowerMultiplierAttr extends MovePowerMultiplierAttr { constructor(limit: number, resetOnFail: boolean, resetOnLimit?: boolean, ...comboMoves: MoveId[]) { - super((user: Pokemon, target: Pokemon, move: Move): number => { + super((user: Pokemon, _target: Pokemon, move: Move): number => { const moveHistory = user.getLastXMoves(limit + 1).slice(1); let count = 0; let turnMove: TurnMove | undefined; while ( - ( - (turnMove = moveHistory.shift())?.move === move.id - || (comboMoves.length && comboMoves.includes(turnMove?.move ?? MoveId.NONE)) - ) + ((turnMove = moveHistory.shift())?.move === move.id + || (comboMoves.length > 0 && comboMoves.includes(turnMove?.move ?? MoveId.NONE))) && (!resetOnFail || turnMove?.result === MoveResult.SUCCESS) ) { - if (count < (limit - 1)) { + if (count < limit - 1) { count++; } else if (resetOnLimit) { count = 0; @@ -4264,16 +4532,16 @@ export class ConsecutiveUseDoublePowerAttr extends ConsecutiveUsePowerMultiplier export class ConsecutiveUseMultiBasePowerAttr extends ConsecutiveUsePowerMultiplierAttr { getMultiplier(count: number): number { - return (count + 1); + return count + 1; } } export class WeightPowerAttr extends VariablePowerAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { const power = args[0] as NumberHolder; const targetWeight = target.getWeight(); - const weightThresholds = [ 10, 25, 50, 100, 200 ]; + const weightThresholds = [10, 25, 50, 100, 200]; let w = 0; while (targetWeight >= weightThresholds[w]) { @@ -4290,7 +4558,7 @@ export class WeightPowerAttr extends VariablePowerAttr { /** * Attribute used for Electro Ball move. - **/ + */ export class ElectroBallPowerAttr extends VariablePowerAttr { /** * Move that deals more damage the faster {@linkcode Stat.SPD} @@ -4301,12 +4569,12 @@ export class ElectroBallPowerAttr extends VariablePowerAttr { * @param args N/A * @returns true if the function succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { 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 ]; - const statThresholdPowers = [ 150, 120, 80, 60, 40 ]; + const statThresholds = [0.25, 1 / 3, 0.5, 1, -1]; + const statThresholdPowers = [150, 120, 80, 60, 40]; let w = 0; while (w < statThresholds.length - 1 && statRatio > statThresholds[w]) { @@ -4320,10 +4588,9 @@ export class ElectroBallPowerAttr extends VariablePowerAttr { } } - /** * Attribute used for Gyro Ball move. - **/ + */ export class GyroBallPowerAttr extends VariablePowerAttr { /** * Move that deals more damage the slower {@linkcode Stat.SPD} @@ -4334,7 +4601,7 @@ export class GyroBallPowerAttr extends VariablePowerAttr { * @param args N/A * @returns true if the function succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { const power = args[0] as NumberHolder; const userSpeed = user.getEffectiveStat(Stat.SPD); if (userSpeed < 1) { @@ -4343,30 +4610,30 @@ export class GyroBallPowerAttr extends VariablePowerAttr { return true; } - power.value = Math.floor(Math.min(150, 25 * target.getEffectiveStat(Stat.SPD) / userSpeed + 1)); + power.value = Math.floor(Math.min(150, (25 * target.getEffectiveStat(Stat.SPD)) / userSpeed + 1)); return true; } } export class LowHpPowerAttr extends VariablePowerAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const power = args[0] as NumberHolder; const hpRatio = user.getHpRatio(); switch (true) { - case (hpRatio < 0.0417): + case hpRatio < 0.0417: power.value = 200; break; - case (hpRatio < 0.1042): + case hpRatio < 0.1042: power.value = 150; break; - case (hpRatio < 0.2083): + case hpRatio < 0.2083: power.value = 100; break; - case (hpRatio < 0.3542): + case hpRatio < 0.3542: power.value = 80; break; - case (hpRatio < 0.6875): + case hpRatio < 0.6875: power.value = 40; break; default: @@ -4379,7 +4646,7 @@ export class LowHpPowerAttr extends VariablePowerAttr { } export class CompareWeightPowerAttr extends VariablePowerAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { const power = args[0] as NumberHolder; const userWeight = user.getWeight(); const targetWeight = target.getWeight(); @@ -4391,16 +4658,16 @@ export class CompareWeightPowerAttr extends VariablePowerAttr { const relativeWeight = (targetWeight / userWeight) * 100; switch (true) { - case (relativeWeight < 20.01): + case relativeWeight < 20.01: power.value = 120; break; - case (relativeWeight < 25.01): + case relativeWeight < 25.01: power.value = 100; break; - case (relativeWeight < 33.35): + case relativeWeight < 33.35: power.value = 80; break; - case (relativeWeight < 50.01): + case relativeWeight < 50.01: power.value = 60; break; default: @@ -4413,7 +4680,7 @@ export class CompareWeightPowerAttr extends VariablePowerAttr { } export class HpPowerAttr extends VariablePowerAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { (args[0] as NumberHolder).value = toDmgValue(150 * user.getHpRatio()); return true; @@ -4441,7 +4708,7 @@ export class OpponentHighHpPowerAttr extends VariablePowerAttr { * @param args holds the base power of the move at args[0] * @returns true */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { (args[0] as NumberHolder).value = toDmgValue(this.maxBasePower * target.getHpRatio()); return true; @@ -4449,7 +4716,7 @@ export class OpponentHighHpPowerAttr extends VariablePowerAttr { } export class TurnDamagedDoublePowerAttr extends VariablePowerAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + 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 NumberHolder).value *= 2; return true; @@ -4459,35 +4726,43 @@ export class TurnDamagedDoublePowerAttr extends VariablePowerAttr { } } -const magnitudeMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => { +const magnitudeMessageFunc = (_user: Pokemon, _target: Pokemon, _move: Move) => { let message: string; - globalScene.executeWithSeedOffset(() => { - const magnitudeThresholds = [ 5, 15, 35, 65, 75, 95 ]; + globalScene.executeWithSeedOffset( + () => { + const magnitudeThresholds = [5, 15, 35, 65, 75, 95]; - const rand = randSeedInt(100); + const rand = randSeedInt(100); - let m = 0; - for (; m < magnitudeThresholds.length; m++) { - if (rand < magnitudeThresholds[m]) { - break; + let m = 0; + for (; m < magnitudeThresholds.length; m++) { + if (rand < magnitudeThresholds[m]) { + break; + } } - } - message = i18next.t("moveTriggers:magnitudeMessage", { magnitude: m + 4 }); - }, globalScene.currentBattle.turn << 6, globalScene.waveSeed); + message = i18next.t("moveTriggers:magnitudeMessage", { magnitude: m + 4 }); + }, + globalScene.currentBattle.turn << 6, + globalScene.waveSeed, + ); return message!; }; export class MagnitudePowerAttr extends VariablePowerAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const power = args[0] as NumberHolder; - const magnitudeThresholds = [ 5, 15, 35, 65, 75, 95 ]; - const magnitudePowers = [ 10, 30, 50, 70, 90, 100, 110, 150 ]; + const magnitudeThresholds = [5, 15, 35, 65, 75, 95]; + const magnitudePowers = [10, 30, 50, 70, 90, 100, 110, 150]; let rand: number; - globalScene.executeWithSeedOffset(() => rand = 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++) { @@ -4503,7 +4778,7 @@ export class MagnitudePowerAttr extends VariablePowerAttr { } export class AntiSunlightPowerDecreaseAttr extends VariablePowerAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { if (!globalScene.arena.weather?.isEffectSuppressed()) { const power = args[0] as NumberHolder; const weatherType = globalScene.arena.weather?.weatherType || WeatherType.NONE; @@ -4524,7 +4799,7 @@ export class AntiSunlightPowerDecreaseAttr extends VariablePowerAttr { } export class FriendshipPowerAttr extends VariablePowerAttr { - private invert: boolean; + private readonly invert: boolean; constructor(invert?: boolean) { super(); @@ -4532,11 +4807,13 @@ export class FriendshipPowerAttr extends VariablePowerAttr { this.invert = !!invert; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const power = args[0] as NumberHolder; - const friendshipPower = Math.floor(Math.min(user.isPlayer() ? user.friendship : user.species.baseFriendship, 255) / 2.5); - power.value = Math.max(!this.invert ? friendshipPower : 102 - friendshipPower, 1); + const friendshipPower = Math.floor( + Math.min(user.isPlayer() ? user.friendship : user.species.baseFriendship, 255) / 2.5, + ); + power.value = Math.max(this.invert ? 102 - friendshipPower : friendshipPower, 1); return true; } @@ -4548,7 +4825,7 @@ export class FriendshipPowerAttr extends VariablePowerAttr { * Self-inflicted confusion damage and hits taken by a Subsitute are ignored. */ export class RageFistPowerAttr extends VariablePowerAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { /* Reasons this works correctly: * Confusion calls user.damageAndUpdate() directly (no counter increment), * Substitute hits call user.damageAndUpdate() with a damage value of 0, also causing @@ -4560,7 +4837,6 @@ export class RageFistPowerAttr extends VariablePowerAttr { basePower.value = 50 * (1 + Math.min(hitCount, 6)); return true; } - } /** @@ -4569,14 +4845,13 @@ export class RageFistPowerAttr extends VariablePowerAttr { * @returns the amount of positive stats */ const countPositiveStatStages = (pokemon: Pokemon): number => { - return pokemon.getStatStages().reduce((total, stat) => (stat && stat > 0) ? total + stat : total, 0); + return pokemon.getStatStages().reduce((total, stat) => (stat && stat > 0 ? total + stat : total), 0); }; /** * Attribute that increases power based on the amount of positive stat stage increases. */ export class PositiveStatStagePowerAttr extends VariablePowerAttr { - /** * @param user The pokemon that is being used to calculate the amount of positive stats * @param target N/A @@ -4584,7 +4859,7 @@ export class PositiveStatStagePowerAttr extends VariablePowerAttr { * @param args The argument for VariablePowerAttr, accumulates and sets the amount of power multiplied by stats * @returns Returns true if attribute is applied */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const positiveStatStages: number = countPositiveStatStages(user); (args[0] as NumberHolder).value += positiveStatStages * 20; @@ -4598,33 +4873,33 @@ export class PositiveStatStagePowerAttr extends VariablePowerAttr { * up to a maximum of 200 base power in total. */ export class PunishmentPowerAttr extends VariablePowerAttr { - private PUNISHMENT_MIN_BASE_POWER = 60; - private PUNISHMENT_MAX_BASE_POWER = 200; + private readonly PUNISHMENT_MIN_BASE_POWER = 60; + private readonly PUNISHMENT_MAX_BASE_POWER = 200; /** - * @param user N/A - * @param target The pokemon that the move is being used against, as well as calculating the stats for the min/max base power - * @param move N/A - * @param args The value that is being changed due to VariablePowerAttr - * @returns Returns true if attribute is applied - */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + * @param user N/A + * @param target The pokemon that the move is being used against, as well as calculating the stats for the min/max base power + * @param move N/A + * @param args The value that is being changed due to VariablePowerAttr + * @returns Returns true if attribute is applied + */ + apply(_user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { const positiveStatStages: number = countPositiveStatStages(target); (args[0] as NumberHolder).value = Math.min( this.PUNISHMENT_MAX_BASE_POWER, - this.PUNISHMENT_MIN_BASE_POWER + positiveStatStages * 20 + this.PUNISHMENT_MIN_BASE_POWER + positiveStatStages * 20, ); return true; } } export class PresentPowerAttr extends VariablePowerAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { /** * If this move is multi-hit, and this attribute is applied to any hit * other than the first, this move cannot result in a heal. */ - const firstHit = (user.turnData.hitCount === user.turnData.hitsLeft); + const firstHit = user.turnData.hitCount === user.turnData.hitsLeft; const powerSeed = randSeedInt(firstHit ? 100 : 80); if (powerSeed <= 40) { @@ -4637,8 +4912,13 @@ export class PresentPowerAttr extends VariablePowerAttr { // If this move is multi-hit, disable all other hits user.turnData.hitCount = 1; user.turnData.hitsLeft = 1; - globalScene.phaseManager.unshiftNew("PokemonHealPhase", target.getBattlerIndex(), - toDmgValue(target.getMaxHp() / 4), i18next.t("moveTriggers:regainedHealth", { pokemonName: getPokemonNameWithAffix(target) }), true); + globalScene.phaseManager.unshiftNew( + "PokemonHealPhase", + target.getBattlerIndex(), + toDmgValue(target.getMaxHp() / 4), + i18next.t("moveTriggers:regainedHealth", { pokemonName: getPokemonNameWithAffix(target) }), + true, + ); } return true; @@ -4646,8 +4926,12 @@ export class PresentPowerAttr extends VariablePowerAttr { } export class WaterShurikenPowerAttr extends VariablePowerAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - if (user.species.speciesId === SpeciesId.GRENINJA && user.hasAbility(AbilityId.BATTLE_BOND) && user.formIndex === 2) { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { + if ( + user.species.speciesId === SpeciesId.GRENINJA + && user.hasAbility(AbilityId.BATTLE_BOND) + && user.formIndex === 2 + ) { (args[0] as NumberHolder).value = 20; return true; } @@ -4659,14 +4943,14 @@ export class WaterShurikenPowerAttr extends VariablePowerAttr { * Attribute used to calculate the power of attacks that scale with Stockpile stacks (i.e. Spit Up). */ export class SpitUpPowerAttr extends VariablePowerAttr { - private multiplier: number = 0; + private readonly multiplier: number; constructor(multiplier: number) { super(); this.multiplier = multiplier; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const stockpilingTag = user.getTag(StockpilingTag); if (stockpilingTag && stockpilingTag.stockpiledCount > 0) { @@ -4685,10 +4969,10 @@ export class SpitUpPowerAttr extends VariablePowerAttr { */ export class SwallowHealAttr extends HealAttr { constructor() { - super(1) + super(1); } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { const stockpilingTag = user.getTag(StockpilingTag); if (stockpilingTag && stockpilingTag.stockpiledCount > 0) { @@ -4698,9 +4982,10 @@ export class SwallowHealAttr extends HealAttr { if (stockpiled === 1) { healRatio = 0.25; } else if (stockpiled === 2) { - healRatio = 0.50; - } else { // stockpiled >= 3 - healRatio = 1.00; + healRatio = 0.5; + } else { + // stockpiled >= 3 + healRatio = 1.0; } if (healRatio) { @@ -4713,7 +4998,7 @@ export class SwallowHealAttr extends HealAttr { } } -const hasStockpileStacksCondition: MoveConditionFunc = (user) => { +const hasStockpileStacksCondition: MoveConditionFunc = user => { const hasStockpilingTag = user.getTag(StockpilingTag); return !!hasStockpilingTag && hasStockpilingTag.stockpiledCount > 0; }; @@ -4724,7 +5009,7 @@ const hasStockpileStacksCondition: MoveConditionFunc = (user) => { */ export class MultiHitPowerIncrementAttr extends VariablePowerAttr { /** The max number of base power increments allowed for this move */ - private maxHits: number; + private readonly maxHits: number; constructor(maxHits: number) { super(); @@ -4743,11 +5028,11 @@ export class MultiHitPowerIncrementAttr extends VariablePowerAttr { * @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 { + 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 NumberHolder; - power.value = move.power * (1 + hitsTotal % this.maxHits); + power.value = move.power * (1 + (hitsTotal % this.maxHits)); return true; } @@ -4760,7 +5045,7 @@ export class MultiHitPowerIncrementAttr extends VariablePowerAttr { */ export class LastMoveDoublePowerAttr extends VariablePowerAttr { /** The move that must precede the current move */ - private move: MoveId; + private readonly move: MoveId; constructor(move: MoveId) { super(); @@ -4778,17 +5063,16 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr { * @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 { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const power = args[0] as NumberHolder; for (const p of globalScene.phaseManager.dynamicQueueManager.getLastTurnOrder().slice(0, -1).reverse()) { - const [ lastMove ] = p.getLastXMoves(1); + const [lastMove] = p.getLastXMoves(1); if (lastMove.result !== MoveResult.FAIL) { - if ((lastMove.result === MoveResult.SUCCESS) && (lastMove.move === this.move)) { + if (lastMove.result === MoveResult.SUCCESS && lastMove.move === this.move) { power.value *= 2; return true; - } else { - break; } + break; } } @@ -4801,7 +5085,7 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr { * move from an ally. */ export class CombinedPledgePowerAttr extends VariablePowerAttr { - override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + override apply(user: Pokemon, _target: Pokemon, move: Move, args: any[]): boolean { const power = args[0]; if (!(power instanceof NumberHolder)) { return false; @@ -4820,7 +5104,7 @@ export class CombinedPledgePowerAttr extends VariablePowerAttr { * Applies STAB to the given Pledge move if the move is part of a combined attack. */ export class CombinedPledgeStabBoostAttr extends MoveAttr { - override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + override apply(user: Pokemon, _target: Pokemon, move: Move, args: any[]): boolean { const stabMultiplier = args[0]; if (!(stabMultiplier instanceof NumberHolder)) { return false; @@ -4840,7 +5124,7 @@ export class CombinedPledgeStabBoostAttr extends MoveAttr { * Doubles power if another Pokemon has previously selected Round this turn. */ export class RoundPowerAttr extends VariablePowerAttr { - override apply(user: Pokemon, target: Pokemon, move: Move, args: [NumberHolder]): boolean { + override apply(user: Pokemon, _target: Pokemon, _move: Move, args: [NumberHolder]): boolean { const power = args[0]; if (user.turnData.joinedRound) { @@ -4861,7 +5145,7 @@ export class CueNextRoundAttr extends MoveEffectAttr { super(true, { lastHitOnly: true }); } - override apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean { + override apply(_user: Pokemon, _target: Pokemon, _move: Move, _args?: any[]): boolean { const nextRoundPhase = globalScene.phaseManager.getMovePhase(phase => phase.move.moveId === MoveId.ROUND); if (!nextRoundPhase) { @@ -4890,7 +5174,7 @@ export class StatChangeBeforeDmgCalcAttr extends MoveAttr { * * @returns true if stat stages where correctly applied */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { return false; } } @@ -4911,7 +5195,7 @@ export class SpectralThiefAttr extends StatChangeBeforeDmgCalcAttr { * * @returns true if stat stages where correctly stolen */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { /** * Copy all positive stat stages to user and reduce copied stat stages on target. */ @@ -4925,76 +5209,67 @@ export class SpectralThiefAttr extends StatChangeBeforeDmgCalcAttr { */ const availableToSteal = Math.min(statStageValueTarget, 6 - statStageValueUser); - globalScene.phaseManager.unshiftNew("StatStageChangePhase", user.getBattlerIndex(), this.selfTarget, [ s ], availableToSteal); + globalScene.phaseManager.unshiftNew( + "StatStageChangePhase", + user.getBattlerIndex(), + this.selfTarget, + [s], + availableToSteal, + ); target.setStatStage(s, statStageValueTarget - availableToSteal); } } target.updateInfo(); user.updateInfo(); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:stealPositiveStats", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:stealPositiveStats", { + pokemonName: getPokemonNameWithAffix(user), + targetName: getPokemonNameWithAffix(target), + }), + ); return true; } - } export class VariableAtkAttr extends MoveAttr { - constructor() { - super(); - } - - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { //const atk = args[0] as Utils.NumberHolder; return false; } } export class TargetAtkUserAtkAttr extends VariableAtkAttr { - constructor() { - super(); - } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { (args[0] as NumberHolder).value = target.getEffectiveStat(Stat.ATK, target); return true; } } export class DefAtkAttr extends VariableAtkAttr { - constructor() { - super(); - } - - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { (args[0] as NumberHolder).value = user.getEffectiveStat(Stat.DEF, target); return true; } } export class VariableDefAttr extends MoveAttr { - constructor() { - super(); - } - - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { //const def = args[0] as Utils.NumberHolder; return false; } } export class DefDefAttr extends VariableDefAttr { - constructor() { - super(); - } - - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { (args[0] as NumberHolder).value = target.getEffectiveStat(Stat.DEF, user); return true; } } export class VariableAccuracyAttr extends MoveAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { //const accuracy = args[0] as Utils.NumberHolder; return false; } @@ -5004,7 +5279,7 @@ export class VariableAccuracyAttr extends MoveAttr { * Attribute used for Thunder and Hurricane that sets accuracy to 50 in sun and never miss in rain */ export class ThunderAccuracyAttr extends VariableAccuracyAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { if (!globalScene.arena.weather?.isEffectSuppressed()) { const accuracy = args[0] as NumberHolder; const weatherType = globalScene.arena.weather?.weatherType || WeatherType.NONE; @@ -5030,7 +5305,7 @@ export class ThunderAccuracyAttr extends VariableAccuracyAttr { * Springtide Storm does NOT have this property */ export class StormAccuracyAttr extends VariableAccuracyAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { if (!globalScene.arena.weather?.isEffectSuppressed()) { const accuracy = args[0] as NumberHolder; const weatherType = globalScene.arena.weather?.weatherType || WeatherType.NONE; @@ -5059,7 +5334,7 @@ export class AlwaysHitMinimizeAttr extends VariableAccuracyAttr { * @param args [0] Accuracy of the move to be modified * @returns true if the function succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { if (target.getTag(BattlerTagType.MINIMIZED)) { const accuracy = args[0] as NumberHolder; accuracy.value = -1; @@ -5072,7 +5347,7 @@ export class AlwaysHitMinimizeAttr extends VariableAccuracyAttr { } export class ToxicAccuracyAttr extends VariableAccuracyAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { if (user.isOfType(PokemonType.POISON)) { const accuracy = args[0] as NumberHolder; accuracy.value = -1; @@ -5084,7 +5359,7 @@ export class ToxicAccuracyAttr extends VariableAccuracyAttr { } export class BlizzardAccuracyAttr extends VariableAccuracyAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { if (!globalScene.arena.weather?.isEffectSuppressed()) { const accuracy = args[0] as NumberHolder; const weatherType = globalScene.arena.weather?.weatherType || WeatherType.NONE; @@ -5099,14 +5374,14 @@ export class BlizzardAccuracyAttr extends VariableAccuracyAttr { } export class VariableMoveCategoryAttr extends MoveAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { return false; } } export class PhotonGeyserCategoryAttr extends VariableMoveCategoryAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const category = (args[0] as NumberHolder); + const category = args[0] as NumberHolder; if (user.getEffectiveStat(Stat.ATK, target, move) > user.getEffectiveStat(Stat.SPATK, target, move)) { category.value = MoveCategory.PHYSICAL; @@ -5125,10 +5400,13 @@ export class PhotonGeyserCategoryAttr extends VariableMoveCategoryAttr { // TODO: Remove the `.partial()` tag from Tera Blast and Tera Starstorm when the above issue is resolved export class TeraMoveCategoryAttr extends VariableMoveCategoryAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const category = (args[0] as 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)) { + 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) + ) { category.value = MoveCategory.PHYSICAL; return true; } @@ -5152,7 +5430,7 @@ export class TeraBlastPowerAttr extends VariablePowerAttr { * previously applied power modifiers. * @returns */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const power = args[0] as NumberHolder; if (user.isTerastallized && user.getTeraType() === PokemonType.STELLAR) { power.value = 100; @@ -5174,8 +5452,8 @@ export class StatusCategoryOnAllyAttr extends VariableMoveCategoryAttr { * @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 NumberHolder); + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { + const category = args[0] as NumberHolder; if (user.getAlly() === target) { category.value = MoveCategory.STATUS; @@ -5188,15 +5466,34 @@ 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 NumberHolder); + const category = args[0] as NumberHolder; - 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}); + 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; return true; - } else if (predictedPhysDmg === predictedSpecDmg && user.randBattleSeedInt(2) === 0) { + } + if (predictedPhysDmg === predictedSpecDmg && user.randBattleSeedInt(2) === 0) { category.value = MoveCategory.PHYSICAL; return true; } @@ -5205,7 +5502,7 @@ export class ShellSideArmCategoryAttr extends VariableMoveCategoryAttr { } export class VariableMoveTypeAttr extends MoveAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { return false; } @@ -5215,20 +5512,27 @@ export class VariableMoveTypeAttr extends MoveAttr { * @param move - The move being used * @returns An array of types to add to the pool of type-boosting items */ - getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] { + getTypesForItemSpawn(_user: Pokemon, move: Move): PokemonType[] { return [move.type]; } } export class FormChangeItemTypeAttr extends VariableMoveTypeAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; if (!(moveType instanceof NumberHolder)) { return false; } - if ([ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(SpeciesId.ARCEUS) || [ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(SpeciesId.SILVALLY)) { - const form = user.species.speciesId === SpeciesId.ARCEUS || user.species.speciesId === SpeciesId.SILVALLY ? user.formIndex : user.fusionSpecies?.formIndex!; + // TODO: this needs to be cleaned up + if ( + [user.species.speciesId, user.fusionSpecies?.speciesId].includes(SpeciesId.ARCEUS) + || [user.species.speciesId, user.fusionSpecies?.speciesId].includes(SpeciesId.SILVALLY) + ) { + const form = + user.species.speciesId === SpeciesId.ARCEUS || user.species.speciesId === SpeciesId.SILVALLY + ? user.formIndex + : user.fusionSpecies!.formIndex; if (form >= 0 && form <= MAX_POKEMON_TYPE && form !== PokemonType.STELLAR) { moveType.value = form as PokemonType; return true; @@ -5240,7 +5544,7 @@ export class FormChangeItemTypeAttr extends VariableMoveTypeAttr { if (moveType.value === move.type) { return false; } - moveType.value = move.type + moveType.value = move.type; return true; } @@ -5248,19 +5552,19 @@ export class FormChangeItemTypeAttr extends VariableMoveTypeAttr { // Get the type const typeHolder = new NumberHolder(move.type); // Passing user in for target is fine; the parameter is unused anyway - this.apply(user, user, move, [ typeHolder ]); + this.apply(user, user, move, [typeHolder]); return [typeHolder.value]; } } export class TechnoBlastTypeAttr extends VariableMoveTypeAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const moveType = args[0]; if (!(moveType instanceof NumberHolder)) { return false; } - if ([ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(SpeciesId.GENESECT)) { + if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(SpeciesId.GENESECT)) { const form = user.species.speciesId === SpeciesId.GENESECT ? user.formIndex : user.fusionSpecies?.formIndex; switch (form) { @@ -5288,19 +5592,19 @@ export class TechnoBlastTypeAttr extends VariableMoveTypeAttr { override getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] { const typeHolder = new NumberHolder(move.type); - this.apply(user, user, move, [ typeHolder ]); + this.apply(user, user, move, [typeHolder]); return [typeHolder.value]; } } export class AuraWheelTypeAttr extends VariableMoveTypeAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const moveType = args[0]; if (!(moveType instanceof NumberHolder)) { return false; } - if ([ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(SpeciesId.MORPEKO)) { + if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(SpeciesId.MORPEKO)) { const form = user.species.speciesId === SpeciesId.MORPEKO ? user.formIndex : user.fusionSpecies?.formIndex; switch (form) { @@ -5328,13 +5632,13 @@ export class AuraWheelTypeAttr extends VariableMoveTypeAttr { } export class RagingBullTypeAttr extends VariableMoveTypeAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const moveType = args[0]; if (!(moveType instanceof NumberHolder)) { return false; } - if ([ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(SpeciesId.PALDEA_TAUROS)) { + if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(SpeciesId.PALDEA_TAUROS)) { const form = user.species.speciesId === SpeciesId.PALDEA_TAUROS ? user.formIndex : user.fusionSpecies?.formIndex; switch (form) { @@ -5356,19 +5660,19 @@ export class RagingBullTypeAttr extends VariableMoveTypeAttr { override getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] { const typeHolder = new NumberHolder(move.type); - this.apply(user, user, move, [ typeHolder ]); - return [ typeHolder.value ]; + this.apply(user, user, move, [typeHolder]); + return [typeHolder.value]; } } export class IvyCudgelTypeAttr extends VariableMoveTypeAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const moveType = args[0]; if (!(moveType instanceof NumberHolder)) { return false; } - if ([ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(SpeciesId.OGERPON)) { + if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(SpeciesId.OGERPON)) { const form = user.species.speciesId === SpeciesId.OGERPON ? user.formIndex : user.fusionSpecies?.formIndex; switch (form) { @@ -5397,13 +5701,13 @@ export class IvyCudgelTypeAttr extends VariableMoveTypeAttr { override getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] { const typeHolder = new NumberHolder(move.type); - this.apply(user, user, move, [ typeHolder ]); - return [ typeHolder.value ]; + this.apply(user, user, move, [typeHolder]); + return [typeHolder.value]; } } export class WeatherBallTypeAttr extends VariableMoveTypeAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; if (!(moveType instanceof NumberHolder)) { return false; @@ -5452,7 +5756,7 @@ export class TerrainPulseTypeAttr extends VariableMoveTypeAttr { * @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 { + apply(user: Pokemon, _target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; if (!(moveType instanceof NumberHolder)) { return false; @@ -5492,31 +5796,48 @@ export class TerrainPulseTypeAttr extends VariableMoveTypeAttr { * Changes type based on the user's IVs */ export class HiddenPowerTypeAttr extends VariableMoveTypeAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const moveType = args[0]; if (!(moveType instanceof NumberHolder)) { return false; } - const iv_val = Math.floor(((user.ivs[Stat.HP] & 1) - + (user.ivs[Stat.ATK] & 1) * 2 - + (user.ivs[Stat.DEF] & 1) * 4 - + (user.ivs[Stat.SPD] & 1) * 8 - + (user.ivs[Stat.SPATK] & 1) * 16 - + (user.ivs[Stat.SPDEF] & 1) * 32) * 15 / 63); + const iv_val = Math.floor( + (((user.ivs[Stat.HP] & 1) + + (user.ivs[Stat.ATK] & 1) * 2 + + (user.ivs[Stat.DEF] & 1) * 4 + + (user.ivs[Stat.SPD] & 1) * 8 + + (user.ivs[Stat.SPATK] & 1) * 16 + + (user.ivs[Stat.SPDEF] & 1) * 32) + * 15) + / 63, + ); moveType.value = [ - PokemonType.FIGHTING, PokemonType.FLYING, PokemonType.POISON, PokemonType.GROUND, - PokemonType.ROCK, PokemonType.BUG, PokemonType.GHOST, PokemonType.STEEL, - PokemonType.FIRE, PokemonType.WATER, PokemonType.GRASS, PokemonType.ELECTRIC, - PokemonType.PSYCHIC, PokemonType.ICE, PokemonType.DRAGON, PokemonType.DARK ][iv_val]; + PokemonType.FIGHTING, + PokemonType.FLYING, + PokemonType.POISON, + PokemonType.GROUND, + PokemonType.ROCK, + PokemonType.BUG, + PokemonType.GHOST, + PokemonType.STEEL, + PokemonType.FIRE, + PokemonType.WATER, + PokemonType.GRASS, + PokemonType.ELECTRIC, + PokemonType.PSYCHIC, + PokemonType.ICE, + PokemonType.DRAGON, + PokemonType.DARK, + ][iv_val]; return true; } override getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] { const typeHolder = new NumberHolder(move.type); - this.apply(user, user, move, [ typeHolder ]); + this.apply(user, user, move, [typeHolder]); return [typeHolder.value]; } } @@ -5532,7 +5853,7 @@ export class TeraBlastTypeAttr extends VariableMoveTypeAttr { * @param args `[0]` the move's type to be modified * @returns `true` if the move's type was modified; `false` otherwise */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const moveType = args[0]; if (!(moveType instanceof NumberHolder)) { return false; @@ -5576,7 +5897,7 @@ export class TeraStarstormTypeAttr extends VariableMoveTypeAttr { * @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 { + override apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { if (user.isTerastallized && user.hasSpecies(SpeciesId.TERAPAGOS)) { const moveType = args[0] as NumberHolder; @@ -5588,23 +5909,24 @@ export class TeraStarstormTypeAttr extends VariableMoveTypeAttr { } export class MatchUserTypeAttr extends VariableMoveTypeAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const moveType = args[0]; if (!(moveType instanceof NumberHolder)) { return false; } const userTypes = user.getTypes(true); - if (userTypes.includes(PokemonType.STELLAR)) { // will not change to stellar type + if (userTypes.includes(PokemonType.STELLAR)) { + // will not change to stellar type const nonTeraTypes = user.getTypes(); moveType.value = nonTeraTypes[0]; return true; - } else if (userTypes.length > 0) { + } + if (userTypes.length > 0) { moveType.value = userTypes[0]; return true; - } else { - return false; } + return false; } override getTypesForItemSpawn(user: Pokemon, move: Move): PokemonType[] { @@ -5619,7 +5941,7 @@ export class MatchUserTypeAttr extends VariableMoveTypeAttr { * Changes the type of a Pledge move based on the Pledge move combined with it. */ export class CombinedPledgeTypeAttr extends VariableMoveTypeAttr { - override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + override apply(user: Pokemon, _target: Pokemon, move: Move, args: any[]): boolean { const moveType = args[0]; if (!(moveType instanceof NumberHolder)) { return false; @@ -5656,13 +5978,13 @@ export class CombinedPledgeTypeAttr extends VariableMoveTypeAttr { } export class VariableMoveTypeMultiplierAttr extends MoveAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { return false; } } export class NeutralDamageAgainstFlyingTypeMultiplierAttr extends VariableMoveTypeMultiplierAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { if (!target.getTag(BattlerTagType.IGNORE_FLYING)) { const multiplier = args[0] as NumberHolder; //When a flying type is hit, the first hit is always 1x multiplier. @@ -5685,7 +6007,7 @@ export class IceNoEffectTypeAttr extends VariableMoveTypeMultiplierAttr { * @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 { + apply(_user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { const multiplier = args[0] as NumberHolder; if (target.isOfType(PokemonType.ICE)) { multiplier.value = 0; @@ -5696,7 +6018,7 @@ export class IceNoEffectTypeAttr extends VariableMoveTypeMultiplierAttr { } export class FlyingTypeMultiplierAttr extends VariableMoveTypeMultiplierAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { const multiplier = args[0] as NumberHolder; multiplier.value *= target.getAttackTypeEffectiveness(PokemonType.FLYING, user); return true; @@ -5716,7 +6038,7 @@ export class VariableMoveTypeChartAttr extends MoveAttr { * * @returns true if application of the attribute succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { return false; } } @@ -5725,21 +6047,20 @@ export class VariableMoveTypeChartAttr extends MoveAttr { * This class forces Freeze-Dry to be super effective against Water Type. */ export class FreezeDryAttr extends VariableMoveTypeChartAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move, args: any[]): boolean { const multiplier = args[0] as NumberHolder; const defType = args[1] as PokemonType; if (defType === PokemonType.WATER) { multiplier.value = 2; return true; - } else { - return false; } + return false; } } export class OneHitKOAccuracyAttr extends VariableAccuracyAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { const accuracy = args[0] as NumberHolder; if (user.level < target.level) { accuracy.value = 0; @@ -5761,7 +6082,7 @@ export class SheerColdAccuracyAttr extends OneHitKOAccuracyAttr { * the first if/else, or 30/20 depending on the type of the user Pokemon. * @returns Returns true if move is successful, false if misses. */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { const accuracy = args[0] as NumberHolder; if (user.level < target.level) { accuracy.value = 0; @@ -5774,7 +6095,7 @@ export class SheerColdAccuracyAttr extends OneHitKOAccuracyAttr { } export class MissEffectAttr extends MoveAttr { - private missEffectFunc: UserMoveConditionFunc; + private readonly missEffectFunc: UserMoveConditionFunc; constructor(missEffectFunc: UserMoveConditionFunc) { super(); @@ -5782,14 +6103,14 @@ export class MissEffectAttr extends MoveAttr { this.missEffectFunc = missEffectFunc; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, move: Move, _args: any[]): boolean { this.missEffectFunc(user, move); return true; } } export class NoEffectAttr extends MoveAttr { - private noEffectFunc: UserMoveConditionFunc; + private readonly noEffectFunc: UserMoveConditionFunc; constructor(noEffectFunc: UserMoveConditionFunc) { super(); @@ -5797,7 +6118,7 @@ export class NoEffectAttr extends MoveAttr { this.noEffectFunc = noEffectFunc; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, _target: Pokemon, move: Move, _args: any[]): boolean { this.noEffectFunc(user, move); return true; } @@ -5806,30 +6127,32 @@ export class NoEffectAttr extends MoveAttr { /** * Function to deal Crash Damage (1/2 max hp) to the user on apply. */ -const crashDamageFunc: UserMoveConditionFunc = (user: Pokemon, move: Move) => { +const crashDamageFunc: UserMoveConditionFunc = (user: Pokemon, _move: Move) => { const cancelled = new BooleanHolder(false); - applyAbAttrs("BlockNonDirectDamageAbAttr", {pokemon: user, cancelled}); + applyAbAttrs("BlockNonDirectDamageAbAttr", { pokemon: user, cancelled }); if (cancelled.value) { return false; } user.damageAndUpdate(toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT }); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:keptGoingAndCrashed", { pokemonName: getPokemonNameWithAffix(user) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:keptGoingAndCrashed", { pokemonName: getPokemonNameWithAffix(user) }), + ); user.turnData.damageTaken += toDmgValue(user.getMaxHp() / 2); return true; }; -export class TypelessAttr extends MoveAttr { } +export class TypelessAttr extends MoveAttr {} /** -* Attribute used for moves which ignore redirection effects, and always target their original target, i.e. Snipe Shot -* Bypasses Storm Drain, Follow Me, Ally Switch, and the like. -*/ + * Attribute used for moves which ignore redirection effects, and always target their original target, i.e. Snipe Shot + * Bypasses Storm Drain, Follow Me, Ally Switch, and the like. + */ export class BypassRedirectAttr extends MoveAttr { /** `true` if this move only bypasses redirection from Abilities */ public readonly abilitiesOnly: boolean; - constructor(abilitiesOnly: boolean = false) { + constructor(abilitiesOnly = false) { super(); this.abilitiesOnly = abilitiesOnly; } @@ -5840,7 +6163,7 @@ export class FrenzyAttr extends MoveEffectAttr { super(true, { lastHitOnly: true }); } - canApply(user: Pokemon, target: Pokemon, move: Move, args: any[]) { + canApply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]) { return !(this.selfTarget ? user : target).isFainted(); } @@ -5857,7 +6180,7 @@ export class FrenzyAttr extends MoveEffectAttr { if (!user.getTag(BattlerTagType.FRENZY) && user.getMoveQueue().length === 0) { const turnCount = user.randBattleSeedIntRange(1, 2); // excludes initial use for (let i = 0; i < turnCount; i++) { - user.pushMoveQueue({ move: move.id, targets: [ target.getBattlerIndex() ], useMode: MoveUseMode.IGNORE_PP }); + user.pushMoveQueue({ move: move.id, targets: [target.getBattlerIndex()], useMode: MoveUseMode.IGNORE_PP }); } user.addTag(BattlerTagType.FRENZY, turnCount, move.id, user.id); } else { @@ -5903,9 +6226,16 @@ export class AddBattlerTagAttr extends MoveEffectAttr { public tagType: BattlerTagType; public turnCountMin: number; public turnCountMax: number; - private failOnOverlap: boolean; + private readonly failOnOverlap: boolean; - constructor(tagType: BattlerTagType, selfTarget: boolean = false, failOnOverlap = false, turnCountMin = 0, turnCountMax = turnCountMin, lastHitOnly = false) { + constructor( + tagType: BattlerTagType, + selfTarget = false, + failOnOverlap = false, + turnCountMin = 0, + turnCountMax = turnCountMin, + lastHitOnly = false, + ) { super(selfTarget, { lastHitOnly }); this.tagType = tagType; @@ -5922,16 +6252,19 @@ export class AddBattlerTagAttr extends MoveEffectAttr { // TODO: Do any moves actually use chance-based battler tag adding? const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); if (moveChance < 0 || moveChance === 100 || user.randBattleSeedInt(100) < moveChance) { - return (this.selfTarget ? user : target).addTag(this.tagType, user.randBattleSeedIntRange(this.turnCountMin, this.turnCountMax), move.id, user.id); + return (this.selfTarget ? user : target).addTag( + this.tagType, + user.randBattleSeedIntRange(this.turnCountMin, this.turnCountMax), + move.id, + user.id, + ); } return false; } getCondition(): MoveConditionFunc | null { - return this.failOnOverlap - ? (user, target, move) => !(this.selfTarget ? user : target).getTag(this.tagType) - : null; + return this.failOnOverlap ? (user, target, _move) => !(this.selfTarget ? user : target).getTag(this.tagType) : null; } getTagTargetBenefitScore(): number { @@ -6022,7 +6355,9 @@ export class FallDownAttr extends AddBattlerTagAttr { */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!target.isGrounded()) { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:fallDown", { targetPokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:fallDown", { targetPokemonName: getPokemonNameWithAffix(target) }), + ); } return super.apply(user, target, move, args); } @@ -6050,7 +6385,7 @@ export class GulpMissileTagAttr extends MoveEffectAttr { } if (user.hasAbility(AbilityId.GULP_MISSILE) && user.species.speciesId === SpeciesId.CRAMORANT) { - if (user.getHpRatio() >= .5) { + if (user.getHpRatio() >= 0.5) { user.addTag(BattlerTagType.GULP_MISSILE_ARROKUDA, 0, move.id); } else { user.addTag(BattlerTagType.GULP_MISSILE_PIKACHU, 0, move.id); @@ -6061,7 +6396,7 @@ export class GulpMissileTagAttr extends MoveEffectAttr { return false; } - getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getUserBenefitScore(user: Pokemon, _target: Pokemon, _move: Move): number { const isCramorant = user.hasAbility(AbilityId.GULP_MISSILE) && user.species.speciesId === SpeciesId.CRAMORANT; return isCramorant && !user.getTag(GulpMissileTag) ? 10 : 0; } @@ -6091,8 +6426,10 @@ export class JawLockAttr extends AddBattlerTagAttr { * Add the tag to both the user and the target. * The target's tag source is considered to be the user and vice versa */ - return target.addTag(BattlerTagType.TRAPPED, 1, move.id, user.id) - && user.addTag(BattlerTagType.TRAPPED, 1, move.id, target.id); + return ( + target.addTag(BattlerTagType.TRAPPED, 1, move.id, user.id) + && user.addTag(BattlerTagType.TRAPPED, 1, move.id, target.id) + ); } return false; @@ -6100,8 +6437,7 @@ export class JawLockAttr extends AddBattlerTagAttr { } export class CurseAttr extends MoveEffectAttr { - - apply(user: Pokemon, target: Pokemon, move:Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, move: Move, _args: any[]): boolean { if (user.getTypes(true).includes(PokemonType.GHOST)) { if (target.getTag(BattlerTagType.CURSED)) { globalScene.phaseManager.queueMessage(i18next.t("battle:attackFailed")); @@ -6112,17 +6448,16 @@ export class CurseAttr extends MoveEffectAttr { globalScene.phaseManager.queueMessage( i18next.t("battlerTags:cursedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user), - pokemonName: getPokemonNameWithAffix(target) - }) + pokemonName: getPokemonNameWithAffix(target), + }), ); target.addTag(BattlerTagType.CURSED, 0, move.id, user.id); return true; - } else { - globalScene.phaseManager.unshiftNew("StatStageChangePhase", user.getBattlerIndex(), true, [ Stat.ATK, Stat.DEF ], 1); - globalScene.phaseManager.unshiftNew("StatStageChangePhase", user.getBattlerIndex(), true, [ Stat.SPD ], -1); - return true; } + globalScene.phaseManager.unshiftNew("StatStageChangePhase", user.getBattlerIndex(), true, [Stat.ATK, Stat.DEF], 1); + globalScene.phaseManager.unshiftNew("StatStageChangePhase", user.getBattlerIndex(), true, [Stat.SPD], -1); + return true; } } @@ -6164,7 +6499,9 @@ export class ConfuseAttr extends AddBattlerTagAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!this.selfTarget && target.isSafeguarded(user)) { if (move.category === MoveCategory.STATUS) { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:safeguard", { targetName: getPokemonNameWithAffix(target) }), + ); } return false; } @@ -6191,24 +6528,26 @@ export class ProtectAttr extends AddBattlerTagAttr { } getCondition(): MoveConditionFunc { - return ((user, target, move): boolean => { + return (user, _target, _move): boolean => { let timesUsed = 0; for (const turnMove of user.getLastXMoves(-1).slice()) { if ( // Quick & Wide guard increment the Protect counter without using it for fail chance - !(allMoves[turnMove.move].hasAttr("ProtectAttr") || - [MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) || - turnMove.result !== MoveResult.SUCCESS + !( + allMoves[turnMove.move].hasAttr("ProtectAttr") + || [MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move) + ) + || turnMove.result !== MoveResult.SUCCESS ) { break; } - timesUsed++ + timesUsed++; } return timesUsed === 0 || user.randBattleSeedInt(Math.pow(3, timesUsed)) === 0; - }); + }; } } @@ -6247,7 +6586,7 @@ export class RemoveAllSubstitutesAttr extends MoveEffectAttr { * Attribute used when a move can deal damage to {@linkcode BattlerTagType} * Moves that always hit but do not deal double damage: Thunder, Fissure, Sky Uppercut, * Smack Down, Hurricane, Thousand Arrows -*/ + */ export class HitsTagAttr extends MoveAttr { /** The {@linkcode BattlerTagType} this move hits */ public tagType: BattlerTagType; @@ -6260,8 +6599,8 @@ export class HitsTagAttr extends MoveAttr { this.tagType = tagType; } - getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { - return target.getTag(this.tagType) ? this.doubleDamage ? 10 : 5 : 0; + getTargetBenefitScore(_user: Pokemon, target: Pokemon, _move: Move): number { + return target.getTag(this.tagType) ? (this.doubleDamage ? 10 : 5) : 0; } } @@ -6280,10 +6619,10 @@ export class HitsTagForDoubleDamageAttr extends HitsTagAttr { export class AddArenaTagAttr extends MoveEffectAttr { public tagType: ArenaTagType; public turnCount: number; - private failOnOverlap: boolean; + private readonly failOnOverlap: boolean; public selfSideTarget: boolean; - constructor(tagType: ArenaTagType, turnCount = 0, failOnOverlap = false, selfSideTarget: boolean = false) { + constructor(tagType: ArenaTagType, turnCount = 0, failOnOverlap = false, selfSideTarget = false) { super(true); this.tagType = tagType; @@ -6298,8 +6637,14 @@ export class AddArenaTagAttr extends MoveEffectAttr { } // TODO: Why does this check effect chance if nothing uses it? - if ((move.chance < 0 || move.chance === 100 || user.randBattleSeedInt(100) < move.chance) && user.getLastXMoves(1)[0]?.result === MoveResult.SUCCESS) { - const side = ((this.selfSideTarget ? user : target).isPlayer() !== (move.hasAttr("AddArenaTrapTagAttr") && target === user)) ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + if ( + (move.chance < 0 || move.chance === 100 || user.randBattleSeedInt(100) < move.chance) + && user.getLastXMoves(1)[0]?.result === MoveResult.SUCCESS + ) { + 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; } @@ -6309,7 +6654,8 @@ export class AddArenaTagAttr extends MoveEffectAttr { getCondition(): MoveConditionFunc | null { return this.failOnOverlap - ? (_user, target, _move) => !globalScene.arena.getTagOnSide(this.tagType, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY) + ? (_user, target, _move) => + !globalScene.arena.getTagOnSide(this.tagType, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY) : null; } } @@ -6325,9 +6671,13 @@ export class RemoveArenaTagsAttr extends MoveEffectAttr { * the target's side of the field (`false`) * @defaultValue `false` */ - private removeAllTags: boolean + private readonly removeAllTags: boolean; - constructor(tagTypes: readonly [ArenaTagType, ...ArenaTagType[]], removeAllTags = false, options?: MoveEffectAttrOptions) { + constructor( + tagTypes: readonly [ArenaTagType, ...ArenaTagType[]], + removeAllTags = false, + options?: MoveEffectAttrOptions, + ) { super(true, options); this.tagTypes = tagTypes; @@ -6349,8 +6699,8 @@ export class RemoveArenaTagsAttr extends MoveEffectAttr { export class AddArenaTrapTagAttr extends AddArenaTagAttr { getCondition(): MoveConditionFunc { - return (user, target, move) => { - const side = (this.selfSideTarget !== user.isPlayer()) ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER; + return (user, _target, _move) => { + const side = this.selfSideTarget !== user.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER; const tag = globalScene.arena.getTagOnSide(this.tagType, side) as EntryHazardTag; if (!tag) { return true; @@ -6371,11 +6721,14 @@ export class AddArenaTrapTagHitAttr extends AddArenaTagAttr { * @param target {@linkcode Pokemon} target of this move * @param move {@linkcode Move} being used */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, move: Move, _args: any[]): boolean { const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true); const side = (this.selfSideTarget ? user : target).isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; const tag = globalScene.arena.getTagOnSide(this.tagType, side) as EntryHazardTag; - if ((moveChance < 0 || moveChance === 100 || user.randBattleSeedInt(100) < moveChance) && user.getLastXMoves(1)[0]?.result === MoveResult.SUCCESS) { + if ( + (moveChance < 0 || moveChance === 100 || user.randBattleSeedInt(100) < moveChance) + && user.getLastXMoves(1)[0]?.result === MoveResult.SUCCESS + ) { globalScene.arena.addTag(this.tagType, 0, move.id, user.id, side); if (!tag) { return true; @@ -6401,11 +6754,7 @@ export class RemoveArenaTrapAttr extends RemoveArenaTagsAttr { } } -const screenTags = [ - ArenaTagType.REFLECT, - ArenaTagType.LIGHT_SCREEN, - ArenaTagType.AURORA_VEIL -] as const; +const screenTags = [ArenaTagType.REFLECT, ArenaTagType.LIGHT_SCREEN, ArenaTagType.AURORA_VEIL] as const; export class RemoveScreensAttr extends RemoveArenaTagsAttr { constructor(targetBothSides = false) { @@ -6426,24 +6775,43 @@ export class SwapArenaTagsAttr extends MoveEffectAttr { this.validTags = validTags; } - apply(user:Pokemon, target:Pokemon, move:Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (!super.apply(user, target, move, args)) { return false; } - const tagPlayerTemp = globalScene.arena.findTagsOnSide((t => this.validTags.includes(t.tagType)), ArenaTagSide.PLAYER); - const tagEnemyTemp = globalScene.arena.findTagsOnSide((t => this.validTags.includes(t.tagType)), ArenaTagSide.ENEMY); + const tagPlayerTemp = globalScene.arena.findTagsOnSide( + t => this.validTags.includes(t.tagType), + ArenaTagSide.PLAYER, + ); + const tagEnemyTemp = globalScene.arena.findTagsOnSide(t => this.validTags.includes(t.tagType), ArenaTagSide.ENEMY); for (const playerTag of tagPlayerTemp) { globalScene.arena.removeTagOnSide(playerTag.tagType, ArenaTagSide.PLAYER, true); - globalScene.arena.addTag(playerTag.tagType, playerTag.turnCount, playerTag.sourceMove, playerTag.sourceId!, ArenaTagSide.ENEMY, true); // TODO: is the bang correct? + globalScene.arena.addTag( + playerTag.tagType, + playerTag.turnCount, + playerTag.sourceMove, + playerTag.sourceId!, + ArenaTagSide.ENEMY, + true, + ); // TODO: is the bang correct? } for (const enemyTag of tagEnemyTemp) { globalScene.arena.removeTagOnSide(enemyTag.tagType, ArenaTagSide.ENEMY, true); - globalScene.arena.addTag(enemyTag.tagType, enemyTag.turnCount, enemyTag.sourceMove, enemyTag.sourceId!, ArenaTagSide.PLAYER, true); // TODO: is the bang correct? + globalScene.arena.addTag( + enemyTag.tagType, + enemyTag.turnCount, + enemyTag.sourceMove, + enemyTag.sourceId!, + ArenaTagSide.PLAYER, + true, + ); // TODO: is the bang correct? } - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:swapArenaTags", { pokemonName: getPokemonNameWithAffix(user) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:swapArenaTags", { pokemonName: getPokemonNameWithAffix(user) }), + ); return true; } } @@ -6455,7 +6823,7 @@ export class SwapArenaTagsAttr extends MoveEffectAttr { export class AddPledgeEffectAttr extends AddArenaTagAttr { private readonly requiredPledge: MoveId; - constructor(tagType: ArenaTagType, requiredPledge: MoveId, selfSideTarget: boolean = false) { + constructor(tagType: ArenaTagType, requiredPledge: MoveId, selfSideTarget = false) { super(tagType, 4, false, selfSideTarget); this.requiredPledge = requiredPledge; @@ -6490,35 +6858,53 @@ export class RevivalBlessingAttr extends MoveEffectAttr { * @param args N/A * @returns `true` if function succeeds. */ - override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + override apply(user: Pokemon, _target: Pokemon, _move: Move, _args: any[]): boolean { // If user is player, checks if the user has fainted pokemon if (user.isPlayer()) { globalScene.phaseManager.unshiftNew("RevivalBlessingPhase", user); return true; - } else if (user.isEnemy() && user.hasTrainer() && globalScene.getEnemyParty().findIndex((p) => p.isFainted() && !p.isBoss()) > -1) { + } + if ( + user.isEnemy() + && user.hasTrainer() + && globalScene.getEnemyParty().findIndex(p => p.isFainted() && !p.isBoss()) > -1 + ) { // If used by an enemy trainer with at least one fainted non-boss Pokemon, this // revives one of said Pokemon selected at random. - const faintedPokemon = globalScene.getEnemyParty().filter((p) => p.isFainted() && !p.isBoss()); + const faintedPokemon = globalScene.getEnemyParty().filter(p => p.isFainted() && !p.isBoss()); const pokemon = faintedPokemon[user.randBattleSeedInt(faintedPokemon.length)]; - const slotIndex = globalScene.getEnemyParty().findIndex((p) => pokemon.id === p.id); + const slotIndex = globalScene.getEnemyParty().findIndex(p => pokemon.id === p.id); pokemon.resetStatus(true, false, false, true); pokemon.heal(Math.min(toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp())); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:revivalBlessing", { pokemonName: getPokemonNameWithAffix(pokemon) }), 0, true); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:revivalBlessing", { pokemonName: getPokemonNameWithAffix(pokemon) }), + 0, + true, + ); const allyPokemon = user.getAlly(); - if (globalScene.currentBattle.double && globalScene.getEnemyParty().length > 1 && allyPokemon != null) { - // Handle cases where revived pokemon needs to get switched in on same turn - if (allyPokemon.isFainted() || allyPokemon === pokemon) { - // Enemy switch phase should be removed and replaced with the revived pkmn switching in - globalScene.phaseManager.tryRemovePhase("SwitchSummonPhase", phase => phase.getFieldIndex() === slotIndex); - // If the pokemon being revived was alive earlier in the turn, cancel its move - // (revived pokemon can't move in the turn they're brought back) - // TODO: might make sense to move this to `FaintPhase` after checking for Rev Seed (rather than handling it in the move) - globalScene.phaseManager.getMovePhase((phase: MovePhase) => phase.pokemon === pokemon)?.cancel(); - if (user.fieldPosition === FieldPosition.CENTER) { - user.setFieldPosition(FieldPosition.LEFT); - } - globalScene.phaseManager.unshiftNew("SwitchSummonPhase", SwitchType.SWITCH, allyPokemon.getFieldIndex(), slotIndex, false, false); + if ( + globalScene.currentBattle.double + && globalScene.getEnemyParty().length > 1 + && allyPokemon != null // Handle cases where revived pokemon needs to get switched in on same turn + && (allyPokemon.isFainted() || allyPokemon === pokemon) + ) { + // Enemy switch phase should be removed and replaced with the revived pkmn switching in + globalScene.phaseManager.tryRemovePhase("SwitchSummonPhase", phase => phase.getFieldIndex() === slotIndex); + // If the pokemon being revived was alive earlier in the turn, cancel its move + // TODO: check if revived pokemon shouldn't be able to move in the same turn they're brought back + // TODO: might make sense to move this to `FaintPhase` after checking for Rev Seed (rather than handling it in the move) + globalScene.phaseManager.getMovePhase((phase: MovePhase) => phase.pokemon === pokemon)?.cancel(); + if (user.fieldPosition === FieldPosition.CENTER) { + user.setFieldPosition(FieldPosition.LEFT); } + globalScene.phaseManager.unshiftNew( + "SwitchSummonPhase", + SwitchType.SWITCH, + allyPokemon.getFieldIndex(), + slotIndex, + false, + false, + ); } return true; } @@ -6526,13 +6912,15 @@ export class RevivalBlessingAttr extends MoveEffectAttr { } getCondition(): MoveConditionFunc { - return (user, target, move) => - user.hasTrainer() && - (user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).some((p: Pokemon) => p.isFainted() && !p.isBoss()); + return (user, _target, _move) => + user.hasTrainer() + && (user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).some( + (p: Pokemon) => p.isFainted() && !p.isBoss(), + ); } override getUserBenefitScore(user: Pokemon, _target: Pokemon, _move: Move): number { - if (user.hasTrainer() && globalScene.getEnemyParty().some((p) => p.isFainted() && !p.isBoss())) { + if (user.hasTrainer() && globalScene.getEnemyParty().some(p => p.isFainted() && !p.isBoss())) { return 20; } @@ -6541,18 +6929,21 @@ export class RevivalBlessingAttr extends MoveEffectAttr { } export class ForceSwitchOutAttr extends MoveEffectAttr { - constructor( - private selfSwitch: boolean = false, - private switchType: SwitchType = SwitchType.SWITCH - ) { + private readonly selfSwitch: boolean; + private readonly switchType: SwitchType; + + constructor(selfSwitch = false, switchType: SwitchType = SwitchType.SWITCH) { super(false, { lastHitOnly: true }); + + this.selfSwitch = selfSwitch; + this.switchType = switchType; } isBatonPass() { return this.switchType === SwitchType.BATON_PASS; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, move: Move, _args: any[]): boolean { // Check if the move category is not STATUS or if the switch out condition is not met if (!this.getSwitchOutCondition()(user, target, move)) { return false; @@ -6567,15 +6958,15 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { if (switchOutTarget.isPlayer()) { /** - * Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch - * If it did, the user of U-turn or Volt Switch will not be switched out. - */ - if (target.getAbility().hasAttr("PostDamageForceSwitchAbAttr") - && [ MoveId.U_TURN, MoveId.VOLT_SWITCH, MoveId.FLIP_TURN ].includes(move.id) + * Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch + * If it did, the user of U-turn or Volt Switch will not be switched out. + */ + if ( + target.getAbility().hasAttr("PostDamageForceSwitchAbAttr") + && [MoveId.U_TURN, MoveId.VOLT_SWITCH, MoveId.FLIP_TURN].includes(move.id) + && this.hpDroppedBelowHalf(target) ) { - if (this.hpDroppedBelowHalf(target)) { - return false; - } + return false; } // Find indices of off-field Pokemon that are eligible to be switched into @@ -6586,7 +6977,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { } }); - if (eligibleNewIndices.length < 1) { + if (eligibleNewIndices.length === 0) { return false; } @@ -6600,32 +6991,38 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { switchOutTarget.getFieldIndex(), slotIndex, false, - true + true, ); } else { switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); globalScene.phaseManager.queueDeferred( "SwitchPhase", - this.switchType, - switchOutTarget.getFieldIndex(), - true, - true + this.switchType, + switchOutTarget.getFieldIndex(), + true, + true, ); return true; } } return false; - } else if (globalScene.currentBattle.battleType !== BattleType.WILD) { // Switch out logic for enemy trainers + } + 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() && (!isPartnerTrainer || pokemon.trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)) { + if ( + pokemon.isAllowedInBattle() + && !pokemon.isOnField() + && (!isPartnerTrainer || pokemon.trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot) + ) { eligibleNewIndices.push(index); } }); - if (eligibleNewIndices.length < 1) { + if (eligibleNewIndices.length === 0) { return false; } @@ -6635,11 +7032,11 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { const slotIndex = eligibleNewIndices[user.randBattleSeedInt(eligibleNewIndices.length)]; globalScene.phaseManager.queueDeferred( "SwitchSummonPhase", - this.switchType, - switchOutTarget.getFieldIndex(), - slotIndex, - false, - false + this.switchType, + switchOutTarget.getFieldIndex(), + slotIndex, + false, + false, ); } else { switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); @@ -6647,30 +7044,38 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { "SwitchSummonPhase", this.switchType, switchOutTarget.getFieldIndex(), - (globalScene.currentBattle.trainer ? globalScene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0), + globalScene.currentBattle.trainer + ? globalScene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) + : 0, + false, false, - false ); } } - } else { // Switch out logic for wild pokemon + } else { + // Switch out logic for wild pokemon /** - * Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch - * If it did, the user of U-turn or Volt Switch will not be switched out. - */ - if (target.getAbility().hasAttr("PostDamageForceSwitchAbAttr") - && [ MoveId.U_TURN, MoveId.VOLT_SWITCH, MoveId.FLIP_TURN ].includes(move.id) + * Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch + * If it did, the user of U-turn or Volt Switch will not be switched out. + */ + if ( + target.getAbility().hasAttr("PostDamageForceSwitchAbAttr") + && [MoveId.U_TURN, MoveId.VOLT_SWITCH, MoveId.FLIP_TURN].includes(move.id) + && this.hpDroppedBelowHalf(target) ) { - if (this.hpDroppedBelowHalf(target)) { - return false; - } + return false; } const allyPokemon = switchOutTarget.getAlly(); if (switchOutTarget.hp > 0) { switchOutTarget.leaveField(false); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), + null, + true, + 500, + ); // in double battles redirect potential moves off fled pokemon if (globalScene.currentBattle.double && allyPokemon != null) { @@ -6682,37 +7087,39 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { globalScene.clearEnemyHeldItemModifiers(switchOutTarget); if (!allyPokemon?.isActive(true) && switchOutTarget.hp) { - globalScene.phaseManager.pushNew("BattleEndPhase", false); + globalScene.phaseManager.pushNew("BattleEndPhase", false); - if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { - globalScene.phaseManager.pushNew("SelectBiomePhase"); - } + if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) { + globalScene.phaseManager.pushNew("SelectBiomePhase"); + } - globalScene.phaseManager.pushNew("NewBattlePhase"); + globalScene.phaseManager.pushNew("NewBattlePhase"); } } - return true; + return true; } getCondition(): MoveConditionFunc { - return (user, target, move) => (move.category !== MoveCategory.STATUS || this.getSwitchOutCondition()(user, target, move)); + return (user, target, move) => + move.category !== MoveCategory.STATUS || this.getSwitchOutCondition()(user, target, move); } getFailedText(_user: Pokemon, target: Pokemon, _move: Move): string | undefined { const cancelled = new BooleanHolder(false); - applyAbAttrs("ForceSwitchOutImmunityAbAttr", {pokemon: target, cancelled}); + applyAbAttrs("ForceSwitchOutImmunityAbAttr", { pokemon: target, cancelled }); if (cancelled.value) { return i18next.t("moveTriggers:cannotBeSwitchedOut", { pokemonName: getPokemonNameWithAffix(target) }); } } - getSwitchOutCondition(): MoveConditionFunc { return (user, target, move) => { - const switchOutTarget = (this.selfSwitch ? user : target); + const switchOutTarget = this.selfSwitch ? user : target; const player = switchOutTarget.isPlayer(); - const forceSwitchAttr = move.getAttrs("ForceSwitchOutAttr").find(attr => attr.switchType === SwitchType.FORCE_SWITCH); + const forceSwitchAttr = move + .getAttrs("ForceSwitchOutAttr") + .find(attr => attr.switchType === SwitchType.FORCE_SWITCH); if (!this.selfSwitch) { if (move.hitsSubstitute(user, target)) { @@ -6721,9 +7128,9 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { // Check if the move is Roar or Whirlwind and if there is a trainer with only Pokémon left. if (forceSwitchAttr && globalScene.currentBattle.trainer) { - const enemyParty = globalScene.getEnemyParty(); - // Filter out any Pokémon that are not allowed in battle (e.g. fainted ones) - const remainingPokemon = enemyParty.filter(p => p.hp > 0 && p.isAllowedInBattle()); + const enemyParty = globalScene.getEnemyParty(); + // Filter out any Pokémon that are not allowed in battle (e.g. fainted ones) + const remainingPokemon = enemyParty.filter(p => p.hp > 0 && p.isAllowedInBattle()); if (remainingPokemon.length <= 1) { return false; } @@ -6735,30 +7142,40 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { return false; } - if (!player && globalScene.currentBattle.isBattleMysteryEncounter() && !globalScene.currentBattle.mysteryEncounter?.fleeAllowed) { + if ( + !player + && globalScene.currentBattle.isBattleMysteryEncounter() + && !globalScene.currentBattle.mysteryEncounter?.fleeAllowed + ) { // Don't allow wild opponents to be force switched during MEs with flee disabled return false; } const blockedByAbility = new BooleanHolder(false); - applyAbAttrs("ForceSwitchOutImmunityAbAttr", {pokemon: target, cancelled: blockedByAbility}); + applyAbAttrs("ForceSwitchOutImmunityAbAttr", { pokemon: target, cancelled: blockedByAbility }); if (blockedByAbility.value) { return false; } } - if (!player && globalScene.currentBattle.battleType === BattleType.WILD) { // 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); + 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 party.filter(p => p.isAllowedInBattle() && !p.isOnField() - && (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > 0; + return ( + party.filter( + p => + p.isAllowedInBattle() + && !p.isOnField() + && (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot), + ).length > 0 + ); }; } @@ -6766,19 +7183,24 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { if (!globalScene.getEnemyParty().find(p => p.isActive() && !p.isOnField())) { return -20; } - let ret = this.selfSwitch ? Math.floor((1 - user.getHpRatio()) * 20) : super.getUserBenefitScore(user, target, move); + let ret = this.selfSwitch + ? Math.floor((1 - user.getHpRatio()) * 20) + : super.getUserBenefitScore(user, target, move); if (this.selfSwitch && this.isBatonPass()) { - const statStageTotal = user.getStatStages().reduce((s: number, total: number) => total += s, 0); - ret = ret / 2 + (Phaser.Tweens.Builders.GetEaseFunction("Sine.easeOut")(Math.min(Math.abs(statStageTotal), 10) / 10) * (statStageTotal >= 0 ? 10 : -10)); + const statStageTotal = user.getStatStages().reduce((s: number, total: number) => (total += s), 0); + ret = + ret / 2 + + Phaser.Tweens.Builders.GetEaseFunction("Sine.easeOut")(Math.min(Math.abs(statStageTotal), 10) / 10) + * (statStageTotal >= 0 ? 10 : -10); } return ret; } /** - * Helper function to check if the Pokémon's health is below half after taking damage. - * Used for an edge case interaction with Wimp Out/Emergency Exit. - * If the Ability activates due to being hit by U-turn or Volt Switch, the user of that move will not be switched out. - */ + * Helper function to check if the Pokémon's health is below half after taking damage. + * Used for an edge case interaction with Wimp Out/Emergency Exit. + * If the Ability activates due to being hit by U-turn or Volt Switch, the user of that move will not be switched out. + */ hpDroppedBelowHalf(target: Pokemon): boolean { const pokemonHealth = target.hp; const maxPokemonHealth = target.getMaxHp(); @@ -6798,21 +7220,20 @@ export class ChillyReceptionAttr extends ForceSwitchOutAttr { getCondition(): MoveConditionFunc { // chilly reception move will go through if the weather is change-able to snow, or the user can switch out, else move will fail - return (user, target, move) => globalScene.arena.weather?.weatherType !== WeatherType.SNOW || super.getSwitchOutCondition()(user, target, move); + return (user, target, move) => + globalScene.arena.weather?.weatherType !== WeatherType.SNOW || super.getSwitchOutCondition()(user, target, move); } } export class RemoveTypeAttr extends MoveEffectAttr { - // TODO: Remove the message callback - private removedType: PokemonType; - private messageCallback: ((user: Pokemon) => void) | undefined; + private readonly removedType: PokemonType; + private readonly messageCallback: ((user: Pokemon) => void) | undefined; constructor(removedType: PokemonType, messageCallback?: (user: Pokemon) => void) { super(true, { trigger: MoveEffectTrigger.POST_TARGET }); this.removedType = removedType; this.messageCallback = messageCallback; - } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { @@ -6820,7 +7241,8 @@ export class RemoveTypeAttr extends MoveEffectAttr { return false; } - if (user.isTerastallized && user.getTeraType() === this.removedType) { // active tera types cannot be removed + if (user.isTerastallized && user.getTeraType() === this.removedType) { + // active tera types cannot be removed return false; } @@ -6832,7 +7254,6 @@ export class RemoveTypeAttr extends MoveEffectAttr { user.summonData.types = modifiedTypes; user.updateInfo(); - if (this.messageCallback) { this.messageCallback(user); } @@ -6858,13 +7279,19 @@ export class CopyTypeAttr extends MoveEffectAttr { user.summonData.types = targetTypes; user.updateInfo(); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:copyType", { pokemonName: getPokemonNameWithAffix(user), targetPokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:copyType", { + pokemonName: getPokemonNameWithAffix(user), + targetPokemonName: getPokemonNameWithAffix(target), + }), + ); return true; } getCondition(): MoveConditionFunc { - return (user, target, move) => target.getTypes()[0] !== PokemonType.UNKNOWN || target.summonData.addedType !== null; + return (_user, target, _move) => + target.getTypes()[0] !== PokemonType.UNKNOWN || target.summonData.addedType !== null; } } @@ -6886,10 +7313,15 @@ export class CopyBiomeTypeAttr extends MoveEffectAttr { typeChange = this.getTypeForBiome(globalScene.arena.biomeType); } - user.summonData.types = [ typeChange ]; + user.summonData.types = [typeChange]; user.updateInfo(); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), typeName: i18next.t(`pokemonInfo:type.${toCamelCase(PokemonType[typeChange])}`) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:transformedIntoType", { + pokemonName: getPokemonNameWithAffix(user), + typeName: i18next.t(`pokemonInfo:type.${toCamelCase(PokemonType[typeChange])}`), + }), + ); return true; } @@ -6986,29 +7418,38 @@ export class CopyBiomeTypeAttr extends MoveEffectAttr { * Used by {@linkcode MoveId.SOAK} and {@linkcode MoveId.MAGIC_POWDER}. */ export class ChangeTypeAttr extends MoveEffectAttr { - private type: PokemonType; + private readonly type: PokemonType; constructor(type: PokemonType) { super(false); this.type = type; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - target.summonData.types = [ this.type ]; + apply(_user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { + target.summonData.types = [this.type]; target.updateInfo(); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:transformedIntoType", { pokemonName: getPokemonNameWithAffix(target), typeName: i18next.t(`pokemonInfo:type.${toCamelCase(PokemonType[this.type])}`) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:transformedIntoType", { + pokemonName: getPokemonNameWithAffix(target), + typeName: i18next.t(`pokemonInfo:type.${toCamelCase(PokemonType[this.type])}`), + }), + ); return true; } getCondition(): MoveConditionFunc { - return (user, target, move) => !target.isTerastallized && !target.hasAbility(AbilityId.MULTITYPE) && !target.hasAbility(AbilityId.RKS_SYSTEM) && !(target.getTypes().length === 1 && target.getTypes()[0] === this.type); + return (_user, target, _move) => + !target.isTerastallized + && !target.hasAbility(AbilityId.MULTITYPE) + && !target.hasAbility(AbilityId.RKS_SYSTEM) + && !(target.getTypes().length === 1 && target.getTypes()[0] === this.type); } } export class AddTypeAttr extends MoveEffectAttr { - private type: PokemonType; + private readonly type: PokemonType; constructor(type: PokemonType) { super(false); @@ -7016,17 +7457,22 @@ export class AddTypeAttr extends MoveEffectAttr { this.type = type; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { target.summonData.addedType = this.type; target.updateInfo(); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:addType", { typeName: i18next.t(`pokemonInfo:type.${toCamelCase(PokemonType[this.type])}`), pokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:addType", { + typeName: i18next.t(`pokemonInfo:type.${toCamelCase(PokemonType[this.type])}`), + pokemonName: getPokemonNameWithAffix(target), + }), + ); return true; } getCondition(): MoveConditionFunc { - return (user, target, move) => !target.isTerastallized && !target.getTypes().includes(this.type); + return (_user, target, _move) => !target.isTerastallized && !target.getTypes().includes(this.type); } } @@ -7041,8 +7487,13 @@ export class FirstMoveTypeAttr extends MoveEffectAttr { } const firstMoveType = target.getMoveset()[0].getMove().type; - user.summonData.types = [ firstMoveType ]; - globalScene.phaseManager.queueMessage(i18next.t("battle:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), type: i18next.t(`pokemonInfo:type.${toCamelCase(PokemonType[firstMoveType])}`) })); + user.summonData.types = [firstMoveType]; + globalScene.phaseManager.queueMessage( + i18next.t("battle:transformedIntoType", { + pokemonName: getPokemonNameWithAffix(user), + type: i18next.t(`pokemonInfo:type.${toCamelCase(PokemonType[firstMoveType])}`), + }), + ); return true; } @@ -7056,7 +7507,7 @@ class CallMoveAttr extends OverrideMoveEffectAttr { protected invalidMoves: ReadonlySet; protected hasTarget: boolean; - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, move: Move, _args: any[]): boolean { // Get eligible targets for move, failing if we can't target anything const replaceMoveTarget = move.moveTarget === MoveTarget.NEAR_OTHER ? MoveTarget.NEAR_ENEMY : undefined; const moveTargets = getMoveTargets(user, move.id, replaceMoveTarget); @@ -7067,14 +7518,24 @@ class CallMoveAttr extends OverrideMoveEffectAttr { // Spread moves and ones with only 1 valid target will use their normal targeting. // If not, target the Mirror Move recipient or else a random enemy in our target list - const targets = moveTargets.multiple || moveTargets.targets.length === 1 - ? moveTargets.targets - : [this.hasTarget - ? target.getBattlerIndex() - : moveTargets.targets[user.randBattleSeedInt(moveTargets.targets.length)]]; + const targets = + moveTargets.multiple || moveTargets.targets.length === 1 + ? moveTargets.targets + : [ + this.hasTarget + ? target.getBattlerIndex() + : moveTargets.targets[user.randBattleSeedInt(moveTargets.targets.length)], + ]; globalScene.phaseManager.unshiftNew("LoadMoveAnimPhase", move.id); - globalScene.phaseManager.unshiftNew("MovePhase", user, targets, new PokemonMove(move.id), MoveUseMode.FOLLOW_UP, MovePhaseTimingModifier.FIRST); + globalScene.phaseManager.unshiftNew( + "MovePhase", + user, + targets, + new PokemonMove(move.id), + MoveUseMode.FOLLOW_UP, + MovePhaseTimingModifier.FIRST, + ); return true; } } @@ -7107,17 +7568,18 @@ export class RandomMoveAttr extends CallMoveAttr { */ override apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { // TODO: Move this into the constructor to avoid constructing this every call - const moveIds = getEnumValues(MoveId).map(m => !this.invalidMoves.has(m) && !allMoves[m].name.endsWith(" (N)") ? m : MoveId.NONE); + const moveIds = getEnumValues(MoveId).map(m => + !this.invalidMoves.has(m) && !allMoves[m].name.endsWith(" (N)") ? m : MoveId.NONE, + ); let moveId: MoveId = MoveId.NONE; const moveStatus = new BooleanHolder(true); do { moveId = this.getMoveOverride() ?? moveIds[user.randBattleSeedInt(moveIds.length)]; moveStatus.value = moveId !== MoveId.NONE; if (user.isPlayer()) { - applyChallenges(ChallengeType.POKEMON_MOVE, moveId, moveStatus); + applyChallenges(ChallengeType.POKEMON_MOVE, moveId, moveStatus); } - } - while (!moveStatus.value); + } while (!moveStatus.value); return super.apply(user, target, allMoves[moveId], args); } } @@ -7131,9 +7593,9 @@ export class RandomMoveAttr extends CallMoveAttr { * Invalid moves are indicated by what is passed in to invalidMoves: {@linkcode invalidAssistMoves} or {@linkcode invalidSleepTalkMoves} */ export class RandomMovesetMoveAttr extends CallMoveAttr { - private includeParty: boolean; + private readonly includeParty: boolean; private moveId: number; - constructor(invalidMoves: ReadonlySet, includeParty: boolean = false) { + constructor(invalidMoves: ReadonlySet, includeParty = false) { super(); this.includeParty = includeParty; this.invalidMoves = invalidMoves; @@ -7146,18 +7608,18 @@ export class RandomMovesetMoveAttr extends CallMoveAttr { * @param move Move being used * @param args Unused */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { return super.apply(user, target, allMoves[this.moveId], args); } getCondition(): MoveConditionFunc { - return (user, target, move) => { + return (user, _target, _move) => { // includeParty will be true for Assist, false for Sleep Talk let allies: Pokemon[]; if (this.includeParty) { allies = (user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty()).filter(p => p !== user); } else { - allies = [ user ]; + allies = [user]; } const partyMoveset = allies.flatMap(p => p.moveset); const moves = partyMoveset.filter(m => !this.invalidMoves.has(m.moveId) && !m.getMove().name.endsWith(" (N)")); @@ -7173,10 +7635,10 @@ export class RandomMovesetMoveAttr extends CallMoveAttr { // TODO: extend CallMoveAttr export class NaturePowerAttr extends OverrideMoveEffectAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { let moveId = MoveId.NONE; switch (globalScene.arena.getTerrainType()) { - // this allows terrains to 'override' the biome move + // this allows terrains to 'override' the biome move case TerrainType.NONE: switch (globalScene.arena.biomeType) { case BiomeId.TOWN: @@ -7306,7 +7768,14 @@ export class NaturePowerAttr extends OverrideMoveEffectAttr { // Load the move's animation if we didn't already and unshift a new usage phase globalScene.phaseManager.unshiftNew("LoadMoveAnimPhase", moveId); - globalScene.phaseManager.unshiftNew("MovePhase", user, [ target.getBattlerIndex() ], new PokemonMove(moveId), MoveUseMode.FOLLOW_UP, MovePhaseTimingModifier.FIRST); + globalScene.phaseManager.unshiftNew( + "MovePhase", + user, + [target.getBattlerIndex()], + new PokemonMove(moveId), + MoveUseMode.FOLLOW_UP, + MovePhaseTimingModifier.FIRST, + ); return true; } } @@ -7316,7 +7785,7 @@ export class NaturePowerAttr extends OverrideMoveEffectAttr { * Used for {@linkcode MoveId.COPYCAT} and {@linkcode MoveId.MIRROR_MOVE} */ export class CopyMoveAttr extends CallMoveAttr { - private mirrorMove: boolean; + private readonly mirrorMove: boolean; constructor(mirrorMove: boolean, invalidMoves: ReadonlySet = new Set()) { super(); this.mirrorMove = mirrorMove; @@ -7326,13 +7795,17 @@ export class CopyMoveAttr extends CallMoveAttr { apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { this.hasTarget = this.mirrorMove; // bang is correct as condition func returns `false` and fails move if no last move exists - const lastMove = this.mirrorMove ? target.getLastNonVirtualMove(false, false)!.move : globalScene.currentBattle.lastMove; + const lastMove = this.mirrorMove + ? target.getLastNonVirtualMove(false, false)!.move + : globalScene.currentBattle.lastMove; return super.apply(user, target, allMoves[lastMove], args); } getCondition(): MoveConditionFunc { return (_user, target, _move) => { - const lastMove = this.mirrorMove ? target.getLastNonVirtualMove(false, false)?.move : globalScene.currentBattle.lastMove; + const lastMove = this.mirrorMove + ? target.getLastNonVirtualMove(false, false)?.move + : globalScene.currentBattle.lastMove; return lastMove != null && !this.invalidMoves.has(lastMove); }; } @@ -7343,7 +7816,7 @@ export class CopyMoveAttr extends CallMoveAttr { * * Used by {@linkcode MoveId.INSTRUCT | Instruct}. * @see [Instruct on Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Instruct_(move)) -*/ + */ export class RepeatMoveAttr extends MoveEffectAttr { private movesetMove: PokemonMove; constructor() { @@ -7361,13 +7834,15 @@ export class RepeatMoveAttr extends MoveEffectAttr { // bangs are justified as Instruct fails if no prior move or moveset move exists // TODO: How does instruct work when copying a move called via Copycat that the user itself knows? const lastMove = target.getLastNonVirtualMove()!; - const movesetMove = target.getMoveset().find(m => m.moveId === lastMove?.move)! + const movesetMove = target.getMoveset().find(m => m.moveId === lastMove?.move)!; // If the last move used can hit more than one target or has variable targets, // re-compute the targets for the attack (mainly for alternating double/single battles) // Rampaging moves (e.g. Outrage) are not included due to being incompatible with Instruct, // nor is Dragon Darts (due to its smart targeting bypassing normal target selection) - let moveTargets = this.movesetMove.getMove().isMultiTarget() ? getMoveTargets(target, this.movesetMove.moveId).targets : lastMove.targets; + let moveTargets = this.movesetMove.getMove().isMultiTarget() + ? getMoveTargets(target, this.movesetMove.moveId).targets + : lastMove.targets; // In the event the instructed move's only target is a fainted opponent, redirect it to an alive ally if possible. // Normally, all yet-unexecuted move phases would swap targets after any foe faints or flees (see `redirectPokemonMoves` in `battle-scene.ts`), @@ -7380,17 +7855,26 @@ export class RepeatMoveAttr extends MoveEffectAttr { && firstTarget !== target.getAlly() ) { const ally = firstTarget.getAlly(); - if (ally != null && ally.isActive()) { - moveTargets = [ ally.getBattlerIndex() ]; + if (ally?.isActive()) { + moveTargets = [ally.getBattlerIndex()]; } } - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:instructingMove", { - userPokemonName: getPokemonNameWithAffix(user), - targetPokemonName: getPokemonNameWithAffix(target) - })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:instructingMove", { + userPokemonName: getPokemonNameWithAffix(user), + targetPokemonName: getPokemonNameWithAffix(target), + }), + ); target.turnData.extraTurns++; - globalScene.phaseManager.unshiftNew("MovePhase", target, moveTargets, movesetMove, MoveUseMode.NORMAL, MovePhaseTimingModifier.FIRST); + globalScene.phaseManager.unshiftNew( + "MovePhase", + target, + moveTargets, + movesetMove, + MoveUseMode.NORMAL, + MovePhaseTimingModifier.FIRST, + ); return true; } @@ -7464,12 +7948,14 @@ export class RepeatMoveAttr extends MoveEffectAttr { // TODO: Add Max/G-Max/Z-Move blockage if or when they are implemented ]; - if (!lastMove?.move // no move to instruct + if ( + !lastMove?.move // no move to instruct || !movesetMove // called move not in target's moveset (forgetting the move, etc.) - || movesetMove.ppUsed === movesetMove.getMovePp() // move out of pp - // TODO: This next line is likely redundant as all charging moves are in the above list + || movesetMove.ppUsed === movesetMove.getMovePp() // move out of pp // TODO: This next line is likely redundant as all charging moves are in the above list || allMoves[lastMove.move].isChargingMove() // called move is a charging/recharging move - || uninstructableMoves.includes(lastMove.move)) { // called move is in the banlist + || uninstructableMoves.includes(lastMove.move) + ) { + // called move is in the banlist return false; } this.movesetMove = movesetMove; @@ -7477,14 +7963,14 @@ export class RepeatMoveAttr extends MoveEffectAttr { }; } - getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getTargetBenefitScore(_user: Pokemon, _target: Pokemon, _move: Move): number { // TODO: Make the AI actually use instruct /* Ideally, the AI would score instruct based on the scorings of the on-field pokemons' - * last used moves at the time of using Instruct (by the time the instructor gets to act) - * with respect to the user's side. - * In 99.9% of cases, this would be the pokemon's ally (unless the target had last - * used a move like Decorate on the user or its ally) - */ + * last used moves at the time of using Instruct (by the time the instructor gets to act) + * with respect to the user's side. + * In 99.9% of cases, this would be the pokemon's ally (unless the target had last + * used a move like Decorate on the user or its ally) + */ return 2; } } @@ -7509,7 +7995,7 @@ export class ReducePpMoveAttr extends MoveEffectAttr { * @param args - N/A * @returns always `true` */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(_user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { /** The last move the target themselves used */ const lastMove = target.getLastNonVirtualMove(); const movesetMove = target.getMoveset().find(m => m.moveId === lastMove?.move)!; // bang is correct as condition prevents this from being nullish @@ -7517,22 +8003,28 @@ export class ReducePpMoveAttr extends MoveEffectAttr { movesetMove.ppUsed = Math.min(lastPpUsed + this.reduction, movesetMove.getMovePp()); globalScene.eventTarget.dispatchEvent(new MoveUsedEvent(target.id, movesetMove.getMove(), movesetMove.ppUsed)); - globalScene.phaseManager.queueMessage(i18next.t("battle:ppReduced", { targetName: getPokemonNameWithAffix(target), moveName: movesetMove.getName(), reduction: (movesetMove.ppUsed) - lastPpUsed })); + globalScene.phaseManager.queueMessage( + i18next.t("battle:ppReduced", { + targetName: getPokemonNameWithAffix(target), + moveName: movesetMove.getName(), + reduction: movesetMove.ppUsed - lastPpUsed, + }), + ); return true; } getCondition(): MoveConditionFunc { - return (user, target, move) => { + return (_user, target, _move) => { const lastMove = target.getLastNonVirtualMove(); - const movesetMove = target.getMoveset().find(m => m.moveId === lastMove?.move) + const movesetMove = target.getMoveset().find(m => m.moveId === lastMove?.move); return !!movesetMove?.getPpRatio(); }; } - getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + getTargetBenefitScore(_user: Pokemon, target: Pokemon, _move: Move): number { const lastMove = target.getLastNonVirtualMove(); - const movesetMove = target.getMoveset().find(m => m.moveId === lastMove?.move) + const movesetMove = target.getMoveset().find(m => m.moveId === lastMove?.move); if (!movesetMove) { return 0; } @@ -7544,7 +8036,6 @@ export class ReducePpMoveAttr extends MoveEffectAttr { return (value / 4) * ppLeft; } return value; - } } @@ -7553,10 +8044,6 @@ export class ReducePpMoveAttr extends MoveEffectAttr { * Used for Eerie Spell. */ export class AttackReducePpMoveAttr extends ReducePpMoveAttr { - constructor(reduction: number) { - super(reduction); - } - /** * Checks if the target has used a move prior to the attack. PP-reduction is applied through the super class if so. * @@ -7587,7 +8074,7 @@ export class AttackReducePpMoveAttr extends ReducePpMoveAttr { } } -const targetMoveCopiableCondition: MoveConditionFunc = (user, target, move) => { +const targetMoveCopiableCondition: MoveConditionFunc = (_user, target, _move) => { const copiableMove = target.getLastNonVirtualMove(); if (!copiableMove?.move) { return false; @@ -7607,8 +8094,8 @@ const targetMoveCopiableCondition: MoveConditionFunc = (user, target, move) => { * Used by {@linkcode MoveId.MIMIC}. */ export class MovesetCopyMoveAttr extends OverrideMoveEffectAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const lastMove = target.getLastNonVirtualMove() + apply(user: Pokemon, target: Pokemon, move: Move, _args: any[]): boolean { + const lastMove = target.getLastNonVirtualMove(); if (!lastMove?.move) { return false; } @@ -7625,7 +8112,9 @@ export class MovesetCopyMoveAttr extends OverrideMoveEffectAttr { user.summonData.moveset = user.getMoveset().slice(0); user.summonData.moveset[thisMoveIndex] = new PokemonMove(copiedMove.id); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:copiedMove", { pokemonName: getPokemonNameWithAffix(user), moveName: copiedMove.name })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:copiedMove", { pokemonName: getPokemonNameWithAffix(user), moveName: copiedMove.name }), + ); return true; } @@ -7661,7 +8150,7 @@ export class SketchAttr extends MoveEffectAttr { return false; } - const targetMove = target.getLastNonVirtualMove() + const targetMove = target.getLastNonVirtualMove(); if (!targetMove) { // failsafe for TS compiler return false; @@ -7675,7 +8164,12 @@ export class SketchAttr extends MoveEffectAttr { user.setMove(sketchIndex, sketchedMove.id); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:sketchedMove", { pokemonName: getPokemonNameWithAffix(user), moveName: sketchedMove.name })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:sketchedMove", { + pokemonName: getPokemonNameWithAffix(user), + moveName: sketchedMove.name, + }), + ); return true; } @@ -7687,9 +8181,11 @@ export class SketchAttr extends MoveEffectAttr { } const targetMove = target.getLastNonVirtualMove(); - return targetMove != null + return ( + targetMove != null && !invalidSketchMoves.has(targetMove.move) && user.getMoveset().every(m => m.moveId !== targetMove.move) + ); }; } } @@ -7712,23 +8208,32 @@ export class AbilityChangeAttr extends MoveEffectAttr { globalScene.triggerPokemonFormChange(moveTarget, SpeciesFormChangeRevertWeatherFormTrigger); if (moveTarget.breakIllusion()) { - globalScene.phaseManager.queueMessage(i18next.t("abilityTriggers:illusionBreak", { pokemonName: getPokemonNameWithAffix(moveTarget) })); + globalScene.phaseManager.queueMessage( + i18next.t("abilityTriggers:illusionBreak", { pokemonName: getPokemonNameWithAffix(moveTarget) }), + ); } - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:acquiredAbility", { pokemonName: getPokemonNameWithAffix(moveTarget), abilityName: allAbilities[this.ability].name })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:acquiredAbility", { + pokemonName: getPokemonNameWithAffix(moveTarget), + abilityName: allAbilities[this.ability].name, + }), + ); moveTarget.setTempAbility(allAbilities[this.ability]); globalScene.triggerPokemonFormChange(moveTarget, SpeciesFormChangeRevertWeatherFormTrigger); return true; } getCondition(): MoveConditionFunc { - return (user, target, move) => (this.selfTarget ? user : target).getAbility().replaceable && (this.selfTarget ? user : target).getAbility().id !== this.ability; + return (user, target, _move) => + (this.selfTarget ? user : target).getAbility().replaceable + && (this.selfTarget ? user : target).getAbility().id !== this.ability; } } export class AbilityCopyAttr extends MoveEffectAttr { public copyToPartner: boolean; - constructor(copyToPartner: boolean = false) { + constructor(copyToPartner = false) { super(false); this.copyToPartner = copyToPartner; @@ -7739,13 +8244,26 @@ export class AbilityCopyAttr extends MoveEffectAttr { return false; } - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:copiedTargetAbility", { + pokemonName: getPokemonNameWithAffix(user), + targetName: getPokemonNameWithAffix(target), + abilityName: allAbilities[target.getAbility().id].name, + }), + ); user.setTempAbility(target.getAbility()); const ally = user.getAlly(); - if (this.copyToPartner && globalScene.currentBattle?.double && ally != null && ally.hp) { // TODO is this the best way to check that the ally is active? - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(ally), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name })); + if (this.copyToPartner && globalScene.currentBattle?.double && ally != null && ally.hp) { + // TODO is this the best way to check that the ally is active? + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:copiedTargetAbility", { + pokemonName: getPokemonNameWithAffix(ally), + targetName: getPokemonNameWithAffix(target), + abilityName: allAbilities[target.getAbility().id].name, + }), + ); ally.setTempAbility(target.getAbility()); } @@ -7753,7 +8271,7 @@ export class AbilityCopyAttr extends MoveEffectAttr { } getCondition(): MoveConditionFunc { - return (user, target, move) => { + return (user, target, _move) => { const ally = user.getAlly(); let ret = target.getAbility().copiable && user.getAbility().replaceable; if (this.copyToPartner && globalScene.currentBattle?.double) { @@ -7778,7 +8296,12 @@ export class AbilityGiveAttr extends MoveEffectAttr { return false; } - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:acquiredAbility", { pokemonName: getPokemonNameWithAffix(target), abilityName: allAbilities[user.getAbility().id].name })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:acquiredAbility", { + pokemonName: getPokemonNameWithAffix(target), + abilityName: allAbilities[user.getAbility().id].name, + }), + ); target.setTempAbility(user.getAbility()); @@ -7786,7 +8309,8 @@ export class AbilityGiveAttr extends MoveEffectAttr { } getCondition(): MoveConditionFunc { - return (user, target, move) => user.getAbility().copiable && target.getAbility().replaceable && user.getAbility().id !== target.getAbility().id; + return (user, target, _move) => + user.getAbility().copiable && target.getAbility().replaceable && user.getAbility().id !== target.getAbility().id; } } @@ -7798,7 +8322,9 @@ export class SwitchAbilitiesAttr extends MoveEffectAttr { const tempAbility = user.getAbility(); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:swappedAbilitiesWithTarget", { pokemonName: getPokemonNameWithAffix(user) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:swappedAbilitiesWithTarget", { pokemonName: getPokemonNameWithAffix(user) }), + ); user.setTempAbility(target.getAbility()); target.setTempAbility(tempAbility); @@ -7809,7 +8335,7 @@ export class SwitchAbilitiesAttr extends MoveEffectAttr { } getCondition(): MoveConditionFunc { - return (user, target, move) => [user, target].every(pkmn => pkmn.getAbility().swappable); + return (user, target, _move) => [user, target].every(pkmn => pkmn.getAbility().swappable); } } @@ -7824,7 +8350,9 @@ export class SuppressAbilitiesAttr extends MoveEffectAttr { return false; } - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:suppressAbilities", { pokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:suppressAbilities", { pokemonName: getPokemonNameWithAffix(target) }), + ); target.suppressAbility(); @@ -7835,7 +8363,9 @@ export class SuppressAbilitiesAttr extends MoveEffectAttr { /** Causes the effect to fail when the target's ability is unsupressable or already suppressed. */ getCondition(): MoveConditionFunc { - return (_user, target, _move) => !target.summonData.abilitySuppressed && (target.getAbility().suppressable || (target.hasPassive() && target.getPassiveAbility().suppressable)); + return (_user, target, _move) => + !target.summonData.abilitySuppressed + && (target.getAbility().suppressable || (target.hasPassive() && target.getPassiveAbility().suppressable)); } } @@ -7883,7 +8413,7 @@ export class TransformAttr extends MoveEffectAttr { } getCondition(): MoveConditionFunc { - return (user, target) => user.canTransformInto(target) + return (user, target) => user.canTransformInto(target); } } @@ -7893,7 +8423,7 @@ export class TransformAttr extends MoveEffectAttr { */ export class SwapStatAttr extends MoveEffectAttr { /** The stat to be swapped between the user and the target */ - private stat: EffectiveStat; + private readonly stat: EffectiveStat; constructor(stat: EffectiveStat) { super(); @@ -7916,10 +8446,12 @@ export class SwapStatAttr extends MoveEffectAttr { user.setStat(this.stat, target.getStat(this.stat, false), false); target.setStat(this.stat, temp, false); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:switchedStat", { - pokemonName: getPokemonNameWithAffix(user), - stat: i18next.t(getStatKey(this.stat)), - })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:switchedStat", { + pokemonName: getPokemonNameWithAffix(user), + stat: i18next.t(getStatKey(this.stat)), + }), + ); return true; } @@ -7932,8 +8464,8 @@ export class SwapStatAttr extends MoveEffectAttr { * Used by Power Shift. */ export class ShiftStatAttr extends MoveEffectAttr { - private statToSwitch: EffectiveStat; - private statToSwitchWith: EffectiveStat; + private readonly statToSwitch: EffectiveStat; + private readonly statToSwitchWith: EffectiveStat; constructor(statToSwitch: EffectiveStat, statToSwitchWith: EffectiveStat) { super(); @@ -7961,11 +8493,13 @@ export class ShiftStatAttr extends MoveEffectAttr { user.setStat(this.statToSwitch, secondStat, false); user.setStat(this.statToSwitchWith, firstStat, false); - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:shiftedStats", { - pokemonName: getPokemonNameWithAffix(user), - statToSwitch: i18next.t(getStatKey(this.statToSwitch)), - statToSwitchWith: i18next.t(getStatKey(this.statToSwitchWith)) - })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:shiftedStats", { + pokemonName: getPokemonNameWithAffix(user), + statToSwitch: i18next.t(getStatKey(this.statToSwitch)), + statToSwitchWith: i18next.t(getStatKey(this.statToSwitchWith)), + }), + ); return true; } @@ -7977,7 +8511,7 @@ export class ShiftStatAttr extends MoveEffectAttr { * @param move n/a * @returns number of points to add to the user's benefit score */ - override getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { + override getUserBenefitScore(user: Pokemon, _target: Pokemon, _move: Move): number { return user.getStat(this.statToSwitchWith, false) > user.getStat(this.statToSwitch, false) ? 10 : 0; } } @@ -7989,8 +8523,8 @@ export class ShiftStatAttr extends MoveEffectAttr { */ export class AverageStatsAttr extends MoveEffectAttr { /** The stats to be averaged individually between the user and the target */ - private stats: readonly EffectiveStat[]; - private msgKey: string; + private readonly stats: readonly EffectiveStat[]; + private readonly msgKey: string; constructor(stats: readonly EffectiveStat[], msgKey: string) { super(); @@ -8028,10 +8562,10 @@ export class AverageStatsAttr extends MoveEffectAttr { export class MoneyAttr extends MoveEffectAttr { constructor() { - super(true, {firstHitOnly: true }); + super(true, { firstHitOnly: true }); } - apply(user: Pokemon, target: Pokemon, move: Move): boolean { + apply(_user: Pokemon, _target: Pokemon, _move: Move): boolean { globalScene.currentBattle.moneyScattered += globalScene.getWaveMoneyAmount(0.2); globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:coinsScatteredEverywhere")); return true; @@ -8052,8 +8586,10 @@ export class DestinyBondAttr extends MoveEffectAttr { * @param args N/A * @returns true */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - globalScene.phaseManager.queueMessage(`${i18next.t("moveTriggers:tryingToTakeFoeDown", { pokemonName: getPokemonNameWithAffix(user) })}`); + apply(user: Pokemon, _target: Pokemon, move: Move, _args: any[]): boolean { + globalScene.phaseManager.queueMessage( + `${i18next.t("moveTriggers:tryingToTakeFoeDown", { pokemonName: getPokemonNameWithAffix(user) })}`, + ); user.addTag(BattlerTagType.DESTINY_BOND, undefined, move.id, user.id); return true; } @@ -8098,7 +8634,7 @@ export class StatusIfBoostedAttr extends MoveEffectAttr { * @param args N/A * @returns true */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { if (target.turnData.statStagesIncreased) { target.trySetStatus(this.effect, user); } @@ -8107,7 +8643,7 @@ export class StatusIfBoostedAttr extends MoveEffectAttr { } export class VariableTargetAttr extends MoveAttr { - private targetChangeFunc: (user: Pokemon, target: Pokemon, move: Move) => number; + private readonly targetChangeFunc: (user: Pokemon, target: Pokemon, move: Move) => number; constructor(targetChange: (user: Pokemon, target: Pokemon, move: Move) => number) { super(); @@ -8136,8 +8672,10 @@ export class AfterYouAttr extends MoveEffectAttr { * @param _args - Unused * @returns `true` */ - override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:afterYou", { targetName: getPokemonNameWithAffix(target) })); + override apply(_user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:afterYou", { targetName: getPokemonNameWithAffix(target) }), + ); globalScene.phaseManager.forceMoveNext((phase: MovePhase) => phase.pokemon === target); return true; @@ -8158,50 +8696,59 @@ export class ForceLastAttr extends MoveEffectAttr { * @param _args N/A * @returns true */ - override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:forceLast", { targetPokemonName: getPokemonNameWithAffix(target) })); + override apply(_user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:forceLast", { targetPokemonName: getPokemonNameWithAffix(target) }), + ); globalScene.phaseManager.forceMoveLast((phase: MovePhase) => phase.pokemon === target); return true; } } -const failOnBossCondition: MoveConditionFunc = (user, target, move) => !target.isBossImmune(); +const failOnBossCondition: MoveConditionFunc = (_user, target, _move) => !target.isBossImmune(); -const failIfSingleBattle: MoveConditionFunc = (user, target, move) => globalScene.currentBattle.double; +const failIfSingleBattle: MoveConditionFunc = (_user, _target, _move) => globalScene.currentBattle.double; -const failIfDampCondition: MoveConditionFunc = (user, target, move) => { +const failIfDampCondition: MoveConditionFunc = (user, _target, move) => { const cancelled = new BooleanHolder(false); // temporary workaround to prevent displaying the message during enemy command phase // TODO: either move this, or make the move condition func have a `simulated` param - const simulated = globalScene.phaseManager.getCurrentPhase()?.is('EnemyCommandPhase'); + const simulated = globalScene.phaseManager.getCurrentPhase()?.is("EnemyCommandPhase"); for (const p of inSpeedOrder(ArenaTagSide.BOTH)) { - applyAbAttrs("FieldPreventExplosiveMovesAbAttr", {pokemon: p, cancelled, simulated}); + applyAbAttrs("FieldPreventExplosiveMovesAbAttr", { pokemon: p, cancelled, simulated }); } // Queue a message if an ability prevented usage of the move if (!simulated && cancelled.value) { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cannotUseMove", { pokemonName: getPokemonNameWithAffix(user), moveName: move.name })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:cannotUseMove", { pokemonName: getPokemonNameWithAffix(user), moveName: move.name }), + ); } return !cancelled.value; }; -const userSleptOrComatoseCondition: MoveConditionFunc = (user) => user.status?.effect === StatusEffect.SLEEP || user.hasAbility(AbilityId.COMATOSE); +const userSleptOrComatoseCondition: MoveConditionFunc = user => + user.status?.effect === StatusEffect.SLEEP || user.hasAbility(AbilityId.COMATOSE); -const targetSleptOrComatoseCondition: MoveConditionFunc = (_user: Pokemon, target: Pokemon, _move: Move) => target.status?.effect === StatusEffect.SLEEP || target.hasAbility(AbilityId.COMATOSE); +const targetSleptOrComatoseCondition: MoveConditionFunc = (_user: Pokemon, target: Pokemon, _move: Move) => + target.status?.effect === StatusEffect.SLEEP || target.hasAbility(AbilityId.COMATOSE); const failIfLastCondition: MoveConditionFunc = () => globalScene.phaseManager.hasPhaseOfType("MovePhase"); -const failIfLastInPartyCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => { +const failIfLastInPartyCondition: MoveConditionFunc = (user: Pokemon, _target: Pokemon, _move: Move) => { const party: Pokemon[] = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); return party.some(pokemon => pokemon.isActive() && !pokemon.isOnField()); }; -const failIfGhostTypeCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => !target.isOfType(PokemonType.GHOST); +const failIfGhostTypeCondition: MoveConditionFunc = (_user: Pokemon, target: Pokemon, _move: Move) => + !target.isOfType(PokemonType.GHOST); -const failIfNoTargetHeldItemsCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => target.getHeldItems().filter(i => i.isTransferable)?.length > 0; +const failIfNoTargetHeldItemsCondition: MoveConditionFunc = (_user: Pokemon, target: Pokemon, _move: Move) => + target.getHeldItems().filter(i => i.isTransferable)?.length > 0; -const attackedByItemMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => { - if (target == null) { // Fix bug when used against targets that have both fainted +const attackedByItemMessageFunc = (_user: Pokemon, target: Pokemon, _move: Move) => { + if (target == null) { + // Fix bug when used against targets that have both fainted return ""; } const heldItems = target.getHeldItems().filter(i => i.isTransferable); @@ -8209,12 +8756,15 @@ const attackedByItemMessageFunc = (user: Pokemon, target: Pokemon, move: Move) = return ""; } const itemName = heldItems[0]?.type?.name ?? "item"; - const message: string = i18next.t("moveTriggers:attackedByItem", { pokemonName: getPokemonNameWithAffix(target), itemName: itemName }); + const message: string = i18next.t("moveTriggers:attackedByItem", { + pokemonName: getPokemonNameWithAffix(target), + itemName, + }); return message; }; export class HitsSameTypeAttr extends VariableMoveTypeMultiplierAttr { - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + apply(user: Pokemon, target: Pokemon, _move: Move, args: any[]): boolean { const multiplier = args[0] as NumberHolder; if (!user.getTypes(true).some(type => target.getTypes(true).includes(type))) { multiplier.value = 0; @@ -8263,12 +8813,17 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr { } const userTypes = user.getTypes(); const validTypes = this.getTypeResistances(globalScene.gameMode, moveData.type).filter(t => !userTypes.includes(t)); // valid types are ones that are not already the user's types - if (!validTypes.length) { + if (validTypes.length === 0) { return false; } const type = validTypes[user.randBattleSeedInt(validTypes.length)]; - user.summonData.types = [ type ]; - globalScene.phaseManager.queueMessage(i18next.t("battle:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), type: toTitleCase(PokemonType[type]) })); + user.summonData.types = [type]; + globalScene.phaseManager.queueMessage( + i18next.t("battle:transformedIntoType", { + pokemonName: getPokemonNameWithAffix(user), + type: toTitleCase(PokemonType[type]), + }), + ); user.updateInfo(); return true; @@ -8278,7 +8833,7 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr { * Retrieve the types resisting a given type. Used by Conversion 2 * @returns An array populated with Types, or an empty array if no resistances exist (Unknown or Stellar type) */ - getTypeResistances(gameMode: GameMode, type: number): PokemonType[] { + getTypeResistances(_gameMode: GameMode, type: number): PokemonType[] { const typeResistances: PokemonType[] = []; for (let i = 0; i < Object.keys(PokemonType).length; i++) { @@ -8295,7 +8850,7 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr { getCondition(): MoveConditionFunc { // TODO: Does this count dancer? - return (user, target, move) => { + return (_user, target, _move) => { return target.getLastXMoves(-1).some(tm => tm.move !== MoveId.NONE); }; } @@ -8324,14 +8879,19 @@ export class ExposedMoveAttr extends AddBattlerTagAttr { return false; } - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:exposedMove", { pokemonName: getPokemonNameWithAffix(user), targetPokemonName: getPokemonNameWithAffix(target) })); + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:exposedMove", { + pokemonName: getPokemonNameWithAffix(user), + targetPokemonName: getPokemonNameWithAffix(target), + }), + ); return true; } } - -const unknownTypeCondition: MoveConditionFunc = (user, target, move) => !user.getTypes().includes(PokemonType.UNKNOWN); +const unknownTypeCondition: MoveConditionFunc = (user, _target, _move) => + !user.getTypes().includes(PokemonType.UNKNOWN); export type MoveTargetSet = { targets: BattlerIndex[]; @@ -8564,14 +9124,14 @@ export function initMoves() { (allMoves as Move[]).push( new SelfStatusMove(MoveId.NONE, PokemonType.NORMAL, MoveCategory.STATUS, -1, -1, 0, 1), new AttackMove(MoveId.POUND, PokemonType.NORMAL, MoveCategory.PHYSICAL, 40, 100, 35, -1, 0, 1), - new AttackMove(MoveId.KARATE_CHOP, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 50, 100, 25, -1, 0, 1) + new AttackMove(MoveId.KARATE_CHOP, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 50, 100, 25, -1, 0, 1) // .attr(HighCritAttr), - new AttackMove(MoveId.DOUBLE_SLAP, PokemonType.NORMAL, MoveCategory.PHYSICAL, 15, 85, 10, -1, 0, 1) + new AttackMove(MoveId.DOUBLE_SLAP, PokemonType.NORMAL, MoveCategory.PHYSICAL, 15, 85, 10, -1, 0, 1) // .attr(MultiHitAttr), new AttackMove(MoveId.COMET_PUNCH, PokemonType.NORMAL, MoveCategory.PHYSICAL, 18, 85, 15, -1, 0, 1) .attr(MultiHitAttr) .punchingMove(), - new AttackMove(MoveId.MEGA_PUNCH, PokemonType.NORMAL, MoveCategory.PHYSICAL, 80, 85, 20, -1, 0, 1) + new AttackMove(MoveId.MEGA_PUNCH, PokemonType.NORMAL, MoveCategory.PHYSICAL, 80, 85, 20, -1, 0, 1) // .punchingMove(), new AttackMove(MoveId.PAY_DAY, PokemonType.NORMAL, MoveCategory.PHYSICAL, 40, 100, 20, -1, 0, 1) .attr(MoneyAttr) @@ -8596,9 +9156,9 @@ export function initMoves() { .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), new SelfStatusMove(MoveId.SWORDS_DANCE, PokemonType.NORMAL, -1, 20, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.ATK ], 2, true) + .attr(StatStageChangeAttr, [Stat.ATK], 2, true) .danceMove(), - new AttackMove(MoveId.CUT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 50, 95, 30, -1, 0, 1) + new AttackMove(MoveId.CUT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 50, 95, 30, -1, 0, 1) // .slicingMove(), new AttackMove(MoveId.GUST, PokemonType.FLYING, MoveCategory.SPECIAL, 40, 100, 35, -1, 0, 1) .attr(HitsTagForDoubleDamageAttr, BattlerTagType.FLYING) @@ -8614,7 +9174,7 @@ export function initMoves() { .chargeText(i18next.t("moveTriggers:flewUpHigh", { pokemonName: "{USER}" })) .chargeAttr(SemiInvulnerableAttr, BattlerTagType.FLYING) .affectedByGravity(), - new AttackMove(MoveId.BIND, PokemonType.NORMAL, MoveCategory.PHYSICAL, 15, 85, 20, -1, 0, 1) + new AttackMove(MoveId.BIND, PokemonType.NORMAL, MoveCategory.PHYSICAL, 15, 85, 20, -1, 0, 1) // .attr(TrapAttr, BattlerTagType.BIND), new AttackMove(MoveId.SLAM, PokemonType.NORMAL, MoveCategory.PHYSICAL, 80, 75, 20, -1, 0, 1), new AttackMove(MoveId.VINE_WHIP, PokemonType.GRASS, MoveCategory.PHYSICAL, 45, 100, 25, -1, 0, 1), @@ -8622,7 +9182,7 @@ export function initMoves() { .attr(AlwaysHitMinimizeAttr) .attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED) .attr(FlinchAttr), - new AttackMove(MoveId.DOUBLE_KICK, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 30, 100, 30, -1, 0, 1) + new AttackMove(MoveId.DOUBLE_KICK, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 30, 100, 30, -1, 0, 1) // .attr(MultiHitAttr, MultiHitType._2), new AttackMove(MoveId.MEGA_KICK, PokemonType.NORMAL, MoveCategory.PHYSICAL, 120, 75, 5, -1, 0, 1), new AttackMove(MoveId.JUMP_KICK, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 100, 95, 10, -1, 0, 1) @@ -8630,15 +9190,15 @@ export function initMoves() { .attr(NoEffectAttr, crashDamageFunc) .affectedByGravity() .recklessMove(), - new AttackMove(MoveId.ROLLING_KICK, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 60, 85, 15, 30, 0, 1) + new AttackMove(MoveId.ROLLING_KICK, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 60, 85, 15, 30, 0, 1) // .attr(FlinchAttr), new StatusMove(MoveId.SAND_ATTACK, PokemonType.GROUND, 100, 15, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.ACC ], -1) + .attr(StatStageChangeAttr, [Stat.ACC], -1) .reflectable(), - new AttackMove(MoveId.HEADBUTT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 15, 30, 0, 1) + new AttackMove(MoveId.HEADBUTT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 15, 30, 0, 1) // .attr(FlinchAttr), new AttackMove(MoveId.HORN_ATTACK, PokemonType.NORMAL, MoveCategory.PHYSICAL, 65, 100, 25, -1, 0, 1), - new AttackMove(MoveId.FURY_ATTACK, PokemonType.NORMAL, MoveCategory.PHYSICAL, 15, 85, 20, -1, 0, 1) + new AttackMove(MoveId.FURY_ATTACK, PokemonType.NORMAL, MoveCategory.PHYSICAL, 15, 85, 20, -1, 0, 1) // .attr(MultiHitAttr), new AttackMove(MoveId.HORN_DRILL, PokemonType.NORMAL, MoveCategory.PHYSICAL, 250, 30, 5, -1, 0, 1) .attr(OneHitKOAttr) @@ -8648,7 +9208,7 @@ export function initMoves() { .attr(AlwaysHitMinimizeAttr) .attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED) .attr(StatusEffectAttr, StatusEffect.PARALYSIS), - new AttackMove(MoveId.WRAP, PokemonType.NORMAL, MoveCategory.PHYSICAL, 15, 90, 20, -1, 0, 1) + new AttackMove(MoveId.WRAP, PokemonType.NORMAL, MoveCategory.PHYSICAL, 15, 90, 20, -1, 0, 1) // .attr(TrapAttr, BattlerTagType.WRAP), new AttackMove(MoveId.TAKE_DOWN, PokemonType.NORMAL, MoveCategory.PHYSICAL, 90, 85, 20, -1, 0, 1) .attr(RecoilAttr) @@ -8662,7 +9222,7 @@ export function initMoves() { .attr(RecoilAttr, false, 0.33) .recklessMove(), new StatusMove(MoveId.TAIL_WHIP, PokemonType.NORMAL, 100, 30, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1) + .attr(StatStageChangeAttr, [Stat.DEF], -1) .target(MoveTarget.ALL_NEAR_ENEMIES) .reflectable(), new AttackMove(MoveId.POISON_STING, PokemonType.POISON, MoveCategory.PHYSICAL, 15, 100, 35, 30, 0, 1) @@ -8676,14 +9236,14 @@ export function initMoves() { .attr(MultiHitAttr) .makesContact(false), new StatusMove(MoveId.LEER, PokemonType.NORMAL, 100, 30, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1) + .attr(StatStageChangeAttr, [Stat.DEF], -1) .target(MoveTarget.ALL_NEAR_ENEMIES) .reflectable(), new AttackMove(MoveId.BITE, PokemonType.DARK, MoveCategory.PHYSICAL, 60, 100, 25, 30, 0, 1) .attr(FlinchAttr) .bitingMove(), new StatusMove(MoveId.GROWL, PokemonType.NORMAL, 100, 40, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.ATK ], -1) + .attr(StatStageChangeAttr, [Stat.ATK], -1) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES) .reflectable(), @@ -8700,7 +9260,7 @@ export function initMoves() { .attr(ConfuseAttr) .soundBased() .reflectable(), - new AttackMove(MoveId.SONIC_BOOM, PokemonType.NORMAL, MoveCategory.SPECIAL, -1, 90, 20, -1, 0, 1) + new AttackMove(MoveId.SONIC_BOOM, PokemonType.NORMAL, MoveCategory.SPECIAL, -1, 90, 20, -1, 0, 1) // .attr(FixedDamageAttr, 20), new StatusMove(MoveId.DISABLE, PokemonType.NORMAL, 100, 20, -1, 0, 1) .attr(AddBattlerTagAttr, BattlerTagType.DISABLED, false, true) @@ -8711,11 +9271,11 @@ export function initMoves() { .ignoresSubstitute() .reflectable(), new AttackMove(MoveId.ACID, PokemonType.POISON, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) + .attr(StatStageChangeAttr, [Stat.SPDEF], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), - new AttackMove(MoveId.EMBER, PokemonType.FIRE, MoveCategory.SPECIAL, 40, 100, 25, 10, 0, 1) + new AttackMove(MoveId.EMBER, PokemonType.FIRE, MoveCategory.SPECIAL, 40, 100, 25, 10, 0, 1) // .attr(StatusEffectAttr, StatusEffect.BURN), - new AttackMove(MoveId.FLAMETHROWER, PokemonType.FIRE, MoveCategory.SPECIAL, 90, 100, 15, 10, 0, 1) + new AttackMove(MoveId.FLAMETHROWER, PokemonType.FIRE, MoveCategory.SPECIAL, 90, 100, 15, 10, 0, 1) // .attr(StatusEffectAttr, StatusEffect.BURN), new StatusMove(MoveId.MIST, PokemonType.ICE, -1, 30, -1, 0, 1) .attr(AddArenaTagAttr, ArenaTagType.MIST, 5, true) @@ -8726,34 +9286,34 @@ export function initMoves() { .target(MoveTarget.ALL_NEAR_OTHERS) .attr(HitsTagForDoubleDamageAttr, BattlerTagType.UNDERWATER) .attr(GulpMissileTagAttr), - new AttackMove(MoveId.ICE_BEAM, PokemonType.ICE, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 1) + new AttackMove(MoveId.ICE_BEAM, PokemonType.ICE, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 1) // .attr(StatusEffectAttr, StatusEffect.FREEZE), new AttackMove(MoveId.BLIZZARD, PokemonType.ICE, MoveCategory.SPECIAL, 110, 70, 5, 10, 0, 1) .attr(BlizzardAccuracyAttr) .attr(StatusEffectAttr, StatusEffect.FREEZE) .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), - new AttackMove(MoveId.PSYBEAM, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 65, 100, 20, 10, 0, 1) + new AttackMove(MoveId.PSYBEAM, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 65, 100, 20, 10, 0, 1) // .attr(ConfuseAttr), - new AttackMove(MoveId.BUBBLE_BEAM, PokemonType.WATER, MoveCategory.SPECIAL, 65, 100, 20, 10, 0, 1) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1), - new AttackMove(MoveId.AURORA_BEAM, PokemonType.ICE, MoveCategory.SPECIAL, 65, 100, 20, 10, 0, 1) - .attr(StatStageChangeAttr, [ Stat.ATK ], -1), - new AttackMove(MoveId.HYPER_BEAM, PokemonType.NORMAL, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 1) + new AttackMove(MoveId.BUBBLE_BEAM, PokemonType.WATER, MoveCategory.SPECIAL, 65, 100, 20, 10, 0, 1) // + .attr(StatStageChangeAttr, [Stat.SPD], -1), + new AttackMove(MoveId.AURORA_BEAM, PokemonType.ICE, MoveCategory.SPECIAL, 65, 100, 20, 10, 0, 1) // + .attr(StatStageChangeAttr, [Stat.ATK], -1), + new AttackMove(MoveId.HYPER_BEAM, PokemonType.NORMAL, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 1) // .attr(RechargeAttr), new AttackMove(MoveId.PECK, PokemonType.FLYING, MoveCategory.PHYSICAL, 35, 100, 35, -1, 0, 1), new AttackMove(MoveId.DRILL_PECK, PokemonType.FLYING, MoveCategory.PHYSICAL, 80, 100, 20, -1, 0, 1), new AttackMove(MoveId.SUBMISSION, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 80, 80, 20, -1, 0, 1) .attr(RecoilAttr) .recklessMove(), - new AttackMove(MoveId.LOW_KICK, PokemonType.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, -1, 0, 1) + new AttackMove(MoveId.LOW_KICK, PokemonType.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, -1, 0, 1) // .attr(WeightPowerAttr), new AttackMove(MoveId.COUNTER, PokemonType.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, -1, -5, 1) .attr(CounterDamageAttr, 2, MoveCategory.PHYSICAL) .attr(CounterRedirectAttr, MoveCategory.PHYSICAL) .condition(counterAttackConditionPhysical, 3) .target(MoveTarget.ATTACKER), - new AttackMove(MoveId.SEISMIC_TOSS, PokemonType.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, -1, 0, 1) + new AttackMove(MoveId.SEISMIC_TOSS, PokemonType.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, -1, 0, 1) // .attr(LevelDamageAttr), new AttackMove(MoveId.STRENGTH, PokemonType.NORMAL, MoveCategory.PHYSICAL, 80, 100, 15, -1, 0, 1), new AttackMove(MoveId.ABSORB, PokemonType.GRASS, MoveCategory.SPECIAL, 20, 100, 25, -1, 0, 1) @@ -8764,9 +9324,9 @@ export function initMoves() { .triageMove(), new StatusMove(MoveId.LEECH_SEED, PokemonType.GRASS, 90, 10, -1, 0, 1) .attr(LeechSeedAttr) - .condition((user, target, move) => !target.getTag(BattlerTagType.SEEDED) && !target.isOfType(PokemonType.GRASS)) + .condition((_user, target, _move) => !target.getTag(BattlerTagType.SEEDED) && !target.isOfType(PokemonType.GRASS)) .reflectable(), - new SelfStatusMove(MoveId.GROWTH, PokemonType.NORMAL, -1, 20, -1, 0, 1) + new SelfStatusMove(MoveId.GROWTH, PokemonType.NORMAL, -1, 20, -1, 0, 1) // .attr(GrowthStatStageChangeAttr), new AttackMove(MoveId.RAZOR_LEAF, PokemonType.GRASS, MoveCategory.PHYSICAL, 55, 95, 25, -1, 0, 1) .attr(HighCritAttr) @@ -8775,7 +9335,7 @@ export function initMoves() { .target(MoveTarget.ALL_NEAR_ENEMIES), new ChargingAttackMove(MoveId.SOLAR_BEAM, PokemonType.GRASS, MoveCategory.SPECIAL, 120, 100, 10, -1, 0, 1) .chargeText(i18next.t("moveTriggers:tookInSunlight", { pokemonName: "{USER}" })) - .chargeAttr(WeatherInstantChargeAttr, [ WeatherType.SUNNY, WeatherType.HARSH_SUN ]) + .chargeAttr(WeatherInstantChargeAttr, [WeatherType.SUNNY, WeatherType.HARSH_SUN]) .attr(AntiSunlightPowerDecreaseAttr), new StatusMove(MoveId.POISON_POWDER, PokemonType.POISON, 75, 35, -1, 0, 1) .attr(StatusEffectAttr, StatusEffect.POISON) @@ -8797,16 +9357,16 @@ export function initMoves() { .danceMove() .target(MoveTarget.RANDOM_NEAR_ENEMY), new StatusMove(MoveId.STRING_SHOT, PokemonType.BUG, 95, 40, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.SPD ], -2) + .attr(StatStageChangeAttr, [Stat.SPD], -2) .target(MoveTarget.ALL_NEAR_ENEMIES) .reflectable(), - new AttackMove(MoveId.DRAGON_RAGE, PokemonType.DRAGON, MoveCategory.SPECIAL, -1, 100, 10, -1, 0, 1) + new AttackMove(MoveId.DRAGON_RAGE, PokemonType.DRAGON, MoveCategory.SPECIAL, -1, 100, 10, -1, 0, 1) // .attr(FixedDamageAttr, 40), - new AttackMove(MoveId.FIRE_SPIN, PokemonType.FIRE, MoveCategory.SPECIAL, 35, 85, 15, -1, 0, 1) + new AttackMove(MoveId.FIRE_SPIN, PokemonType.FIRE, MoveCategory.SPECIAL, 35, 85, 15, -1, 0, 1) // .attr(TrapAttr, BattlerTagType.FIRE_SPIN), - new AttackMove(MoveId.THUNDER_SHOCK, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1) + new AttackMove(MoveId.THUNDER_SHOCK, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1) // .attr(StatusEffectAttr, StatusEffect.PARALYSIS), - new AttackMove(MoveId.THUNDERBOLT, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 90, 100, 15, 10, 0, 1) + new AttackMove(MoveId.THUNDERBOLT, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 90, 100, 15, 10, 0, 1) // .attr(StatusEffectAttr, StatusEffect.PARALYSIS), new StatusMove(MoveId.THUNDER_WAVE, PokemonType.ELECTRIC, 90, 20, -1, 0, 1) .attr(StatusEffectAttr, StatusEffect.PARALYSIS) @@ -8816,11 +9376,13 @@ export function initMoves() { .attr(StatusEffectAttr, StatusEffect.PARALYSIS) .attr(ThunderAccuracyAttr) .attr(HitsTagAttr, BattlerTagType.FLYING), - new AttackMove(MoveId.ROCK_THROW, PokemonType.ROCK, MoveCategory.PHYSICAL, 50, 90, 15, -1, 0, 1) + new AttackMove(MoveId.ROCK_THROW, PokemonType.ROCK, MoveCategory.PHYSICAL, 50, 90, 15, -1, 0, 1) // .makesContact(false), new AttackMove(MoveId.EARTHQUAKE, PokemonType.GROUND, MoveCategory.PHYSICAL, 100, 100, 10, -1, 0, 1) .attr(HitsTagForDoubleDamageAttr, BattlerTagType.UNDERGROUND) - .attr(MovePowerMultiplierAttr, (user, target, move) => globalScene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1) + .attr(MovePowerMultiplierAttr, (_user, target, _move) => + globalScene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1, + ) .makesContact(false) .target(MoveTarget.ALL_NEAR_OTHERS), new AttackMove(MoveId.FISSURE, PokemonType.GROUND, MoveCategory.PHYSICAL, 250, 30, 5, -1, 0, 1) @@ -8835,59 +9397,59 @@ export function initMoves() { .attr(StatusEffectAttr, StatusEffect.TOXIC) .attr(ToxicAccuracyAttr) .reflectable(), - new AttackMove(MoveId.CONFUSION, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 50, 100, 25, 10, 0, 1) + new AttackMove(MoveId.CONFUSION, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 50, 100, 25, 10, 0, 1) // .attr(ConfuseAttr), - new AttackMove(MoveId.PSYCHIC, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 1) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1), + new AttackMove(MoveId.PSYCHIC, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 1) // + .attr(StatStageChangeAttr, [Stat.SPDEF], -1), new StatusMove(MoveId.HYPNOSIS, PokemonType.PSYCHIC, 60, 20, -1, 0, 1) .attr(StatusEffectAttr, StatusEffect.SLEEP) .reflectable(), - new SelfStatusMove(MoveId.MEDITATE, PokemonType.PSYCHIC, -1, 40, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true), - new SelfStatusMove(MoveId.AGILITY, PokemonType.PSYCHIC, -1, 30, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.SPD ], 2, true), + new SelfStatusMove(MoveId.MEDITATE, PokemonType.PSYCHIC, -1, 40, -1, 0, 1) // + .attr(StatStageChangeAttr, [Stat.ATK], 1, true), + new SelfStatusMove(MoveId.AGILITY, PokemonType.PSYCHIC, -1, 30, -1, 0, 1) // + .attr(StatStageChangeAttr, [Stat.SPD], 2, true), new AttackMove(MoveId.QUICK_ATTACK, PokemonType.NORMAL, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 1), - new AttackMove(MoveId.RAGE, PokemonType.NORMAL, MoveCategory.PHYSICAL, 20, 100, 20, -1, 0, 1) + new AttackMove(MoveId.RAGE, PokemonType.NORMAL, MoveCategory.PHYSICAL, 20, 100, 20, -1, 0, 1) // .partial(), // No effect implemented new SelfStatusMove(MoveId.TELEPORT, PokemonType.PSYCHIC, -1, 20, -1, -6, 1) .attr(ForceSwitchOutAttr, true) .hidesUser() .condition(failTeleportCondition, 3), - new AttackMove(MoveId.NIGHT_SHADE, PokemonType.GHOST, MoveCategory.SPECIAL, -1, 100, 15, -1, 0, 1) + new AttackMove(MoveId.NIGHT_SHADE, PokemonType.GHOST, MoveCategory.SPECIAL, -1, 100, 15, -1, 0, 1) // .attr(LevelDamageAttr), - new StatusMove(MoveId.MIMIC, PokemonType.NORMAL, -1, 10, -1, 0, 1) + new StatusMove(MoveId.MIMIC, PokemonType.NORMAL, -1, 10, -1, 0, 1) // .attr(MovesetCopyMoveAttr) .ignoresSubstitute(), - new StatusMove(MoveId.SCREECH, PokemonType.NORMAL, 85, 40, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.DEF ], -2) + new StatusMove(MoveId.SCREECH, PokemonType.NORMAL, 85, 40, -1, 0, 1) // + .attr(StatStageChangeAttr, [Stat.DEF], -2) .soundBased() .reflectable(), - new SelfStatusMove(MoveId.DOUBLE_TEAM, PokemonType.NORMAL, -1, 15, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.EVA ], 1, true), - new SelfStatusMove(MoveId.RECOVER, PokemonType.NORMAL, -1, 5, -1, 0, 1) + new SelfStatusMove(MoveId.DOUBLE_TEAM, PokemonType.NORMAL, -1, 15, -1, 0, 1) // + .attr(StatStageChangeAttr, [Stat.EVA], 1, true), + new SelfStatusMove(MoveId.RECOVER, PokemonType.NORMAL, -1, 5, -1, 0, 1) // .attr(HealAttr, 0.5) .triageMove(), - new SelfStatusMove(MoveId.HARDEN, PokemonType.NORMAL, -1, 30, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.DEF ], 1, true), + new SelfStatusMove(MoveId.HARDEN, PokemonType.NORMAL, -1, 30, -1, 0, 1) // + .attr(StatStageChangeAttr, [Stat.DEF], 1, true), new SelfStatusMove(MoveId.MINIMIZE, PokemonType.NORMAL, -1, 10, -1, 0, 1) .attr(AddBattlerTagAttr, BattlerTagType.MINIMIZED, true, false) - .attr(StatStageChangeAttr, [ Stat.EVA ], 2, true), + .attr(StatStageChangeAttr, [Stat.EVA], 2, true), new StatusMove(MoveId.SMOKESCREEN, PokemonType.NORMAL, 100, 20, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.ACC ], -1) + .attr(StatStageChangeAttr, [Stat.ACC], -1) .reflectable(), - new StatusMove(MoveId.CONFUSE_RAY, PokemonType.GHOST, 100, 10, -1, 0, 1) + new StatusMove(MoveId.CONFUSE_RAY, PokemonType.GHOST, 100, 10, -1, 0, 1) // .attr(ConfuseAttr) .reflectable(), - new SelfStatusMove(MoveId.WITHDRAW, PokemonType.WATER, -1, 40, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.DEF ], 1, true), - new SelfStatusMove(MoveId.DEFENSE_CURL, PokemonType.NORMAL, -1, 40, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.DEF ], 1, true), - new SelfStatusMove(MoveId.BARRIER, PokemonType.PSYCHIC, -1, 20, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true), + new SelfStatusMove(MoveId.WITHDRAW, PokemonType.WATER, -1, 40, -1, 0, 1) // + .attr(StatStageChangeAttr, [Stat.DEF], 1, true), + new SelfStatusMove(MoveId.DEFENSE_CURL, PokemonType.NORMAL, -1, 40, -1, 0, 1) // + .attr(StatStageChangeAttr, [Stat.DEF], 1, true), + new SelfStatusMove(MoveId.BARRIER, PokemonType.PSYCHIC, -1, 20, -1, 0, 1) // + .attr(StatStageChangeAttr, [Stat.DEF], 2, true), new StatusMove(MoveId.LIGHT_SCREEN, PokemonType.PSYCHIC, -1, 30, -1, 0, 1) .attr(AddArenaTagAttr, ArenaTagType.LIGHT_SCREEN, 5, true) .target(MoveTarget.USER_SIDE), - new SelfStatusMove(MoveId.HAZE, PokemonType.ICE, -1, 30, -1, 0, 1) + new SelfStatusMove(MoveId.HAZE, PokemonType.ICE, -1, 30, -1, 0, 1) // .ignoresSubstitute() .attr(ResetStatsAttr, true), new StatusMove(MoveId.REFLECT, PokemonType.PSYCHIC, -1, 20, -1, 0, 1) @@ -8900,9 +9462,9 @@ export function initMoves() { new AttackMove(MoveId.BIDE, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, -1, 10, -1, 1, 1) .target(MoveTarget.USER) .unimplemented(), - new SelfStatusMove(MoveId.METRONOME, PokemonType.NORMAL, -1, 10, -1, 0, 1) + new SelfStatusMove(MoveId.METRONOME, PokemonType.NORMAL, -1, 10, -1, 0, 1) // .attr(RandomMoveAttr, invalidMetronomeMoves), - new StatusMove(MoveId.MIRROR_MOVE, PokemonType.FLYING, -1, 20, -1, 0, 1) + new StatusMove(MoveId.MIRROR_MOVE, PokemonType.FLYING, -1, 20, -1, 0, 1) // .attr(CopyMoveAttr, true, invalidMirrorMoveMoves), new AttackMove(MoveId.SELF_DESTRUCT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 200, 100, 5, -1, 0, 1) .attr(SacrificialAttr) @@ -8912,37 +9474,37 @@ export function initMoves() { new AttackMove(MoveId.EGG_BOMB, PokemonType.NORMAL, MoveCategory.PHYSICAL, 100, 75, 10, -1, 0, 1) .makesContact(false) .ballBombMove(), - new AttackMove(MoveId.LICK, PokemonType.GHOST, MoveCategory.PHYSICAL, 30, 100, 30, 30, 0, 1) + new AttackMove(MoveId.LICK, PokemonType.GHOST, MoveCategory.PHYSICAL, 30, 100, 30, 30, 0, 1) // .attr(StatusEffectAttr, StatusEffect.PARALYSIS), - new AttackMove(MoveId.SMOG, PokemonType.POISON, MoveCategory.SPECIAL, 30, 70, 20, 40, 0, 1) + new AttackMove(MoveId.SMOG, PokemonType.POISON, MoveCategory.SPECIAL, 30, 70, 20, 40, 0, 1) // .attr(StatusEffectAttr, StatusEffect.POISON), - new AttackMove(MoveId.SLUDGE, PokemonType.POISON, MoveCategory.SPECIAL, 65, 100, 20, 30, 0, 1) + new AttackMove(MoveId.SLUDGE, PokemonType.POISON, MoveCategory.SPECIAL, 65, 100, 20, 30, 0, 1) // .attr(StatusEffectAttr, StatusEffect.POISON), new AttackMove(MoveId.BONE_CLUB, PokemonType.GROUND, MoveCategory.PHYSICAL, 65, 85, 20, 10, 0, 1) .attr(FlinchAttr) .makesContact(false), - new AttackMove(MoveId.FIRE_BLAST, PokemonType.FIRE, MoveCategory.SPECIAL, 110, 85, 5, 10, 0, 1) + new AttackMove(MoveId.FIRE_BLAST, PokemonType.FIRE, MoveCategory.SPECIAL, 110, 85, 5, 10, 0, 1) // .attr(StatusEffectAttr, StatusEffect.BURN), - new AttackMove(MoveId.WATERFALL, PokemonType.WATER, MoveCategory.PHYSICAL, 80, 100, 15, 20, 0, 1) + new AttackMove(MoveId.WATERFALL, PokemonType.WATER, MoveCategory.PHYSICAL, 80, 100, 15, 20, 0, 1) // .attr(FlinchAttr), - new AttackMove(MoveId.CLAMP, PokemonType.WATER, MoveCategory.PHYSICAL, 35, 85, 15, -1, 0, 1) + new AttackMove(MoveId.CLAMP, PokemonType.WATER, MoveCategory.PHYSICAL, 35, 85, 15, -1, 0, 1) // .attr(TrapAttr, BattlerTagType.CLAMP), - new AttackMove(MoveId.SWIFT, PokemonType.NORMAL, MoveCategory.SPECIAL, 60, -1, 20, -1, 0, 1) + new AttackMove(MoveId.SWIFT, PokemonType.NORMAL, MoveCategory.SPECIAL, 60, -1, 20, -1, 0, 1) // .target(MoveTarget.ALL_NEAR_ENEMIES), new ChargingAttackMove(MoveId.SKULL_BASH, PokemonType.NORMAL, MoveCategory.PHYSICAL, 130, 100, 10, -1, 0, 1) .chargeText(i18next.t("moveTriggers:loweredItsHead", { pokemonName: "{USER}" })) - .chargeAttr(StatStageChangeAttr, [ Stat.DEF ], 1, true), + .chargeAttr(StatStageChangeAttr, [Stat.DEF], 1, true), new AttackMove(MoveId.SPIKE_CANNON, PokemonType.NORMAL, MoveCategory.PHYSICAL, 20, 100, 15, -1, 0, 1) .attr(MultiHitAttr) .makesContact(false), - new AttackMove(MoveId.CONSTRICT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 10, 100, 35, 10, 0, 1) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1), - new SelfStatusMove(MoveId.AMNESIA, PokemonType.PSYCHIC, -1, 20, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], 2, true), - new StatusMove(MoveId.KINESIS, PokemonType.PSYCHIC, 80, 15, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.ACC ], -1) + new AttackMove(MoveId.CONSTRICT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 10, 100, 35, 10, 0, 1) // + .attr(StatStageChangeAttr, [Stat.SPD], -1), + new SelfStatusMove(MoveId.AMNESIA, PokemonType.PSYCHIC, -1, 20, -1, 0, 1) // + .attr(StatStageChangeAttr, [Stat.SPDEF], 2, true), + new StatusMove(MoveId.KINESIS, PokemonType.PSYCHIC, 80, 15, -1, 0, 1) // + .attr(StatStageChangeAttr, [Stat.ACC], -1) .reflectable(), - new SelfStatusMove(MoveId.SOFT_BOILED, PokemonType.NORMAL, -1, 5, -1, 0, 1) + new SelfStatusMove(MoveId.SOFT_BOILED, PokemonType.NORMAL, -1, 5, -1, 0, 1) // .attr(HealAttr, 0.5) .triageMove(), new AttackMove(MoveId.HIGH_JUMP_KICK, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 130, 90, 10, -1, 0, 1) @@ -8983,10 +9545,10 @@ export function initMoves() { * Does not copy the target's rage fist hit count * Does not copy the target's volatile status conditions (ie BattlerTags) * Renders user typeless when copying typeless opponent (should revert to original typing) - */ + */ .edgeCase(), new AttackMove(MoveId.BUBBLE, PokemonType.WATER, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1) + .attr(StatStageChangeAttr, [Stat.SPD], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(MoveId.DIZZY_PUNCH, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 10, 20, 0, 1) .attr(ConfuseAttr) @@ -8996,28 +9558,28 @@ export function initMoves() { .powderMove() .reflectable(), new StatusMove(MoveId.FLASH, PokemonType.NORMAL, 100, 20, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.ACC ], -1) + .attr(StatStageChangeAttr, [Stat.ACC], -1) .reflectable(), - new AttackMove(MoveId.PSYWAVE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, -1, 100, 15, -1, 0, 1) + new AttackMove(MoveId.PSYWAVE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, -1, 100, 15, -1, 0, 1) // .attr(RandomLevelDamageAttr), new SelfStatusMove(MoveId.SPLASH, PokemonType.NORMAL, -1, 40, -1, 0, 1) .attr(MessageAttr, i18next.t("moveTriggers:splash")) .affectedByGravity(), - new SelfStatusMove(MoveId.ACID_ARMOR, PokemonType.POISON, -1, 20, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true), - new AttackMove(MoveId.CRABHAMMER, PokemonType.WATER, MoveCategory.PHYSICAL, 100, 90, 10, -1, 0, 1) + new SelfStatusMove(MoveId.ACID_ARMOR, PokemonType.POISON, -1, 20, -1, 0, 1) // + .attr(StatStageChangeAttr, [Stat.DEF], 2, true), + new AttackMove(MoveId.CRABHAMMER, PokemonType.WATER, MoveCategory.PHYSICAL, 100, 90, 10, -1, 0, 1) // .attr(HighCritAttr), new AttackMove(MoveId.EXPLOSION, PokemonType.NORMAL, MoveCategory.PHYSICAL, 250, 100, 5, -1, 0, 1) .condition(failIfDampCondition, 3) .attr(SacrificialAttr) .makesContact(false) .target(MoveTarget.ALL_NEAR_OTHERS), - new AttackMove(MoveId.FURY_SWIPES, PokemonType.NORMAL, MoveCategory.PHYSICAL, 18, 80, 15, -1, 0, 1) + new AttackMove(MoveId.FURY_SWIPES, PokemonType.NORMAL, MoveCategory.PHYSICAL, 18, 80, 15, -1, 0, 1) // .attr(MultiHitAttr), new AttackMove(MoveId.BONEMERANG, PokemonType.GROUND, MoveCategory.PHYSICAL, 50, 90, 10, -1, 0, 1) .attr(MultiHitAttr, MultiHitType._2) .makesContact(false), - new SelfStatusMove(MoveId.REST, PokemonType.PSYCHIC, -1, 5, -1, 0, 1) + new SelfStatusMove(MoveId.REST, PokemonType.PSYCHIC, -1, 5, -1, 0, 1) // .attr(RestAttr, 3) .triageMove(), new AttackMove(MoveId.ROCK_SLIDE, PokemonType.ROCK, MoveCategory.PHYSICAL, 75, 90, 10, 30, 0, 1) @@ -9027,25 +9589,27 @@ export function initMoves() { new AttackMove(MoveId.HYPER_FANG, PokemonType.NORMAL, MoveCategory.PHYSICAL, 80, 90, 15, 10, 0, 1) .attr(FlinchAttr) .bitingMove(), - new SelfStatusMove(MoveId.SHARPEN, PokemonType.NORMAL, -1, 30, -1, 0, 1) - .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true), - new SelfStatusMove(MoveId.CONVERSION, PokemonType.NORMAL, -1, 30, -1, 0, 1) + new SelfStatusMove(MoveId.SHARPEN, PokemonType.NORMAL, -1, 30, -1, 0, 1) // + .attr(StatStageChangeAttr, [Stat.ATK], 1, true), + new SelfStatusMove(MoveId.CONVERSION, PokemonType.NORMAL, -1, 30, -1, 0, 1) // .attr(FirstMoveTypeAttr), - new AttackMove(MoveId.TRI_ATTACK, PokemonType.NORMAL, MoveCategory.SPECIAL, 80, 100, 10, 20, 0, 1) - .attr(MultiStatusEffectAttr, [ StatusEffect.BURN, StatusEffect.FREEZE, StatusEffect.PARALYSIS ]), - new AttackMove(MoveId.SUPER_FANG, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, 90, 10, -1, 0, 1) + new AttackMove(MoveId.TRI_ATTACK, PokemonType.NORMAL, MoveCategory.SPECIAL, 80, 100, 10, 20, 0, 1) // + .attr(MultiStatusEffectAttr, [StatusEffect.BURN, StatusEffect.FREEZE, StatusEffect.PARALYSIS]), + new AttackMove(MoveId.SUPER_FANG, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, 90, 10, -1, 0, 1) // .attr(TargetHalfHpDamageAttr), new AttackMove(MoveId.SLASH, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 1) .attr(HighCritAttr) .slicingMove(), - new SelfStatusMove(MoveId.SUBSTITUTE, PokemonType.NORMAL, -1, 10, -1, 0, 1) + new SelfStatusMove(MoveId.SUBSTITUTE, PokemonType.NORMAL, -1, 10, -1, 0, 1) // .attr(AddSubstituteAttr, 0.25, false), new AttackMove(MoveId.STRUGGLE, PokemonType.NORMAL, MoveCategory.PHYSICAL, 50, -1, 1, -1, 0, 1) .attr(RecoilAttr, true, 0.25, true) .attr(TypelessAttr) - .attr(PreMoveMessageAttr, (user: Pokemon) => i18next.t("moveTriggers:struggle", { pokemonName: getPokemonNameWithAffix(user) })) + .attr(PreMoveMessageAttr, (user: Pokemon) => + i18next.t("moveTriggers:struggle", { pokemonName: getPokemonNameWithAffix(user) }), + ) .target(MoveTarget.RANDOM_NEAR_ENEMY), - new StatusMove(MoveId.SKETCH, PokemonType.NORMAL, -1, 1, -1, 0, 2) + new StatusMove(MoveId.SKETCH, PokemonType.NORMAL, -1, 1, -1, 0, 2) // .ignoresSubstitute() .attr(SketchAttr), new AttackMove(MoveId.TRIPLE_KICK, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 10, 90, 10, -1, 0, 2) @@ -9055,8 +9619,8 @@ export function initMoves() { new AttackMove(MoveId.THIEF, PokemonType.DARK, MoveCategory.PHYSICAL, 60, 100, 25, -1, 0, 2) .attr(StealHeldItemChanceAttr, 0.3) .edgeCase(), - // Should not be able to steal held item if user faints due to Rough Skin, Iron Barbs, etc. - // Should be able to steal items from pokemon with Sticky Hold if the damage causes them to faint + // Should not be able to steal held item if user faints due to Rough Skin, Iron Barbs, etc. + // Should be able to steal items from pokemon with Sticky Hold if the damage causes them to faint new StatusMove(MoveId.SPIDER_WEB, PokemonType.BUG, -1, 10, -1, 0, 2) .condition(failIfGhostTypeCondition) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1) @@ -9064,7 +9628,10 @@ export function initMoves() { new StatusMove(MoveId.MIND_READER, PokemonType.NORMAL, -1, 5, -1, 0, 2) .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_ACCURACY, true, false, 2) .attr(MessageAttr, (user, target) => - i18next.t("moveTriggers:tookAimAtTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) }) + i18next.t("moveTriggers:tookAimAtTarget", { + pokemonName: getPokemonNameWithAffix(user), + targetName: getPokemonNameWithAffix(target), + }), ), new StatusMove(MoveId.NIGHTMARE, PokemonType.GHOST, 100, 15, -1, 0, 2) .attr(AddBattlerTagAttr, BattlerTagType.NIGHTMARE) @@ -9082,7 +9649,7 @@ export function initMoves() { .ignoresSubstitute() .ignoresProtect() .target(MoveTarget.CURSE), - new AttackMove(MoveId.FLAIL, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2) + new AttackMove(MoveId.FLAIL, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2) // .attr(LowHpPowerAttr), new StatusMove(MoveId.CONVERSION_2, PokemonType.NORMAL, -1, 30, -1, 0, 2) .attr(ResistLastMoveTypeAttr) @@ -9092,11 +9659,11 @@ export function initMoves() { .windMove() .attr(HighCritAttr), new StatusMove(MoveId.COTTON_SPORE, PokemonType.GRASS, 100, 40, -1, 0, 2) - .attr(StatStageChangeAttr, [ Stat.SPD ], -2) + .attr(StatStageChangeAttr, [Stat.SPD], -2) .powderMove() .target(MoveTarget.ALL_NEAR_ENEMIES) .reflectable(), - new AttackMove(MoveId.REVERSAL, PokemonType.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2) + new AttackMove(MoveId.REVERSAL, PokemonType.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2) // .attr(LowHpPowerAttr), new StatusMove(MoveId.SPITE, PokemonType.GHOST, 100, 10, -1, 0, 2) .ignoresSubstitute() @@ -9108,26 +9675,31 @@ export function initMoves() { new SelfStatusMove(MoveId.PROTECT, PokemonType.NORMAL, -1, 10, -1, 4, 2) .attr(ProtectAttr) .condition(failIfLastCondition, 3), - new AttackMove(MoveId.MACH_PUNCH, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 2) + new AttackMove(MoveId.MACH_PUNCH, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 2) // .punchingMove(), new StatusMove(MoveId.SCARY_FACE, PokemonType.NORMAL, 100, 10, -1, 0, 2) - .attr(StatStageChangeAttr, [ Stat.SPD ], -2) + .attr(StatStageChangeAttr, [Stat.SPD], -2) .reflectable(), new AttackMove(MoveId.FEINT_ATTACK, PokemonType.DARK, MoveCategory.PHYSICAL, 60, -1, 20, -1, 0, 2), - new StatusMove(MoveId.SWEET_KISS, PokemonType.FAIRY, 75, 10, -1, 0, 2) + new StatusMove(MoveId.SWEET_KISS, PokemonType.FAIRY, 75, 10, -1, 0, 2) // .attr(ConfuseAttr) .reflectable(), - new SelfStatusMove(MoveId.BELLY_DRUM, PokemonType.NORMAL, -1, 10, -1, 0, 2) - .attr(CutHpStatStageBoostAttr, [ Stat.ATK ], 12, 2, (user) => { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:cutOwnHpAndMaximizedStat", { pokemonName: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.ATK)) })); + new SelfStatusMove(MoveId.BELLY_DRUM, PokemonType.NORMAL, -1, 10, -1, 0, 2) // + .attr(CutHpStatStageBoostAttr, [Stat.ATK], 12, 2, user => { + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:cutOwnHpAndMaximizedStat", { + pokemonName: getPokemonNameWithAffix(user), + statName: i18next.t(getStatKey(Stat.ATK)), + }), + ); }), new AttackMove(MoveId.SLUDGE_BOMB, PokemonType.POISON, MoveCategory.SPECIAL, 90, 100, 10, 30, 0, 2) .attr(StatusEffectAttr, StatusEffect.POISON) .ballBombMove(), - new AttackMove(MoveId.MUD_SLAP, PokemonType.GROUND, MoveCategory.SPECIAL, 20, 100, 10, 100, 0, 2) - .attr(StatStageChangeAttr, [ Stat.ACC ], -1), + new AttackMove(MoveId.MUD_SLAP, PokemonType.GROUND, MoveCategory.SPECIAL, 20, 100, 10, 100, 0, 2) // + .attr(StatStageChangeAttr, [Stat.ACC], -1), new AttackMove(MoveId.OCTAZOOKA, PokemonType.WATER, MoveCategory.SPECIAL, 65, 85, 10, 50, 0, 2) - .attr(StatStageChangeAttr, [ Stat.ACC ], -1) + .attr(StatStageChangeAttr, [Stat.ACC], -1) .ballBombMove(), new StatusMove(MoveId.SPIKES, PokemonType.GROUND, -1, 20, -1, 0, 2) .attr(AddArenaTrapTagAttr, ArenaTagType.SPIKES) @@ -9144,25 +9716,23 @@ export function initMoves() { .ignoresProtect() .attr(DestinyBondAttr) .condition(failAgainstFinalBossCondition, 2) - .condition((user, target, move) => { - // Retrieves user's previous move, returns empty array if no moves have been used + .condition((user, _target, move) => { const lastTurnMove = user.getLastXMoves(1); - // Checks last move and allows destiny bond to be used if: - // - no previous moves have been made - // - the previous move used was not destiny bond - // - the previous move was unsuccessful - return lastTurnMove.length === 0 || lastTurnMove[0].move !== move.id || lastTurnMove[0].result !== MoveResult.SUCCESS; + return ( + lastTurnMove.length === 0 || lastTurnMove[0].move !== move.id || lastTurnMove[0].result !== MoveResult.SUCCESS + ); }), new StatusMove(MoveId.PERISH_SONG, PokemonType.NORMAL, -1, 5, -1, 0, 2) .attr(AddBattlerTagAttr, BattlerTagType.PERISH_SONG, false, true, 4) .attr(MessageAttr, (_user, target) => - i18next.t("moveTriggers:faintCountdown", { pokemonName: getPokemonNameWithAffix(target), turnCount: 3 })) + i18next.t("moveTriggers:faintCountdown", { pokemonName: getPokemonNameWithAffix(target), turnCount: 3 }), + ) .ignoresProtect() .soundBased() .condition(failOnBossCondition) .target(MoveTarget.ALL), new AttackMove(MoveId.ICY_WIND, PokemonType.ICE, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 2) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1) + .attr(StatStageChangeAttr, [Stat.SPD], -1) .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), new SelfStatusMove(MoveId.DETECT, PokemonType.FIGHTING, -1, 5, -1, 4, 2) @@ -9174,7 +9744,10 @@ export function initMoves() { new StatusMove(MoveId.LOCK_ON, PokemonType.NORMAL, -1, 5, -1, 0, 2) .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_ACCURACY, true, false, 2) .attr(MessageAttr, (user, target) => - i18next.t("moveTriggers:tookAimAtTarget", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target) }) + i18next.t("moveTriggers:tookAimAtTarget", { + pokemonName: getPokemonNameWithAffix(user), + targetName: getPokemonNameWithAffix(target), + }), ), new AttackMove(MoveId.OUTRAGE, PokemonType.DRAGON, MoveCategory.PHYSICAL, 120, 100, 10, -1, 0, 2) .attr(FrenzyAttr) @@ -9191,27 +9764,27 @@ export function initMoves() { .attr(ProtectAttr, BattlerTagType.ENDURING) .condition(failIfLastCondition, 3), new StatusMove(MoveId.CHARM, PokemonType.FAIRY, 100, 20, -1, 0, 2) - .attr(StatStageChangeAttr, [ Stat.ATK ], -2) + .attr(StatStageChangeAttr, [Stat.ATK], -2) .reflectable(), new AttackMove(MoveId.ROLLOUT, PokemonType.ROCK, MoveCategory.PHYSICAL, 30, 90, 20, -1, 0, 2) .partial() // Does not lock the user, also does not increase damage properly .attr(ConsecutiveUseDoublePowerAttr, 5, true, true, MoveId.DEFENSE_CURL), - new AttackMove(MoveId.FALSE_SWIPE, PokemonType.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 2) + new AttackMove(MoveId.FALSE_SWIPE, PokemonType.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 2) // .attr(SurviveDamageAttr), new StatusMove(MoveId.SWAGGER, PokemonType.NORMAL, 85, 15, -1, 0, 2) - .attr(StatStageChangeAttr, [ Stat.ATK ], 2) + .attr(StatStageChangeAttr, [Stat.ATK], 2) .attr(ConfuseAttr) .reflectable(), - new SelfStatusMove(MoveId.MILK_DRINK, PokemonType.NORMAL, -1, 5, -1, 0, 2) + new SelfStatusMove(MoveId.MILK_DRINK, PokemonType.NORMAL, -1, 5, -1, 0, 2) // .attr(HealAttr, 0.5) .triageMove(), - new AttackMove(MoveId.SPARK, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 65, 100, 20, 30, 0, 2) + new AttackMove(MoveId.SPARK, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 65, 100, 20, 30, 0, 2) // .attr(StatusEffectAttr, StatusEffect.PARALYSIS), new AttackMove(MoveId.FURY_CUTTER, PokemonType.BUG, MoveCategory.PHYSICAL, 40, 95, 20, -1, 0, 2) .attr(ConsecutiveUseDoublePowerAttr, 3, true) .slicingMove(), - new AttackMove(MoveId.STEEL_WING, PokemonType.STEEL, MoveCategory.PHYSICAL, 70, 90, 25, 10, 0, 2) - .attr(StatStageChangeAttr, [ Stat.DEF ], 1, true), + new AttackMove(MoveId.STEEL_WING, PokemonType.STEEL, MoveCategory.PHYSICAL, 70, 90, 25, 10, 0, 2) // + .attr(StatStageChangeAttr, [Stat.DEF], 1, true), new StatusMove(MoveId.MEAN_LOOK, PokemonType.NORMAL, -1, 5, -1, 0, 2) .condition(failIfGhostTypeCondition) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1) @@ -9219,7 +9792,7 @@ export function initMoves() { new StatusMove(MoveId.ATTRACT, PokemonType.NORMAL, 100, 15, -1, 0, 2) .attr(AddBattlerTagAttr, BattlerTagType.INFATUATED) .ignoresSubstitute() - .condition((user, target, move) => user.isOppositeGender(target)) + .condition((user, target, _move) => user.isOppositeGender(target)) .reflectable(), new SelfStatusMove(MoveId.SLEEP_TALK, PokemonType.NORMAL, -1, 10, -1, 0, 2) .attr(BypassSleepAttr) @@ -9230,12 +9803,12 @@ export function initMoves() { .attr(PartyStatusCureAttr, i18next.t("moveTriggers:bellChimed"), AbilityId.SOUNDPROOF) .soundBased() .target(MoveTarget.PARTY), - new AttackMove(MoveId.RETURN, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, 100, 20, -1, 0, 2) + new AttackMove(MoveId.RETURN, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, 100, 20, -1, 0, 2) // .attr(FriendshipPowerAttr), new AttackMove(MoveId.PRESENT, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, 90, 15, -1, 0, 2) .attr(PresentPowerAttr) .makesContact(false), - new AttackMove(MoveId.FRUSTRATION, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, 100, 20, -1, 0, 2) + new AttackMove(MoveId.FRUSTRATION, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, 100, 20, -1, 0, 2) // .attr(FriendshipPowerAttr, true), new StatusMove(MoveId.SAFEGUARD, PokemonType.NORMAL, -1, 25, -1, 0, 2) .target(MoveTarget.USER_SIDE) @@ -9250,7 +9823,9 @@ export function initMoves() { new AttackMove(MoveId.MAGNITUDE, PokemonType.GROUND, MoveCategory.PHYSICAL, -1, 100, 30, -1, 0, 2) .attr(PreMoveMessageAttr, magnitudeMessageFunc) .attr(MagnitudePowerAttr) - .attr(MovePowerMultiplierAttr, (user, target, move) => globalScene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1) + .attr(MovePowerMultiplierAttr, (_user, target, _move) => + globalScene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1, + ) .attr(HitsTagForDoubleDamageAttr, BattlerTagType.UNDERGROUND) .makesContact(false) .target(MoveTarget.ALL_NEAR_OTHERS), @@ -9258,7 +9833,7 @@ export function initMoves() { .attr(ConfuseAttr) .punchingMove(), new AttackMove(MoveId.MEGAHORN, PokemonType.BUG, MoveCategory.PHYSICAL, 120, 85, 10, -1, 0, 2), - new AttackMove(MoveId.DRAGON_BREATH, PokemonType.DRAGON, MoveCategory.SPECIAL, 60, 100, 20, 30, 0, 2) + new AttackMove(MoveId.DRAGON_BREATH, PokemonType.DRAGON, MoveCategory.SPECIAL, 60, 100, 20, 30, 0, 2) // .attr(StatusEffectAttr, StatusEffect.PARALYSIS), new SelfStatusMove(MoveId.BATON_PASS, PokemonType.NORMAL, -1, 40, -1, 0, 2) .attr(ForceSwitchOutAttr, true, SwitchType.BATON_PASS) @@ -9267,50 +9842,54 @@ export function initMoves() { new StatusMove(MoveId.ENCORE, PokemonType.NORMAL, 100, 5, -1, 0, 2) .attr(AddBattlerTagAttr, BattlerTagType.ENCORE, false, true) .ignoresSubstitute() - .condition((user, target, move) => new EncoreTag(user.id).canAdd(target)) + .condition((user, target, _move) => new EncoreTag(user.id).canAdd(target)) .reflectable() // Can lock infinitely into struggle; has incorrect interactions with Blood Moon/Gigaton Hammer // Also may or may not incorrectly select targets for replacement move (needs verification) .edgeCase(), - new AttackMove(MoveId.PURSUIT, PokemonType.DARK, MoveCategory.PHYSICAL, 40, 100, 20, -1, 0, 2) + new AttackMove(MoveId.PURSUIT, PokemonType.DARK, MoveCategory.PHYSICAL, 40, 100, 20, -1, 0, 2) // .partial(), // No effect implemented new AttackMove(MoveId.RAPID_SPIN, PokemonType.NORMAL, MoveCategory.PHYSICAL, 50, 100, 40, 100, 0, 2) - .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true) - .attr(RemoveBattlerTagAttr, [ - BattlerTagType.BIND, - BattlerTagType.WRAP, - BattlerTagType.FIRE_SPIN, - BattlerTagType.WHIRLPOOL, - BattlerTagType.CLAMP, - BattlerTagType.SAND_TOMB, - BattlerTagType.MAGMA_STORM, - BattlerTagType.SNAP_TRAP, - BattlerTagType.THUNDER_CAGE, - BattlerTagType.SEEDED, - BattlerTagType.INFESTATION - ], true) + .attr(StatStageChangeAttr, [Stat.SPD], 1, true) + .attr( + RemoveBattlerTagAttr, + [ + BattlerTagType.BIND, + BattlerTagType.WRAP, + BattlerTagType.FIRE_SPIN, + BattlerTagType.WHIRLPOOL, + BattlerTagType.CLAMP, + BattlerTagType.SAND_TOMB, + BattlerTagType.MAGMA_STORM, + BattlerTagType.SNAP_TRAP, + BattlerTagType.THUNDER_CAGE, + BattlerTagType.SEEDED, + BattlerTagType.INFESTATION, + ], + true, + ) .attr(RemoveArenaTrapAttr), new StatusMove(MoveId.SWEET_SCENT, PokemonType.NORMAL, 100, 20, -1, 0, 2) - .attr(StatStageChangeAttr, [ Stat.EVA ], -2) + .attr(StatStageChangeAttr, [Stat.EVA], -2) .target(MoveTarget.ALL_NEAR_ENEMIES) .reflectable(), - new AttackMove(MoveId.IRON_TAIL, PokemonType.STEEL, MoveCategory.PHYSICAL, 100, 75, 15, 30, 0, 2) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1), - new AttackMove(MoveId.METAL_CLAW, PokemonType.STEEL, MoveCategory.PHYSICAL, 50, 95, 35, 10, 0, 2) - .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true), + new AttackMove(MoveId.IRON_TAIL, PokemonType.STEEL, MoveCategory.PHYSICAL, 100, 75, 15, 30, 0, 2) // + .attr(StatStageChangeAttr, [Stat.DEF], -1), + new AttackMove(MoveId.METAL_CLAW, PokemonType.STEEL, MoveCategory.PHYSICAL, 50, 95, 35, 10, 0, 2) // + .attr(StatStageChangeAttr, [Stat.ATK], 1, true), new AttackMove(MoveId.VITAL_THROW, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 70, -1, 10, -1, -1, 2), - new SelfStatusMove(MoveId.MORNING_SUN, PokemonType.NORMAL, -1, 5, -1, 0, 2) + new SelfStatusMove(MoveId.MORNING_SUN, PokemonType.NORMAL, -1, 5, -1, 0, 2) // .attr(PlantHealAttr) .triageMove(), - new SelfStatusMove(MoveId.SYNTHESIS, PokemonType.GRASS, -1, 5, -1, 0, 2) + new SelfStatusMove(MoveId.SYNTHESIS, PokemonType.GRASS, -1, 5, -1, 0, 2) // .attr(PlantHealAttr) .triageMove(), - new SelfStatusMove(MoveId.MOONLIGHT, PokemonType.FAIRY, -1, 5, -1, 0, 2) + new SelfStatusMove(MoveId.MOONLIGHT, PokemonType.FAIRY, -1, 5, -1, 0, 2) // .attr(PlantHealAttr) .triageMove(), - new AttackMove(MoveId.HIDDEN_POWER, PokemonType.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 2) + new AttackMove(MoveId.HIDDEN_POWER, PokemonType.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 2) // .attr(HiddenPowerTypeAttr), - new AttackMove(MoveId.CROSS_CHOP, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 100, 80, 5, -1, 0, 2) + new AttackMove(MoveId.CROSS_CHOP, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 100, 80, 5, -1, 0, 2) // .attr(HighCritAttr), new AttackMove(MoveId.TWISTER, PokemonType.DRAGON, MoveCategory.SPECIAL, 40, 100, 20, 20, 0, 2) .attr(HitsTagForDoubleDamageAttr, BattlerTagType.FLYING) @@ -9324,31 +9903,29 @@ export function initMoves() { .attr(WeatherChangeAttr, WeatherType.SUNNY) .target(MoveTarget.BOTH_SIDES), new AttackMove(MoveId.CRUNCH, PokemonType.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 20, 0, 2) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1) + .attr(StatStageChangeAttr, [Stat.DEF], -1) .bitingMove(), new AttackMove(MoveId.MIRROR_COAT, PokemonType.PSYCHIC, MoveCategory.SPECIAL, -1, 100, 20, -1, -5, 2) .attr(CounterDamageAttr, 2, MoveCategory.SPECIAL) .attr(CounterRedirectAttr, MoveCategory.SPECIAL) .condition(counterAttackConditionSpecial, 3) .target(MoveTarget.ATTACKER), - new StatusMove(MoveId.PSYCH_UP, PokemonType.NORMAL, -1, 10, -1, 0, 2) + new StatusMove(MoveId.PSYCH_UP, PokemonType.NORMAL, -1, 10, -1, 0, 2) // .ignoresSubstitute() .attr(CopyStatsAttr), new AttackMove(MoveId.EXTREME_SPEED, PokemonType.NORMAL, MoveCategory.PHYSICAL, 80, 100, 5, -1, 2, 2), - new AttackMove(MoveId.ANCIENT_POWER, PokemonType.ROCK, MoveCategory.SPECIAL, 60, 100, 5, 10, 0, 2) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true), + new AttackMove(MoveId.ANCIENT_POWER, PokemonType.ROCK, MoveCategory.SPECIAL, 60, 100, 5, 10, 0, 2) // + .attr(StatStageChangeAttr, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], 1, true), new AttackMove(MoveId.SHADOW_BALL, PokemonType.GHOST, MoveCategory.SPECIAL, 80, 100, 15, 20, 0, 2) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) + .attr(StatStageChangeAttr, [Stat.SPDEF], -1) .ballBombMove(), new AttackMove(MoveId.FUTURE_SIGHT, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 120, 100, 10, -1, 0, 2) .attr(DelayedAttackAttr, ChargeAnim.FUTURE_SIGHT_CHARGING, "moveTriggers:foresawAnAttack") .ignoresProtect() - /* - * Should not apply abilities or held items if user is off the field - */ + // Should not apply abilities or held items if user is off the field .edgeCase(), - new AttackMove(MoveId.ROCK_SMASH, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 15, 50, 0, 2) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1), + new AttackMove(MoveId.ROCK_SMASH, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 15, 50, 0, 2) // + .attr(StatStageChangeAttr, [Stat.DEF], -1), new AttackMove(MoveId.WHIRLPOOL, PokemonType.WATER, MoveCategory.SPECIAL, 35, 85, 15, -1, 0, 2) .attr(TrapAttr, BattlerTagType.WHIRLPOOL) .attr(HitsTagForDoubleDamageAttr, BattlerTagType.UNDERWATER), @@ -9362,19 +9939,20 @@ export function initMoves() { new AttackMove(MoveId.UPROAR, PokemonType.NORMAL, MoveCategory.SPECIAL, 90, 100, 10, -1, 0, 3) .soundBased() .target(MoveTarget.RANDOM_NEAR_ENEMY) - .partial(), // Does not lock the user, does not stop Pokemon from sleeping + // Does not lock the user, does not stop Pokemon from sleeping // Likely can make use of FrenzyAttr and an ArenaTag (just without the FrenzyMissFunc) + .partial(), new SelfStatusMove(MoveId.STOCKPILE, PokemonType.NORMAL, -1, 20, -1, 0, 3) .condition(user => (user.getTag(StockpilingTag)?.stockpiledCount ?? 0) < 3, 3) .attr(AddBattlerTagAttr, BattlerTagType.STOCKPILING, true), new AttackMove(MoveId.SPIT_UP, PokemonType.NORMAL, MoveCategory.SPECIAL, -1, 100, 10, -1, 0, 3) .condition(hasStockpileStacksCondition, 3) .attr(SpitUpPowerAttr, 100) - .attr(RemoveBattlerTagAttr, [ BattlerTagType.STOCKPILING ], true), + .attr(RemoveBattlerTagAttr, [BattlerTagType.STOCKPILING], true), new SelfStatusMove(MoveId.SWALLOW, PokemonType.NORMAL, -1, 10, -1, 0, 3) .condition(hasStockpileStacksCondition, 3) .attr(SwallowHealAttr) - .attr(RemoveBattlerTagAttr, [ BattlerTagType.STOCKPILING ], true) + .attr(RemoveBattlerTagAttr, [BattlerTagType.STOCKPILING], true) .triageMove() // TODO: Verify if using Swallow at full HP still consumes stacks or not .edgeCase(), @@ -9392,7 +9970,7 @@ export function initMoves() { .attr(AddBattlerTagAttr, BattlerTagType.TORMENT, false, true, 1) .reflectable(), new StatusMove(MoveId.FLATTER, PokemonType.DARK, 100, 15, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.SPATK ], 1) + .attr(StatStageChangeAttr, [Stat.SPATK], 1) .attr(ConfuseAttr) .reflectable(), new StatusMove(MoveId.WILL_O_WISP, PokemonType.FIRE, 85, 15, -1, 0, 3) @@ -9400,25 +9978,40 @@ export function initMoves() { .reflectable(), new StatusMove(MoveId.MEMENTO, PokemonType.DARK, 100, 10, -1, 0, 3) .attr(SacrificialAttrOnHit) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -2), + .attr(StatStageChangeAttr, [Stat.ATK, Stat.SPATK], -2), new AttackMove(MoveId.FACADE, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 3) - .attr(MovePowerMultiplierAttr, (user, target, move) => user.status - && (user.status.effect === StatusEffect.BURN || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.PARALYSIS) ? 2 : 1) + .attr(MovePowerMultiplierAttr, (user, _target, _move) => + user.status + && (user.status.effect === StatusEffect.BURN + || user.status.effect === StatusEffect.POISON + || user.status.effect === StatusEffect.TOXIC + || user.status.effect === StatusEffect.PARALYSIS) + ? 2 + : 1, + ) .attr(BypassBurnDamageReductionAttr), new AttackMove(MoveId.FOCUS_PUNCH, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 20, -1, -3, 3) - .attr(MessageHeaderAttr, (user) => i18next.t("moveTriggers:isTighteningFocus", { pokemonName: getPokemonNameWithAffix(user) })) - .attr(PreUseInterruptAttr, (user) => i18next.t("moveTriggers:lostFocus", { pokemonName: getPokemonNameWithAffix(user) }), user => user.turnData.attacksReceived.some(r => r.damage > 0)) + .attr(MessageHeaderAttr, user => + i18next.t("moveTriggers:isTighteningFocus", { pokemonName: getPokemonNameWithAffix(user) }), + ) + .attr( + PreUseInterruptAttr, + user => i18next.t("moveTriggers:lostFocus", { pokemonName: getPokemonNameWithAffix(user) }), + user => user.turnData.attacksReceived.some(r => r.damage > 0), + ) .punchingMove(), new AttackMove(MoveId.SMELLING_SALTS, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 10, -1, 0, 3) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.status?.effect === StatusEffect.PARALYSIS ? 2 : 1) + .attr(MovePowerMultiplierAttr, (_user, target, _move) => + target.status?.effect === StatusEffect.PARALYSIS ? 2 : 1, + ) .attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS), new SelfStatusMove(MoveId.FOLLOW_ME, PokemonType.NORMAL, -1, 20, -1, 2, 3) .attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, true) .condition(failIfSingleBattle, 3), - new StatusMove(MoveId.NATURE_POWER, PokemonType.NORMAL, -1, 20, -1, 0, 3) + new StatusMove(MoveId.NATURE_POWER, PokemonType.NORMAL, -1, 20, -1, 0, 3) // .attr(NaturePowerAttr), new SelfStatusMove(MoveId.CHARGE, PokemonType.ELECTRIC, -1, 20, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], 1, true) + .attr(StatStageChangeAttr, [Stat.SPDEF], 1, true) .attr(AddBattlerTagAttr, BattlerTagType.CHARGED, true, false), new StatusMove(MoveId.TAUNT, PokemonType.DARK, 100, 20, -1, 0, 3) .ignoresSubstitute() @@ -9431,24 +10024,24 @@ export function initMoves() { .condition(failIfSingleBattle) // should stack multiplicatively if used multiple times in 1 turn .edgeCase(), - new StatusMove(MoveId.TRICK, PokemonType.PSYCHIC, 100, 10, -1, 0, 3) + new StatusMove(MoveId.TRICK, PokemonType.PSYCHIC, 100, 10, -1, 0, 3) // .unimplemented(), new StatusMove(MoveId.ROLE_PLAY, PokemonType.PSYCHIC, -1, 10, -1, 0, 3) .ignoresSubstitute() // TODO: Enable / remove once balance reaches a consensus on ability overrides during boss fights // .condition(failAgainstFinalBossCondition, 3) .attr(AbilityCopyAttr), - new SelfStatusMove(MoveId.WISH, PokemonType.NORMAL, -1, 10, -1, 0, 3) + new SelfStatusMove(MoveId.WISH, PokemonType.NORMAL, -1, 10, -1, 0, 3) // .attr(WishAttr) .triageMove(), - new SelfStatusMove(MoveId.ASSIST, PokemonType.NORMAL, -1, 20, -1, 0, 3) + new SelfStatusMove(MoveId.ASSIST, PokemonType.NORMAL, -1, 20, -1, 0, 3) // .attr(RandomMovesetMoveAttr, invalidAssistMoves, true), new SelfStatusMove(MoveId.INGRAIN, PokemonType.GRASS, -1, 20, -1, 0, 3) .attr(AddBattlerTagAttr, BattlerTagType.INGRAIN, true, true) .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, true, true) - .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLOATING ], true), - new AttackMove(MoveId.SUPERPOWER, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], -1, true), + .attr(RemoveBattlerTagAttr, [BattlerTagType.FLOATING], true), + new AttackMove(MoveId.SUPERPOWER, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 3) // + .attr(StatStageChangeAttr, [Stat.ATK, Stat.DEF], -1, true), new SelfStatusMove(MoveId.MAGIC_COAT, PokemonType.PSYCHIC, -1, 15, -1, 4, 3) .attr(AddBattlerTagAttr, BattlerTagType.MAGIC_COAT, true, true, 0) .condition(failIfLastCondition, 3) @@ -9456,22 +10049,24 @@ export function initMoves() { // rely on move history // Also will not reflect roar / whirlwind if the target has ForceSwitchOutImmunityAbAttr .edgeCase(), - new SelfStatusMove(MoveId.RECYCLE, PokemonType.NORMAL, -1, 10, -1, 0, 3) + new SelfStatusMove(MoveId.RECYCLE, PokemonType.NORMAL, -1, 10, -1, 0, 3) // .unimplemented(), - new AttackMove(MoveId.REVENGE, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, -4, 3) + new AttackMove(MoveId.REVENGE, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, -4, 3) // .attr(TurnDamagedDoublePowerAttr), - new AttackMove(MoveId.BRICK_BREAK, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 15, -1, 0, 3) + new AttackMove(MoveId.BRICK_BREAK, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 75, 100, 15, -1, 0, 3) // .attr(RemoveScreensAttr), new StatusMove(MoveId.YAWN, PokemonType.NORMAL, -1, 10, -1, 0, 3) .attr(AddBattlerTagAttr, BattlerTagType.DROWSY, false, true) - .condition((user, target, move) => !target.status && !target.isSafeguarded(user)) + .condition((user, target, _move) => !target.status && !target.isSafeguarded(user)) .reflectable(), new AttackMove(MoveId.KNOCK_OFF, PokemonType.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, 0, 3) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.getHeldItems().filter(i => i.isTransferable).length > 0 ? 1.5 : 1) + .attr(MovePowerMultiplierAttr, (_user, target, _move) => + target.getHeldItems().filter(i => i.isTransferable).length > 0 ? 1.5 : 1, + ) .attr(RemoveHeldItemAttr, false) - .edgeCase(), // Should not be able to remove held item if user faints due to Rough Skin, Iron Barbs, etc. // Should be able to remove items from pokemon with Sticky Hold if the damage causes them to faint + .edgeCase(), new AttackMove(MoveId.ENDEAVOR, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 3) .attr(MatchHpAttr) .condition(failOnBossCondition), @@ -9488,11 +10083,23 @@ export function initMoves() { // .condition(failAgainstFinalBossCondition, 2) .target(MoveTarget.ENEMY_SIDE), new SelfStatusMove(MoveId.REFRESH, PokemonType.NORMAL, -1, 20, -1, 0, 3) - .attr(HealStatusEffectAttr, true, [ StatusEffect.PARALYSIS, StatusEffect.POISON, StatusEffect.TOXIC, StatusEffect.BURN ]) - .condition((user, target, move) => !!user.status && (user.status.effect === StatusEffect.PARALYSIS || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.BURN)), - new SelfStatusMove(MoveId.GRUDGE, PokemonType.GHOST, -1, 5, -1, 0, 3) + .attr(HealStatusEffectAttr, true, [ + StatusEffect.PARALYSIS, + StatusEffect.POISON, + StatusEffect.TOXIC, + StatusEffect.BURN, + ]) + .condition( + (user, _target, _move) => + !!user.status + && (user.status.effect === StatusEffect.PARALYSIS + || user.status.effect === StatusEffect.POISON + || user.status.effect === StatusEffect.TOXIC + || user.status.effect === StatusEffect.BURN), + ), + new SelfStatusMove(MoveId.GRUDGE, PokemonType.GHOST, -1, 5, -1, 0, 3) // .attr(AddBattlerTagAttr, BattlerTagType.GRUDGE, true, undefined, 1), - new SelfStatusMove(MoveId.SNATCH, PokemonType.DARK, -1, 10, -1, 4, 3) + new SelfStatusMove(MoveId.SNATCH, PokemonType.DARK, -1, 10, -1, 4, 3) // .unimplemented(), new AttackMove(MoveId.SECRET_POWER, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, 30, 0, 3) .makesContact(false) @@ -9501,19 +10108,19 @@ export function initMoves() { .chargeText(i18next.t("moveTriggers:hidUnderwater", { pokemonName: "{USER}" })) .chargeAttr(SemiInvulnerableAttr, BattlerTagType.UNDERWATER) .chargeAttr(GulpMissileTagAttr), - new AttackMove(MoveId.ARM_THRUST, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 15, 100, 20, -1, 0, 3) + new AttackMove(MoveId.ARM_THRUST, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 15, 100, 20, -1, 0, 3) // .attr(MultiHitAttr), - new SelfStatusMove(MoveId.CAMOUFLAGE, PokemonType.NORMAL, -1, 20, -1, 0, 3) + new SelfStatusMove(MoveId.CAMOUFLAGE, PokemonType.NORMAL, -1, 20, -1, 0, 3) // .attr(CopyBiomeTypeAttr), - new SelfStatusMove(MoveId.TAIL_GLOW, PokemonType.BUG, -1, 20, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.SPATK ], 3, true), - new AttackMove(MoveId.LUSTER_PURGE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 95, 100, 5, 50, 0, 3) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1), + new SelfStatusMove(MoveId.TAIL_GLOW, PokemonType.BUG, -1, 20, -1, 0, 3) // + .attr(StatStageChangeAttr, [Stat.SPATK], 3, true), + new AttackMove(MoveId.LUSTER_PURGE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 95, 100, 5, 50, 0, 3) // + .attr(StatStageChangeAttr, [Stat.SPDEF], -1), new AttackMove(MoveId.MIST_BALL, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 95, 100, 5, 50, 0, 3) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -1) + .attr(StatStageChangeAttr, [Stat.SPATK], -1) .ballBombMove(), new StatusMove(MoveId.FEATHER_DANCE, PokemonType.FLYING, 100, 15, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.ATK ], -2) + .attr(StatStageChangeAttr, [Stat.ATK], -2) .danceMove() .reflectable(), new StatusMove(MoveId.TEETER_DANCE, PokemonType.NORMAL, 100, 20, -1, 0, 3) @@ -9531,9 +10138,9 @@ export function initMoves() { .partial() // Does not lock the user properly, does not increase damage correctly .attr(ConsecutiveUseDoublePowerAttr, 5, true, true, MoveId.DEFENSE_CURL) .ballBombMove(), - new AttackMove(MoveId.NEEDLE_ARM, PokemonType.GRASS, MoveCategory.PHYSICAL, 60, 100, 15, 30, 0, 3) + new AttackMove(MoveId.NEEDLE_ARM, PokemonType.GRASS, MoveCategory.PHYSICAL, 60, 100, 15, 30, 0, 3) // .attr(FlinchAttr), - new SelfStatusMove(MoveId.SLACK_OFF, PokemonType.NORMAL, -1, 5, -1, 0, 3) + new SelfStatusMove(MoveId.SLACK_OFF, PokemonType.NORMAL, -1, 5, -1, 0, 3) // .attr(HealAttr, 0.5) .triageMove(), new AttackMove(MoveId.HYPER_VOICE, PokemonType.NORMAL, MoveCategory.SPECIAL, 90, 100, 10, -1, 0, 3) @@ -9542,25 +10149,34 @@ export function initMoves() { new AttackMove(MoveId.POISON_FANG, PokemonType.POISON, MoveCategory.PHYSICAL, 50, 100, 15, 50, 0, 3) .attr(StatusEffectAttr, StatusEffect.TOXIC) .bitingMove(), - new AttackMove(MoveId.CRUSH_CLAW, PokemonType.NORMAL, MoveCategory.PHYSICAL, 75, 95, 10, 50, 0, 3) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1), - new AttackMove(MoveId.BLAST_BURN, PokemonType.FIRE, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 3) + new AttackMove(MoveId.CRUSH_CLAW, PokemonType.NORMAL, MoveCategory.PHYSICAL, 75, 95, 10, 50, 0, 3) // + .attr(StatStageChangeAttr, [Stat.DEF], -1), + new AttackMove(MoveId.BLAST_BURN, PokemonType.FIRE, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 3) // .attr(RechargeAttr), - new AttackMove(MoveId.HYDRO_CANNON, PokemonType.WATER, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 3) + new AttackMove(MoveId.HYDRO_CANNON, PokemonType.WATER, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 3) // .attr(RechargeAttr), new AttackMove(MoveId.METEOR_MASH, PokemonType.STEEL, MoveCategory.PHYSICAL, 90, 90, 10, 20, 0, 3) - .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true) + .attr(StatStageChangeAttr, [Stat.ATK], 1, true) .punchingMove(), - new AttackMove(MoveId.ASTONISH, PokemonType.GHOST, MoveCategory.PHYSICAL, 30, 100, 15, 30, 0, 3) + new AttackMove(MoveId.ASTONISH, PokemonType.GHOST, MoveCategory.PHYSICAL, 30, 100, 15, 30, 0, 3) // .attr(FlinchAttr), new AttackMove(MoveId.WEATHER_BALL, PokemonType.NORMAL, MoveCategory.SPECIAL, 50, 100, 10, -1, 0, 3) .attr(WeatherBallTypeAttr) - .attr(MovePowerMultiplierAttr, (user, target, move) => { + .attr(MovePowerMultiplierAttr, (_user, _target, _move) => { const weather = globalScene.arena.weather; if (!weather) { return 1; } - const weatherTypes = [ WeatherType.SUNNY, WeatherType.RAIN, WeatherType.SANDSTORM, WeatherType.HAIL, WeatherType.SNOW, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HARSH_SUN ]; + const weatherTypes = [ + WeatherType.SUNNY, + WeatherType.RAIN, + WeatherType.SANDSTORM, + WeatherType.HAIL, + WeatherType.SNOW, + WeatherType.FOG, + WeatherType.HEAVY_RAIN, + WeatherType.HARSH_SUN, + ]; if (weatherTypes.includes(weather.weatherType) && !weather.isEffectSuppressed()) { return 2; } @@ -9571,7 +10187,7 @@ export function initMoves() { .attr(PartyStatusCureAttr, i18next.t("moveTriggers:soothingAromaWaftedThroughArea"), AbilityId.SAP_SIPPER) .target(MoveTarget.PARTY), new StatusMove(MoveId.FAKE_TEARS, PokemonType.DARK, 100, 20, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -2) + .attr(StatStageChangeAttr, [Stat.SPDEF], -2) .reflectable(), new AttackMove(MoveId.AIR_CUTTER, PokemonType.FLYING, MoveCategory.SPECIAL, 60, 95, 25, -1, 0, 3) .attr(HighCritAttr) @@ -9579,20 +10195,20 @@ export function initMoves() { .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(MoveId.OVERHEAT, PokemonType.FIRE, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true) + .attr(StatStageChangeAttr, [Stat.SPATK], -2, true) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE), new StatusMove(MoveId.ODOR_SLEUTH, PokemonType.NORMAL, -1, 40, -1, 0, 3) .attr(ExposedMoveAttr, BattlerTagType.IGNORE_GHOST) .ignoresSubstitute() .reflectable(), new AttackMove(MoveId.ROCK_TOMB, PokemonType.ROCK, MoveCategory.PHYSICAL, 60, 95, 15, 100, 0, 3) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1) + .attr(StatStageChangeAttr, [Stat.SPD], -1) .makesContact(false), new AttackMove(MoveId.SILVER_WIND, PokemonType.BUG, MoveCategory.SPECIAL, 60, 100, 5, 10, 0, 3) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], 1, true) .windMove(), new StatusMove(MoveId.METAL_SOUND, PokemonType.STEEL, 85, 40, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -2) + .attr(StatStageChangeAttr, [Stat.SPDEF], -2) .soundBased() .reflectable(), new StatusMove(MoveId.GRASS_WHISTLE, PokemonType.GRASS, 55, 15, -1, 0, 3) @@ -9600,18 +10216,18 @@ export function initMoves() { .soundBased() .reflectable(), new StatusMove(MoveId.TICKLE, PokemonType.NORMAL, 100, 20, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], -1) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.DEF], -1) .reflectable(), - new SelfStatusMove(MoveId.COSMIC_POWER, PokemonType.PSYCHIC, -1, 20, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], 1, true), + new SelfStatusMove(MoveId.COSMIC_POWER, PokemonType.PSYCHIC, -1, 20, -1, 0, 3) // + .attr(StatStageChangeAttr, [Stat.DEF, Stat.SPDEF], 1, true), new AttackMove(MoveId.WATER_SPOUT, PokemonType.WATER, MoveCategory.SPECIAL, 150, 100, 5, -1, 0, 3) .attr(HpPowerAttr) .target(MoveTarget.ALL_NEAR_ENEMIES), - new AttackMove(MoveId.SIGNAL_BEAM, PokemonType.BUG, MoveCategory.SPECIAL, 75, 100, 15, 10, 0, 3) + new AttackMove(MoveId.SIGNAL_BEAM, PokemonType.BUG, MoveCategory.SPECIAL, 75, 100, 15, 10, 0, 3) // .attr(ConfuseAttr), - new AttackMove(MoveId.SHADOW_PUNCH, PokemonType.GHOST, MoveCategory.PHYSICAL, 60, -1, 20, -1, 0, 3) + new AttackMove(MoveId.SHADOW_PUNCH, PokemonType.GHOST, MoveCategory.PHYSICAL, 60, -1, 20, -1, 0, 3) // .punchingMove(), - new AttackMove(MoveId.EXTRASENSORY, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 20, 10, 0, 3) + new AttackMove(MoveId.EXTRASENSORY, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 20, 10, 0, 3) // .attr(FlinchAttr), new AttackMove(MoveId.SKY_UPPERCUT, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 85, 90, 15, -1, 0, 3) .attr(HitsTagAttr, BattlerTagType.FLYING) @@ -9624,47 +10240,47 @@ export function initMoves() { .attr(OneHitKOAttr) .attr(SheerColdAccuracyAttr), new AttackMove(MoveId.MUDDY_WATER, PokemonType.WATER, MoveCategory.SPECIAL, 90, 85, 10, 30, 0, 3) - .attr(StatStageChangeAttr, [ Stat.ACC ], -1) + .attr(StatStageChangeAttr, [Stat.ACC], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(MoveId.BULLET_SEED, PokemonType.GRASS, MoveCategory.PHYSICAL, 25, 100, 30, -1, 0, 3) .attr(MultiHitAttr) .makesContact(false) .ballBombMove(), - new AttackMove(MoveId.AERIAL_ACE, PokemonType.FLYING, MoveCategory.PHYSICAL, 60, -1, 20, -1, 0, 3) + new AttackMove(MoveId.AERIAL_ACE, PokemonType.FLYING, MoveCategory.PHYSICAL, 60, -1, 20, -1, 0, 3) // .slicingMove(), new AttackMove(MoveId.ICICLE_SPEAR, PokemonType.ICE, MoveCategory.PHYSICAL, 25, 100, 30, -1, 0, 3) .attr(MultiHitAttr) .makesContact(false), - new SelfStatusMove(MoveId.IRON_DEFENSE, PokemonType.STEEL, -1, 15, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true), + new SelfStatusMove(MoveId.IRON_DEFENSE, PokemonType.STEEL, -1, 15, -1, 0, 3) // + .attr(StatStageChangeAttr, [Stat.DEF], 2, true), new StatusMove(MoveId.BLOCK, PokemonType.NORMAL, -1, 5, -1, 0, 3) .condition(failIfGhostTypeCondition) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, true, 1) .reflectable(), new StatusMove(MoveId.HOWL, PokemonType.NORMAL, -1, 40, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.ATK ], 1) + .attr(StatStageChangeAttr, [Stat.ATK], 1) .soundBased() .target(MoveTarget.USER_AND_ALLIES), new AttackMove(MoveId.DRAGON_CLAW, PokemonType.DRAGON, MoveCategory.PHYSICAL, 80, 100, 15, -1, 0, 3), - new AttackMove(MoveId.FRENZY_PLANT, PokemonType.GRASS, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 3) + new AttackMove(MoveId.FRENZY_PLANT, PokemonType.GRASS, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 3) // .attr(RechargeAttr), - new SelfStatusMove(MoveId.BULK_UP, PokemonType.FIGHTING, -1, 20, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], 1, true), + new SelfStatusMove(MoveId.BULK_UP, PokemonType.FIGHTING, -1, 20, -1, 0, 3) // + .attr(StatStageChangeAttr, [Stat.ATK, Stat.DEF], 1, true), new ChargingAttackMove(MoveId.BOUNCE, PokemonType.FLYING, MoveCategory.PHYSICAL, 85, 85, 5, 30, 0, 3) .chargeText(i18next.t("moveTriggers:sprangUp", { pokemonName: "{USER}" })) .chargeAttr(SemiInvulnerableAttr, BattlerTagType.FLYING) .attr(StatusEffectAttr, StatusEffect.PARALYSIS) .affectedByGravity(), - new AttackMove(MoveId.MUD_SHOT, PokemonType.GROUND, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 3) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1), + new AttackMove(MoveId.MUD_SHOT, PokemonType.GROUND, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 3) // + .attr(StatStageChangeAttr, [Stat.SPD], -1), new AttackMove(MoveId.POISON_TAIL, PokemonType.POISON, MoveCategory.PHYSICAL, 50, 100, 25, 10, 0, 3) .attr(HighCritAttr) .attr(StatusEffectAttr, StatusEffect.POISON), new AttackMove(MoveId.COVET, PokemonType.NORMAL, MoveCategory.PHYSICAL, 60, 100, 25, -1, 0, 3) .attr(StealHeldItemChanceAttr, 0.3) - .edgeCase(), // Should not be able to steal held item if user faints due to Rough Skin, Iron Barbs, etc. // Should be able to steal items from pokemon with Sticky Hold if the damage causes them to faint + .edgeCase(), new AttackMove(MoveId.VOLT_TACKLE, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 120, 100, 15, 10, 0, 3) .attr(RecoilAttr, false, 0.33) .attr(StatusEffectAttr, StatusEffect.PARALYSIS) @@ -9674,13 +10290,13 @@ export function initMoves() { .ignoresProtect() .attr(AddArenaTagAttr, ArenaTagType.WATER_SPORT, 5) .target(MoveTarget.BOTH_SIDES), - new SelfStatusMove(MoveId.CALM_MIND, PokemonType.PSYCHIC, -1, 20, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.SPATK, Stat.SPDEF ], 1, true), + new SelfStatusMove(MoveId.CALM_MIND, PokemonType.PSYCHIC, -1, 20, -1, 0, 3) // + .attr(StatStageChangeAttr, [Stat.SPATK, Stat.SPDEF], 1, true), new AttackMove(MoveId.LEAF_BLADE, PokemonType.GRASS, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 3) .attr(HighCritAttr) .slicingMove(), new SelfStatusMove(MoveId.DRAGON_DANCE, PokemonType.DRAGON, -1, 20, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPD ], 1, true) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.SPD], 1, true) .danceMove(), new AttackMove(MoveId.ROCK_BLAST, PokemonType.ROCK, MoveCategory.PHYSICAL, 25, 90, 10, -1, 0, 3) .attr(MultiHitAttr) @@ -9693,12 +10309,10 @@ export function initMoves() { new AttackMove(MoveId.DOOM_DESIRE, PokemonType.STEEL, MoveCategory.SPECIAL, 140, 100, 5, -1, 0, 3) .attr(DelayedAttackAttr, ChargeAnim.DOOM_DESIRE_CHARGING, "moveTriggers:choseDoomDesireAsDestiny") .ignoresProtect() - /* - * Should not apply abilities or held items if user is off the field - */ + // Should not apply abilities or held items if user is off the field .edgeCase(), - new AttackMove(MoveId.PSYCHO_BOOST, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 140, 90, 5, -1, 0, 3) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true), + new AttackMove(MoveId.PSYCHO_BOOST, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 140, 90, 5, -1, 0, 3) // + .attr(StatStageChangeAttr, [Stat.SPATK], -2, true), new SelfStatusMove(MoveId.ROOST, PokemonType.FLYING, -1, 5, -1, 0, 4) .attr(HealAttr, 0.5) .attr(AddBattlerTagAttr, BattlerTagType.ROOSTED, true, false) @@ -9713,10 +10327,12 @@ export function initMoves() { .ignoresSubstitute() .reflectable(), new AttackMove(MoveId.WAKE_UP_SLAP, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 70, 100, 10, -1, 0, 4) - .attr(MovePowerMultiplierAttr, (user, target, move) => targetSleptOrComatoseCondition(user, target, move) ? 2 : 1) + .attr(MovePowerMultiplierAttr, (user, target, move) => + targetSleptOrComatoseCondition(user, target, move) ? 2 : 1, + ) .attr(HealStatusEffectAttr, false, StatusEffect.SLEEP), new AttackMove(MoveId.HAMMER_ARM, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 100, 90, 10, -1, 0, 4) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1, true) + .attr(StatStageChangeAttr, [Stat.SPD], -1, true) .punchingMove(), new AttackMove(MoveId.GYRO_BALL, PokemonType.STEEL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4) .attr(GyroBallPowerAttr) @@ -9725,22 +10341,26 @@ export function initMoves() { .attr(SacrificialFullRestoreAttr, false, "moveTriggers:sacrificialFullRestore") .triageMove() .condition(failIfLastInPartyCondition), - new AttackMove(MoveId.BRINE, PokemonType.WATER, MoveCategory.SPECIAL, 65, 100, 10, -1, 0, 4) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.getHpRatio() < 0.5 ? 2 : 1), + new AttackMove(MoveId.BRINE, PokemonType.WATER, MoveCategory.SPECIAL, 65, 100, 10, -1, 0, 4) // + .attr(MovePowerMultiplierAttr, (_user, target, _move) => (target.getHpRatio() < 0.5 ? 2 : 1)), new AttackMove(MoveId.NATURAL_GIFT, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 4) .makesContact(false) - .unimplemented(), /* NOTE: To whoever tries to implement this, reminder to push to battleData.berriesEaten and enable the harvest test.. Do NOT push to berriesEatenLast or else cud chew will puke the berry. */ + .unimplemented(), new AttackMove(MoveId.FEINT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 30, 100, 10, -1, 2, 4) - .attr(RemoveBattlerTagAttr, [ BattlerTagType.PROTECTED ]) - .attr(RemoveArenaTagsAttr, [ ArenaTagType.QUICK_GUARD, ArenaTagType.WIDE_GUARD, ArenaTagType.MAT_BLOCK, ArenaTagType.CRAFTY_SHIELD ], false) + .attr(RemoveBattlerTagAttr, [BattlerTagType.PROTECTED]) + .attr( + RemoveArenaTagsAttr, + [ArenaTagType.QUICK_GUARD, ArenaTagType.WIDE_GUARD, ArenaTagType.MAT_BLOCK, ArenaTagType.CRAFTY_SHIELD], + false, + ) .makesContact(false) .ignoresProtect(), - new AttackMove(MoveId.PLUCK, PokemonType.FLYING, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 4) + new AttackMove(MoveId.PLUCK, PokemonType.FLYING, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 4) // .attr(StealEatBerryAttr), new StatusMove(MoveId.TAILWIND, PokemonType.FLYING, -1, 15, -1, 0, 4) .windMove() @@ -9755,16 +10375,21 @@ export function initMoves() { .condition(counterAttackConditionBoth, 3) .makesContact(false) .target(MoveTarget.ATTACKER), - new AttackMove(MoveId.U_TURN, PokemonType.BUG, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 4) + new AttackMove(MoveId.U_TURN, PokemonType.BUG, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 4) // .attr(ForceSwitchOutAttr, true), - new AttackMove(MoveId.CLOSE_COMBAT, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 4) - .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], -1, true), + new AttackMove(MoveId.CLOSE_COMBAT, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 4) // + .attr(StatStageChangeAttr, [Stat.DEF, Stat.SPDEF], -1, true), new AttackMove(MoveId.PAYBACK, PokemonType.DARK, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 4) // Payback boosts power on item use - .attr(MovePowerMultiplierAttr, (_user, target) => target.turnData.acted || globalScene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.BALL ? 2 : 1), - new AttackMove(MoveId.ASSURANCE, PokemonType.DARK, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 4) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.turnData.damageTaken > 0 ? 2 : 1), - new StatusMove(MoveId.EMBARGO, PokemonType.DARK, 100, 15, -1, 0, 4) + .attr(MovePowerMultiplierAttr, (_user, target) => + target.turnData.acted + || globalScene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.BALL + ? 2 + : 1, + ), + new AttackMove(MoveId.ASSURANCE, PokemonType.DARK, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 4) // + .attr(MovePowerMultiplierAttr, (_user, target, _move) => (target.turnData.damageTaken > 0 ? 2 : 1)), + new StatusMove(MoveId.EMBARGO, PokemonType.DARK, 100, 15, -1, 0, 4) // .reflectable() .unimplemented(), new AttackMove(MoveId.FLING, PokemonType.DARK, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 4) @@ -9784,9 +10409,9 @@ export function initMoves() { new AttackMove(MoveId.WRING_OUT, PokemonType.NORMAL, MoveCategory.SPECIAL, -1, 100, 5, -1, 0, 4) .attr(OpponentHighHpPowerAttr, 120) .makesContact(), - new SelfStatusMove(MoveId.POWER_TRICK, PokemonType.PSYCHIC, -1, 10, -1, 0, 4) + new SelfStatusMove(MoveId.POWER_TRICK, PokemonType.PSYCHIC, -1, 10, -1, 0, 4) // .attr(AddBattlerTagAttr, BattlerTagType.POWER_TRICK, true), - new StatusMove(MoveId.GASTRO_ACID, PokemonType.POISON, 100, 10, -1, 0, 4) + new StatusMove(MoveId.GASTRO_ACID, PokemonType.POISON, 100, 10, -1, 0, 4) // .attr(SuppressAbilitiesAttr) .reflectable(), new StatusMove(MoveId.LUCKY_CHANT, PokemonType.NORMAL, -1, 30, -1, 0, 4) @@ -9796,13 +10421,13 @@ export function initMoves() { .ignoresSubstitute() .target(MoveTarget.NEAR_ENEMY) .unimplemented(), - new SelfStatusMove(MoveId.COPYCAT, PokemonType.NORMAL, -1, 20, -1, 0, 4) + new SelfStatusMove(MoveId.COPYCAT, PokemonType.NORMAL, -1, 20, -1, 0, 4) // .attr(CopyMoveAttr, false, invalidCopycatMoves), new StatusMove(MoveId.POWER_SWAP, PokemonType.PSYCHIC, -1, 10, 100, 0, 4) - .attr(SwapStatStagesAttr, [ Stat.ATK, Stat.SPATK ]) + .attr(SwapStatStagesAttr, [Stat.ATK, Stat.SPATK]) .ignoresSubstitute(), new StatusMove(MoveId.GUARD_SWAP, PokemonType.PSYCHIC, -1, 10, 100, 0, 4) - .attr(SwapStatStagesAttr, [ Stat.DEF, Stat.SPDEF ]) + .attr(SwapStatStagesAttr, [Stat.DEF, Stat.SPDEF]) .ignoresSubstitute(), new AttackMove(MoveId.PUNISHMENT, PokemonType.DARK, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4) .makesContact(true) @@ -9815,7 +10440,7 @@ export function initMoves() { // TODO: Enable / remove once balance reaches a consensus on ability overrides during boss fights // .condition(failAgainstFinalBossCondition, 3) .reflectable(), - new AttackMove(MoveId.SUCKER_PUNCH, PokemonType.DARK, MoveCategory.PHYSICAL, 70, 100, 5, -1, 1, 4) + new AttackMove(MoveId.SUCKER_PUNCH, PokemonType.DARK, MoveCategory.PHYSICAL, 70, 100, 5, -1, 1, 4) // .condition(failIfTargetNotAttackingCondition, 3), new StatusMove(MoveId.TOXIC_SPIKES, PokemonType.POISON, -1, 20, -1, 0, 4) .attr(AddArenaTrapTagAttr, ArenaTagType.TOXIC_SPIKES) @@ -9824,25 +10449,31 @@ export function initMoves() { new StatusMove(MoveId.HEART_SWAP, PokemonType.PSYCHIC, -1, 10, -1, 0, 4) .attr(SwapStatStagesAttr, BATTLE_STATS) .ignoresSubstitute(), - new SelfStatusMove(MoveId.AQUA_RING, PokemonType.WATER, -1, 20, -1, 0, 4) + new SelfStatusMove(MoveId.AQUA_RING, PokemonType.WATER, -1, 20, -1, 0, 4) // .attr(AddBattlerTagAttr, BattlerTagType.AQUA_RING, true, true), new SelfStatusMove(MoveId.MAGNET_RISE, PokemonType.ELECTRIC, -1, 10, -1, 0, 4) .attr(AddBattlerTagAttr, BattlerTagType.FLOATING, true, true, 5) - .condition(user => [ BattlerTagType.FLOATING, BattlerTagType.IGNORE_FLYING, BattlerTagType.INGRAIN ].every((tag) => !user.getTag(tag)), 3) + .condition( + user => + [BattlerTagType.FLOATING, BattlerTagType.IGNORE_FLYING, BattlerTagType.INGRAIN].every( + tag => !user.getTag(tag), + ), + 3, + ) .affectedByGravity(), new AttackMove(MoveId.FLARE_BLITZ, PokemonType.FIRE, MoveCategory.PHYSICAL, 120, 100, 15, 10, 0, 4) .attr(RecoilAttr, false, 0.33) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) .attr(StatusEffectAttr, StatusEffect.BURN) .recklessMove(), - new AttackMove(MoveId.FORCE_PALM, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, 30, 0, 4) + new AttackMove(MoveId.FORCE_PALM, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, 30, 0, 4) // .attr(StatusEffectAttr, StatusEffect.PARALYSIS), new AttackMove(MoveId.AURA_SPHERE, PokemonType.FIGHTING, MoveCategory.SPECIAL, 80, -1, 20, -1, 0, 4) .pulseMove() .ballBombMove(), - new SelfStatusMove(MoveId.ROCK_POLISH, PokemonType.ROCK, -1, 20, -1, 0, 4) - .attr(StatStageChangeAttr, [ Stat.SPD ], 2, true), - new AttackMove(MoveId.POISON_JAB, PokemonType.POISON, MoveCategory.PHYSICAL, 80, 100, 20, 30, 0, 4) + new SelfStatusMove(MoveId.ROCK_POLISH, PokemonType.ROCK, -1, 20, -1, 0, 4) // + .attr(StatStageChangeAttr, [Stat.SPD], 2, true), + new AttackMove(MoveId.POISON_JAB, PokemonType.POISON, MoveCategory.PHYSICAL, 80, 100, 20, 30, 0, 4) // .attr(StatusEffectAttr, StatusEffect.POISON), new AttackMove(MoveId.DARK_PULSE, PokemonType.DARK, MoveCategory.SPECIAL, 80, 100, 15, 20, 0, 4) .attr(FlinchAttr) @@ -9857,12 +10488,12 @@ export function initMoves() { new AttackMove(MoveId.AIR_SLASH, PokemonType.FLYING, MoveCategory.SPECIAL, 75, 95, 15, 30, 0, 4) .attr(FlinchAttr) .slicingMove(), - new AttackMove(MoveId.X_SCISSOR, PokemonType.BUG, MoveCategory.PHYSICAL, 80, 100, 15, -1, 0, 4) + new AttackMove(MoveId.X_SCISSOR, PokemonType.BUG, MoveCategory.PHYSICAL, 80, 100, 15, -1, 0, 4) // .slicingMove(), new AttackMove(MoveId.BUG_BUZZ, PokemonType.BUG, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 4) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) + .attr(StatStageChangeAttr, [Stat.SPDEF], -1) .soundBased(), - new AttackMove(MoveId.DRAGON_PULSE, PokemonType.DRAGON, MoveCategory.SPECIAL, 85, 100, 10, -1, 0, 4) + new AttackMove(MoveId.DRAGON_PULSE, PokemonType.DRAGON, MoveCategory.SPECIAL, 85, 100, 10, -1, 0, 4) // .pulseMove(), new AttackMove(MoveId.DRAGON_RUSH, PokemonType.DRAGON, MoveCategory.PHYSICAL, 100, 75, 10, 20, 0, 4) .attr(AlwaysHitMinimizeAttr) @@ -9875,29 +10506,29 @@ export function initMoves() { .triageMove(), new AttackMove(MoveId.VACUUM_WAVE, PokemonType.FIGHTING, MoveCategory.SPECIAL, 40, 100, 30, -1, 1, 4), new AttackMove(MoveId.FOCUS_BLAST, PokemonType.FIGHTING, MoveCategory.SPECIAL, 120, 70, 5, 10, 0, 4) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) + .attr(StatStageChangeAttr, [Stat.SPDEF], -1) .ballBombMove(), new AttackMove(MoveId.ENERGY_BALL, PokemonType.GRASS, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 4) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) + .attr(StatStageChangeAttr, [Stat.SPDEF], -1) .ballBombMove(), new AttackMove(MoveId.BRAVE_BIRD, PokemonType.FLYING, MoveCategory.PHYSICAL, 120, 100, 15, -1, 0, 4) .attr(RecoilAttr, false, 0.33) .recklessMove(), - new AttackMove(MoveId.EARTH_POWER, PokemonType.GROUND, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 4) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1), - new StatusMove(MoveId.SWITCHEROO, PokemonType.DARK, 100, 10, -1, 0, 4) + new AttackMove(MoveId.EARTH_POWER, PokemonType.GROUND, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 4) // + .attr(StatStageChangeAttr, [Stat.SPDEF], -1), + new StatusMove(MoveId.SWITCHEROO, PokemonType.DARK, 100, 10, -1, 0, 4) // .unimplemented(), - new AttackMove(MoveId.GIGA_IMPACT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 150, 90, 5, -1, 0, 4) + new AttackMove(MoveId.GIGA_IMPACT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 150, 90, 5, -1, 0, 4) // .attr(RechargeAttr), - new SelfStatusMove(MoveId.NASTY_PLOT, PokemonType.DARK, -1, 20, -1, 0, 4) - .attr(StatStageChangeAttr, [ Stat.SPATK ], 2, true), - new AttackMove(MoveId.BULLET_PUNCH, PokemonType.STEEL, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 4) + new SelfStatusMove(MoveId.NASTY_PLOT, PokemonType.DARK, -1, 20, -1, 0, 4) // + .attr(StatStageChangeAttr, [Stat.SPATK], 2, true), + new AttackMove(MoveId.BULLET_PUNCH, PokemonType.STEEL, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 4) // .punchingMove(), - new AttackMove(MoveId.AVALANCHE, PokemonType.ICE, MoveCategory.PHYSICAL, 60, 100, 10, -1, -4, 4) + new AttackMove(MoveId.AVALANCHE, PokemonType.ICE, MoveCategory.PHYSICAL, 60, 100, 10, -1, -4, 4) // .attr(TurnDamagedDoublePowerAttr), - new AttackMove(MoveId.ICE_SHARD, PokemonType.ICE, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 4) + new AttackMove(MoveId.ICE_SHARD, PokemonType.ICE, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 4) // .makesContact(false), - new AttackMove(MoveId.SHADOW_CLAW, PokemonType.GHOST, MoveCategory.PHYSICAL, 70, 100, 15, -1, 0, 4) + new AttackMove(MoveId.SHADOW_CLAW, PokemonType.GHOST, MoveCategory.PHYSICAL, 70, 100, 15, -1, 0, 4) // .attr(HighCritAttr), new AttackMove(MoveId.THUNDER_FANG, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 65, 95, 15, 10, 0, 4) .attr(FlinchAttr) @@ -9913,42 +10544,42 @@ export function initMoves() { .bitingMove(), new AttackMove(MoveId.SHADOW_SNEAK, PokemonType.GHOST, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 4), new AttackMove(MoveId.MUD_BOMB, PokemonType.GROUND, MoveCategory.SPECIAL, 65, 85, 10, 30, 0, 4) - .attr(StatStageChangeAttr, [ Stat.ACC ], -1) + .attr(StatStageChangeAttr, [Stat.ACC], -1) .ballBombMove(), new AttackMove(MoveId.PSYCHO_CUT, PokemonType.PSYCHIC, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 4) .attr(HighCritAttr) .slicingMove() .makesContact(false), - new AttackMove(MoveId.ZEN_HEADBUTT, PokemonType.PSYCHIC, MoveCategory.PHYSICAL, 80, 90, 15, 20, 0, 4) + new AttackMove(MoveId.ZEN_HEADBUTT, PokemonType.PSYCHIC, MoveCategory.PHYSICAL, 80, 90, 15, 20, 0, 4) // .attr(FlinchAttr), - new AttackMove(MoveId.MIRROR_SHOT, PokemonType.STEEL, MoveCategory.SPECIAL, 65, 85, 10, 30, 0, 4) - .attr(StatStageChangeAttr, [ Stat.ACC ], -1), - new AttackMove(MoveId.FLASH_CANNON, PokemonType.STEEL, MoveCategory.SPECIAL, 80, 100, 10, 10, 0, 4) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1), - new AttackMove(MoveId.ROCK_CLIMB, PokemonType.NORMAL, MoveCategory.PHYSICAL, 90, 85, 20, 20, 0, 4) + new AttackMove(MoveId.MIRROR_SHOT, PokemonType.STEEL, MoveCategory.SPECIAL, 65, 85, 10, 30, 0, 4) // + .attr(StatStageChangeAttr, [Stat.ACC], -1), + new AttackMove(MoveId.FLASH_CANNON, PokemonType.STEEL, MoveCategory.SPECIAL, 80, 100, 10, 10, 0, 4) // + .attr(StatStageChangeAttr, [Stat.SPDEF], -1), + new AttackMove(MoveId.ROCK_CLIMB, PokemonType.NORMAL, MoveCategory.PHYSICAL, 90, 85, 20, 20, 0, 4) // .attr(ConfuseAttr), new StatusMove(MoveId.DEFOG, PokemonType.FLYING, -1, 15, -1, 0, 4) - .attr(StatStageChangeAttr, [ Stat.EVA ], -1) + .attr(StatStageChangeAttr, [Stat.EVA], -1) .attr(ClearWeatherAttr, WeatherType.FOG) .attr(ClearTerrainAttr) .attr(RemoveScreensAttr, false) .attr(RemoveArenaTrapAttr, true) - .attr(RemoveArenaTagsAttr, [ ArenaTagType.MIST, ArenaTagType.SAFEGUARD ], false) + .attr(RemoveArenaTagsAttr, [ArenaTagType.MIST, ArenaTagType.SAFEGUARD], false) .reflectable(), new StatusMove(MoveId.TRICK_ROOM, PokemonType.PSYCHIC, -1, 5, -1, -7, 4) .attr(AddArenaTagAttr, ArenaTagType.TRICK_ROOM, 5) .ignoresProtect() .target(MoveTarget.BOTH_SIDES), - new AttackMove(MoveId.DRACO_METEOR, PokemonType.DRAGON, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 4) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true), + new AttackMove(MoveId.DRACO_METEOR, PokemonType.DRAGON, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 4) // + .attr(StatStageChangeAttr, [Stat.SPATK], -2, true), new AttackMove(MoveId.DISCHARGE, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 80, 100, 15, 30, 0, 4) .attr(StatusEffectAttr, StatusEffect.PARALYSIS) .target(MoveTarget.ALL_NEAR_OTHERS), new AttackMove(MoveId.LAVA_PLUME, PokemonType.FIRE, MoveCategory.SPECIAL, 80, 100, 15, 30, 0, 4) .attr(StatusEffectAttr, StatusEffect.BURN) .target(MoveTarget.ALL_NEAR_OTHERS), - new AttackMove(MoveId.LEAF_STORM, PokemonType.GRASS, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 4) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true), + new AttackMove(MoveId.LEAF_STORM, PokemonType.GRASS, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 4) // + .attr(StatStageChangeAttr, [Stat.SPATK], -2, true), new AttackMove(MoveId.POWER_WHIP, PokemonType.GRASS, MoveCategory.PHYSICAL, 120, 85, 10, -1, 0, 4), new AttackMove(MoveId.ROCK_WRECKER, PokemonType.ROCK, MoveCategory.PHYSICAL, 150, 90, 5, -1, 0, 4) .attr(RechargeAttr) @@ -9961,7 +10592,7 @@ export function initMoves() { new AttackMove(MoveId.GUNK_SHOT, PokemonType.POISON, MoveCategory.PHYSICAL, 120, 80, 5, 30, 0, 4) .attr(StatusEffectAttr, StatusEffect.POISON) .makesContact(false), - new AttackMove(MoveId.IRON_HEAD, PokemonType.STEEL, MoveCategory.PHYSICAL, 80, 100, 15, 30, 0, 4) + new AttackMove(MoveId.IRON_HEAD, PokemonType.STEEL, MoveCategory.PHYSICAL, 80, 100, 15, 30, 0, 4) // .attr(FlinchAttr), new AttackMove(MoveId.MAGNET_BOMB, PokemonType.STEEL, MoveCategory.PHYSICAL, 60, -1, 20, -1, 0, 4) .makesContact(false) @@ -9970,8 +10601,8 @@ export function initMoves() { .attr(HighCritAttr) .makesContact(false), new StatusMove(MoveId.CAPTIVATE, PokemonType.NORMAL, 100, 20, -1, 0, 4) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -2) - .condition((user, target, move) => target.isOppositeGender(user)) + .attr(StatStageChangeAttr, [Stat.SPATK], -2) + .condition((user, target, _move) => target.isOppositeGender(user)) .target(MoveTarget.ALL_NEAR_ENEMIES) .reflectable(), new StatusMove(MoveId.STEALTH_ROCK, PokemonType.ROCK, -1, 20, -1, 0, 4) @@ -9984,12 +10615,12 @@ export function initMoves() { new AttackMove(MoveId.CHATTER, PokemonType.FLYING, MoveCategory.SPECIAL, 65, 100, 20, 100, 0, 4) .attr(ConfuseAttr) .soundBased(), - new AttackMove(MoveId.JUDGMENT, PokemonType.NORMAL, MoveCategory.SPECIAL, 100, 100, 10, -1, 0, 4) + new AttackMove(MoveId.JUDGMENT, PokemonType.NORMAL, MoveCategory.SPECIAL, 100, 100, 10, -1, 0, 4) // .attr(FormChangeItemTypeAttr), - new AttackMove(MoveId.BUG_BITE, PokemonType.BUG, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 4) + new AttackMove(MoveId.BUG_BITE, PokemonType.BUG, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 4) // .attr(StealEatBerryAttr), - new AttackMove(MoveId.CHARGE_BEAM, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 50, 90, 10, 70, 0, 4) - .attr(StatStageChangeAttr, [ Stat.SPATK ], 1, true), + new AttackMove(MoveId.CHARGE_BEAM, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 50, 90, 10, 70, 0, 4) // + .attr(StatStageChangeAttr, [Stat.SPATK], 1, true), new AttackMove(MoveId.WOOD_HAMMER, PokemonType.GRASS, MoveCategory.PHYSICAL, 120, 100, 15, -1, 0, 4) .attr(RecoilAttr, false, 0.33) .recklessMove(), @@ -9997,44 +10628,44 @@ export function initMoves() { new AttackMove(MoveId.ATTACK_ORDER, PokemonType.BUG, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 4) .attr(HighCritAttr) .makesContact(false), - new SelfStatusMove(MoveId.DEFEND_ORDER, PokemonType.BUG, -1, 10, -1, 0, 4) - .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], 1, true), - new SelfStatusMove(MoveId.HEAL_ORDER, PokemonType.BUG, -1, 5, -1, 0, 4) + new SelfStatusMove(MoveId.DEFEND_ORDER, PokemonType.BUG, -1, 10, -1, 0, 4) // + .attr(StatStageChangeAttr, [Stat.DEF, Stat.SPDEF], 1, true), + new SelfStatusMove(MoveId.HEAL_ORDER, PokemonType.BUG, -1, 5, -1, 0, 4) // .attr(HealAttr, 0.5) .triageMove(), new AttackMove(MoveId.HEAD_SMASH, PokemonType.ROCK, MoveCategory.PHYSICAL, 150, 80, 5, -1, 0, 4) .attr(RecoilAttr, false, 0.5) .recklessMove(), - new AttackMove(MoveId.DOUBLE_HIT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 35, 90, 10, -1, 0, 4) + new AttackMove(MoveId.DOUBLE_HIT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 35, 90, 10, -1, 0, 4) // .attr(MultiHitAttr, MultiHitType._2), - new AttackMove(MoveId.ROAR_OF_TIME, PokemonType.DRAGON, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 4) + new AttackMove(MoveId.ROAR_OF_TIME, PokemonType.DRAGON, MoveCategory.SPECIAL, 150, 90, 5, -1, 0, 4) // .attr(RechargeAttr), - new AttackMove(MoveId.SPACIAL_REND, PokemonType.DRAGON, MoveCategory.SPECIAL, 100, 95, 5, -1, 0, 4) + new AttackMove(MoveId.SPACIAL_REND, PokemonType.DRAGON, MoveCategory.SPECIAL, 100, 95, 5, -1, 0, 4) // .attr(HighCritAttr), new SelfStatusMove(MoveId.LUNAR_DANCE, PokemonType.PSYCHIC, -1, 10, -1, 0, 4) .attr(SacrificialFullRestoreAttr, true, "moveTriggers:lunarDanceRestore") .danceMove() .triageMove() .condition(failIfLastInPartyCondition), - new AttackMove(MoveId.CRUSH_GRIP, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4) + new AttackMove(MoveId.CRUSH_GRIP, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, 0, 4) // .attr(OpponentHighHpPowerAttr, 120), - new AttackMove(MoveId.MAGMA_STORM, PokemonType.FIRE, MoveCategory.SPECIAL, 100, 75, 5, -1, 0, 4) + new AttackMove(MoveId.MAGMA_STORM, PokemonType.FIRE, MoveCategory.SPECIAL, 100, 75, 5, -1, 0, 4) // .attr(TrapAttr, BattlerTagType.MAGMA_STORM), - new StatusMove(MoveId.DARK_VOID, PokemonType.DARK, 80, 10, -1, 0, 4) //Accuracy from Generations 4-6 + new StatusMove(MoveId.DARK_VOID, PokemonType.DARK, 80, 10, -1, 0, 4) // Accuracy from Generations 4-6 .attr(StatusEffectAttr, StatusEffect.SLEEP) .target(MoveTarget.ALL_NEAR_ENEMIES) .reflectable(), - new AttackMove(MoveId.SEED_FLARE, PokemonType.GRASS, MoveCategory.SPECIAL, 120, 85, 5, 40, 0, 4) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -2), + new AttackMove(MoveId.SEED_FLARE, PokemonType.GRASS, MoveCategory.SPECIAL, 120, 85, 5, 40, 0, 4) // + .attr(StatStageChangeAttr, [Stat.SPDEF], -2), new AttackMove(MoveId.OMINOUS_WIND, PokemonType.GHOST, MoveCategory.SPECIAL, 60, 100, 5, 10, 0, 4) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], 1, true) .windMove(), new ChargingAttackMove(MoveId.SHADOW_FORCE, PokemonType.GHOST, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 4) .chargeText(i18next.t("moveTriggers:vanishedInstantly", { pokemonName: "{USER}" })) .chargeAttr(SemiInvulnerableAttr, BattlerTagType.HIDDEN) .ignoresProtect(), - new SelfStatusMove(MoveId.HONE_CLAWS, PokemonType.DARK, -1, 15, -1, 0, 5) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.ACC ], 1, true), + new SelfStatusMove(MoveId.HONE_CLAWS, PokemonType.DARK, -1, 15, -1, 0, 5) // + .attr(StatStageChangeAttr, [Stat.ATK, Stat.ACC], 1, true), new StatusMove(MoveId.WIDE_GUARD, PokemonType.ROCK, -1, 10, -1, 3, 5) .target(MoveTarget.USER_SIDE) .attr(AddArenaTagAttr, ArenaTagType.WIDE_GUARD, 1, true, true) @@ -10042,30 +10673,51 @@ export function initMoves() { new StatusMove(MoveId.GUARD_SPLIT, PokemonType.PSYCHIC, -1, 10, -1, 0, 5) // TODO: Enable / remove once balance reaches a consensus on imprison interaction during the final boss fight // .condition(failAgainstFinalBossCondition, 2) - .attr(AverageStatsAttr, [ Stat.DEF, Stat.SPDEF ], "moveTriggers:sharedGuard"), - new StatusMove(MoveId.POWER_SPLIT, PokemonType.PSYCHIC, -1, 10, -1, 0, 5) - .attr(AverageStatsAttr, [ Stat.ATK, Stat.SPATK ], "moveTriggers:sharedPower"), - // TODO: Enable / remove once balance reaches a consensus on imprison interaction during the final boss fight - // .condition(failAgainstFinalBossCondition, 2) + .attr(AverageStatsAttr, [Stat.DEF, Stat.SPDEF], "moveTriggers:sharedGuard"), + new StatusMove(MoveId.POWER_SPLIT, PokemonType.PSYCHIC, -1, 10, -1, 0, 5) // + .attr(AverageStatsAttr, [Stat.ATK, Stat.SPATK], "moveTriggers:sharedPower"), + // TODO: Enable / remove once balance reaches a consensus on imprison interaction during the final boss fight + // .condition(failAgainstFinalBossCondition, 2) new StatusMove(MoveId.WONDER_ROOM, PokemonType.PSYCHIC, -1, 10, -1, 0, 5) .ignoresProtect() .target(MoveTarget.BOTH_SIDES) .unimplemented(), - new AttackMove(MoveId.PSYSHOCK, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5) + new AttackMove(MoveId.PSYSHOCK, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5) // .attr(DefDefAttr), - new AttackMove(MoveId.VENOSHOCK, PokemonType.POISON, MoveCategory.SPECIAL, 65, 100, 10, -1, 0, 5) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.status && (target.status.effect === StatusEffect.POISON || target.status.effect === StatusEffect.TOXIC) ? 2 : 1), + new AttackMove(MoveId.VENOSHOCK, PokemonType.POISON, MoveCategory.SPECIAL, 65, 100, 10, -1, 0, 5) // + .attr(MovePowerMultiplierAttr, (_user, target, _move) => + target.status && (target.status.effect === StatusEffect.POISON || target.status.effect === StatusEffect.TOXIC) + ? 2 + : 1, + ), new SelfStatusMove(MoveId.AUTOTOMIZE, PokemonType.STEEL, -1, 15, -1, 0, 5) - .attr(StatStageChangeAttr, [ Stat.SPD ], 2, true) + .attr(StatStageChangeAttr, [Stat.SPD], 2, true) .attr(AddBattlerTagAttr, BattlerTagType.AUTOTOMIZED, true), new SelfStatusMove(MoveId.RAGE_POWDER, PokemonType.BUG, -1, 20, -1, 2, 5) .powderMove() .attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, true), new StatusMove(MoveId.TELEKINESIS, PokemonType.PSYCHIC, -1, 15, -1, 0, 5) .affectedByGravity() - .condition((_user, target, _move) => ![ SpeciesId.DIGLETT, SpeciesId.DUGTRIO, SpeciesId.ALOLA_DIGLETT, SpeciesId.ALOLA_DUGTRIO, SpeciesId.SANDYGAST, SpeciesId.PALOSSAND, SpeciesId.WIGLETT, SpeciesId.WUGTRIO ].includes(target.species.speciesId)) - .condition((_user, target, _move) => !(target.species.speciesId === SpeciesId.GENGAR && target.getFormKey() === "mega")) - .condition((_user, target, _move) => target.getTag(BattlerTagType.INGRAIN) == null && target.getTag(BattlerTagType.IGNORE_FLYING) == null) + .condition( + (_user, target, _move) => + ![ + SpeciesId.DIGLETT, + SpeciesId.DUGTRIO, + SpeciesId.ALOLA_DIGLETT, + SpeciesId.ALOLA_DUGTRIO, + SpeciesId.SANDYGAST, + SpeciesId.PALOSSAND, + SpeciesId.WIGLETT, + SpeciesId.WUGTRIO, + ].includes(target.species.speciesId), + ) + .condition( + (_user, target, _move) => !(target.species.speciesId === SpeciesId.GENGAR && target.getFormKey() === "mega"), + ) + .condition( + (_user, target, _move) => + target.getTag(BattlerTagType.INGRAIN) == null && target.getTag(BattlerTagType.IGNORE_FLYING) == null, + ) .attr(AddBattlerTagAttr, BattlerTagType.TELEKINESIS, false, true, 3) .attr(AddBattlerTagAttr, BattlerTagType.FLOATING, false, true, 3) .reflectable(), @@ -10076,18 +10728,18 @@ export function initMoves() { new AttackMove(MoveId.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 ]) + .attr(RemoveBattlerTagAttr, [BattlerTagType.FLYING, BattlerTagType.FLOATING, BattlerTagType.TELEKINESIS]) .attr(HitsTagAttr, BattlerTagType.FLYING) .makesContact(false), - new AttackMove(MoveId.STORM_THROW, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 5) + new AttackMove(MoveId.STORM_THROW, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 5) // .attr(CritOnlyAttr), - new AttackMove(MoveId.FLAME_BURST, PokemonType.FIRE, MoveCategory.SPECIAL, 70, 100, 15, -1, 0, 5) + new AttackMove(MoveId.FLAME_BURST, PokemonType.FIRE, MoveCategory.SPECIAL, 70, 100, 15, -1, 0, 5) // .attr(FlameBurstAttr), new AttackMove(MoveId.SLUDGE_WAVE, PokemonType.POISON, MoveCategory.SPECIAL, 95, 100, 10, 10, 0, 5) .attr(StatusEffectAttr, StatusEffect.POISON) .target(MoveTarget.ALL_NEAR_OTHERS), new SelfStatusMove(MoveId.QUIVER_DANCE, PokemonType.BUG, -1, 20, -1, 0, 5) - .attr(StatStageChangeAttr, [ Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true) + .attr(StatStageChangeAttr, [Stat.SPATK, Stat.SPDEF, Stat.SPD], 1, true) .danceMove(), new AttackMove(MoveId.HEAVY_SLAM, PokemonType.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 5) .attr(AlwaysHitMinimizeAttr) @@ -10103,16 +10755,16 @@ export function initMoves() { new StatusMove(MoveId.SOAK, PokemonType.WATER, 100, 20, -1, 0, 5) .attr(ChangeTypeAttr, PokemonType.WATER) .reflectable(), - new AttackMove(MoveId.FLAME_CHARGE, PokemonType.FIRE, MoveCategory.PHYSICAL, 50, 100, 20, 100, 0, 5) - .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true), - new SelfStatusMove(MoveId.COIL, PokemonType.POISON, -1, 20, -1, 0, 5) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.ACC ], 1, true), - new AttackMove(MoveId.LOW_SWEEP, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 20, 100, 0, 5) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1), + new AttackMove(MoveId.FLAME_CHARGE, PokemonType.FIRE, MoveCategory.PHYSICAL, 50, 100, 20, 100, 0, 5) // + .attr(StatStageChangeAttr, [Stat.SPD], 1, true), + new SelfStatusMove(MoveId.COIL, PokemonType.POISON, -1, 20, -1, 0, 5) // + .attr(StatStageChangeAttr, [Stat.ATK, Stat.DEF, Stat.ACC], 1, true), + new AttackMove(MoveId.LOW_SWEEP, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 20, 100, 0, 5) // + .attr(StatStageChangeAttr, [Stat.SPD], -1), new AttackMove(MoveId.ACID_SPRAY, PokemonType.POISON, MoveCategory.SPECIAL, 40, 100, 20, 100, 0, 5) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -2) + .attr(StatStageChangeAttr, [Stat.SPDEF], -2) .ballBombMove(), - new AttackMove(MoveId.FOUL_PLAY, PokemonType.DARK, MoveCategory.PHYSICAL, 95, 100, 15, -1, 0, 5) + new AttackMove(MoveId.FOUL_PLAY, PokemonType.DARK, MoveCategory.PHYSICAL, 95, 100, 15, -1, 0, 5) // .attr(TargetAtkUserAtkAttr), new StatusMove(MoveId.SIMPLE_BEAM, PokemonType.NORMAL, 100, 15, -1, 0, 5) .attr(AbilityChangeAttr, AbilityId.SIMPLE) @@ -10129,7 +10781,7 @@ export function initMoves() { .ignoresSubstitute() .target(MoveTarget.NEAR_OTHER) .condition(failIfSingleBattle) - .condition((user, target, move) => !target.turnData.acted) + .condition((_user, target, _move) => !target.turnData.acted) .attr(AfterYouAttr), new AttackMove(MoveId.ROUND, PokemonType.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5) .attr(CueNextRoundAttr) @@ -10138,17 +10790,17 @@ export function initMoves() { new AttackMove(MoveId.ECHOED_VOICE, PokemonType.NORMAL, MoveCategory.SPECIAL, 40, 100, 15, -1, 0, 5) .attr(ConsecutiveUseMultiBasePowerAttr, 5, false) .soundBased(), - new AttackMove(MoveId.CHIP_AWAY, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 5) + new AttackMove(MoveId.CHIP_AWAY, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, -1, 0, 5) // .attr(IgnoreOpponentStatStagesAttr), - new AttackMove(MoveId.CLEAR_SMOG, PokemonType.POISON, MoveCategory.SPECIAL, 50, -1, 15, -1, 0, 5) + new AttackMove(MoveId.CLEAR_SMOG, PokemonType.POISON, MoveCategory.SPECIAL, 50, -1, 15, -1, 0, 5) // .attr(ResetStatsAttr, false), - new AttackMove(MoveId.STORED_POWER, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 20, 100, 10, -1, 0, 5) + new AttackMove(MoveId.STORED_POWER, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 20, 100, 10, -1, 0, 5) // .attr(PositiveStatStagePowerAttr), new StatusMove(MoveId.QUICK_GUARD, PokemonType.FIGHTING, -1, 15, -1, 3, 5) .target(MoveTarget.USER_SIDE) .attr(AddArenaTagAttr, ArenaTagType.QUICK_GUARD, 1, true, true) .condition(failIfLastCondition, 3), - new SelfStatusMove(MoveId.ALLY_SWITCH, PokemonType.PSYCHIC, -1, 15, -1, 2, 5) + new SelfStatusMove(MoveId.ALLY_SWITCH, PokemonType.PSYCHIC, -1, 15, -1, 2, 5) // .ignoresProtect() .unimplemented(), new AttackMove(MoveId.SCALD, PokemonType.WATER, MoveCategory.SPECIAL, 80, 100, 15, 30, 0, 5) @@ -10156,22 +10808,22 @@ export function initMoves() { .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) .attr(StatusEffectAttr, StatusEffect.BURN), new SelfStatusMove(MoveId.SHELL_SMASH, PokemonType.NORMAL, -1, 15, -1, 0, 5) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK, Stat.SPD ], 2, true) - .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], -1, true), + .attr(StatStageChangeAttr, [Stat.ATK, Stat.SPATK, Stat.SPD], 2, true) + .attr(StatStageChangeAttr, [Stat.DEF, Stat.SPDEF], -1, true), new StatusMove(MoveId.HEAL_PULSE, PokemonType.PSYCHIC, -1, 10, -1, 0, 5) .attr(HealAttr, 0.5, false, false) .pulseMove() .triageMove() .reflectable(), - new AttackMove(MoveId.HEX, PokemonType.GHOST, MoveCategory.SPECIAL, 65, 100, 10, -1, 0, 5) - .attr( - MovePowerMultiplierAttr, - (user, target, move) => target.status || target.hasAbility(AbilityId.COMATOSE) ? 2 : 1), + new AttackMove(MoveId.HEX, PokemonType.GHOST, MoveCategory.SPECIAL, 65, 100, 10, -1, 0, 5) // + .attr(MovePowerMultiplierAttr, (_user, target, _move) => + target.status || target.hasAbility(AbilityId.COMATOSE) ? 2 : 1, + ), new ChargingAttackMove(MoveId.SKY_DROP, PokemonType.FLYING, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 5) .chargeText(i18next.t("moveTriggers:tookTargetIntoSky", { pokemonName: "{USER}", targetName: "{TARGET}" })) .chargeAttr(SemiInvulnerableAttr, BattlerTagType.FLYING) .affectedByGravity() - .condition((user, target, move) => !target.getTag(BattlerTagType.SUBSTITUTE)) + .condition((_user, target, _move) => !target.getTag(BattlerTagType.SUBSTITUTE)) /* * Cf https://bulbapedia.bulbagarden.net/wiki/Sky_Drop_(move) and https://www.smogon.com/dex/sv/moves/sky-drop/: * Should immobilize and give target semi-invulnerability @@ -10181,34 +10833,40 @@ export function initMoves() { */ .partial(), new SelfStatusMove(MoveId.SHIFT_GEAR, PokemonType.STEEL, -1, 10, -1, 0, 5) - .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true) - .attr(StatStageChangeAttr, [ Stat.SPD ], 2, true), + .attr(StatStageChangeAttr, [Stat.ATK], 1, true) + .attr(StatStageChangeAttr, [Stat.SPD], 2, true), new AttackMove(MoveId.CIRCLE_THROW, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5) .attr(ForceSwitchOutAttr, false, SwitchType.FORCE_SWITCH) .hidesTarget(), new AttackMove(MoveId.INCINERATE, PokemonType.FIRE, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5) .target(MoveTarget.ALL_NEAR_ENEMIES) .attr(RemoveHeldItemAttr, true) - .edgeCase(), // Should be able to remove items from pokemon with Sticky Hold if the damage causes them to faint + .edgeCase(), new StatusMove(MoveId.QUASH, PokemonType.DARK, 100, 15, -1, 0, 5) .condition(failIfSingleBattle) - .condition((user, target, move) => !target.turnData.acted) + .condition((_user, target, _move) => !target.turnData.acted) .attr(ForceLastAttr), - new AttackMove(MoveId.ACROBATICS, PokemonType.FLYING, MoveCategory.PHYSICAL, 55, 100, 15, -1, 0, 5) - .attr(MovePowerMultiplierAttr, (user, target, move) => Math.max(1, 2 - 0.2 * user.getHeldItems().filter(i => i.isTransferable).reduce((v, m) => v + m.stackCount, 0))), - new StatusMove(MoveId.REFLECT_TYPE, PokemonType.NORMAL, -1, 15, -1, 0, 5) + new AttackMove(MoveId.ACROBATICS, PokemonType.FLYING, MoveCategory.PHYSICAL, 55, 100, 15, -1, 0, 5) // + .attr(MovePowerMultiplierAttr, (user, _target, _move) => { + const itemCount = user + .getHeldItems() + .filter(i => i.isTransferable) + .reduce((v, m) => v + m.stackCount, 0); + return Math.max(1, 2 - 0.2 * itemCount); + }), + new StatusMove(MoveId.REFLECT_TYPE, PokemonType.NORMAL, -1, 15, -1, 0, 5) // .ignoresSubstitute() .attr(CopyTypeAttr), - new AttackMove(MoveId.RETALIATE, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 5, -1, 0, 5) - .attr(MovePowerMultiplierAttr, (user, target, move) => { + new AttackMove(MoveId.RETALIATE, PokemonType.NORMAL, MoveCategory.PHYSICAL, 70, 100, 5, -1, 0, 5) // + .attr(MovePowerMultiplierAttr, (user, _target, _move) => { const turn = globalScene.currentBattle.turn; - const lastPlayerFaint = globalScene.currentBattle.playerFaintsHistory[globalScene.currentBattle.playerFaintsHistory.length - 1]; - const lastEnemyFaint = globalScene.currentBattle.enemyFaintsHistory[globalScene.currentBattle.enemyFaintsHistory.length - 1]; - return ( - (lastPlayerFaint !== undefined && turn - lastPlayerFaint.turn === 1 && user.isPlayer()) || - (lastEnemyFaint !== undefined && turn - lastEnemyFaint.turn === 1 && user.isEnemy()) - ) ? 2 : 1; + const lastPlayerFaint = globalScene.currentBattle.playerFaintsHistory.at(-1); + const lastEnemyFaint = globalScene.currentBattle.enemyFaintsHistory.at(-1); + return (lastPlayerFaint !== undefined && turn - lastPlayerFaint.turn === 1 && user.isPlayer()) + || (lastEnemyFaint !== undefined && turn - lastEnemyFaint.turn === 1 && user.isEnemy()) + ? 2 + : 1; }), new AttackMove(MoveId.FINAL_GAMBIT, PokemonType.FIGHTING, MoveCategory.SPECIAL, -1, 100, 5, -1, 0, 5) .attr(UserHpDamageAttr) @@ -10217,7 +10875,7 @@ export function initMoves() { .ignoresProtect() .ignoresSubstitute() .unimplemented(), - new AttackMove(MoveId.INFERNO, PokemonType.FIRE, MoveCategory.SPECIAL, 100, 50, 5, 100, 0, 5) + new AttackMove(MoveId.INFERNO, PokemonType.FIRE, MoveCategory.SPECIAL, 100, 50, 5, 100, 0, 5) // .attr(StatusEffectAttr, StatusEffect.BURN), new AttackMove(MoveId.WATER_PLEDGE, PokemonType.WATER, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5) .attr(AwaitCombinedPledgeAttr) @@ -10243,34 +10901,36 @@ export function initMoves() { .attr(AddPledgeEffectAttr, ArenaTagType.GRASS_WATER_PLEDGE, MoveId.WATER_PLEDGE) .attr(AddPledgeEffectAttr, ArenaTagType.FIRE_GRASS_PLEDGE, MoveId.FIRE_PLEDGE) .attr(BypassRedirectAttr, true), - new AttackMove(MoveId.VOLT_SWITCH, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 5) + new AttackMove(MoveId.VOLT_SWITCH, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 5) // .attr(ForceSwitchOutAttr, true), new AttackMove(MoveId.STRUGGLE_BUG, PokemonType.BUG, MoveCategory.SPECIAL, 50, 100, 20, 100, 0, 5) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -1) + .attr(StatStageChangeAttr, [Stat.SPATK], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(MoveId.BULLDOZE, PokemonType.GROUND, MoveCategory.PHYSICAL, 60, 100, 20, 100, 0, 5) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1) - .attr(MovePowerMultiplierAttr, (user, target, move) => globalScene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1) + .attr(StatStageChangeAttr, [Stat.SPD], -1) + .attr(MovePowerMultiplierAttr, (_user, target, _move) => + globalScene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() ? 0.5 : 1, + ) .makesContact(false) .target(MoveTarget.ALL_NEAR_OTHERS), - new AttackMove(MoveId.FROST_BREATH, PokemonType.ICE, MoveCategory.SPECIAL, 60, 90, 10, -1, 0, 5) + new AttackMove(MoveId.FROST_BREATH, PokemonType.ICE, MoveCategory.SPECIAL, 60, 90, 10, -1, 0, 5) // .attr(CritOnlyAttr), new AttackMove(MoveId.DRAGON_TAIL, PokemonType.DRAGON, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5) .attr(ForceSwitchOutAttr, false, SwitchType.FORCE_SWITCH) .hidesTarget(), - new SelfStatusMove(MoveId.WORK_UP, PokemonType.NORMAL, -1, 30, -1, 0, 5) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, true), + new SelfStatusMove(MoveId.WORK_UP, PokemonType.NORMAL, -1, 30, -1, 0, 5) // + .attr(StatStageChangeAttr, [Stat.ATK, Stat.SPATK], 1, true), new AttackMove(MoveId.ELECTROWEB, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 5) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1) + .attr(StatStageChangeAttr, [Stat.SPD], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(MoveId.WILD_CHARGE, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 5) .attr(RecoilAttr) .recklessMove(), - new AttackMove(MoveId.DRILL_RUN, PokemonType.GROUND, MoveCategory.PHYSICAL, 80, 95, 10, -1, 0, 5) + new AttackMove(MoveId.DRILL_RUN, PokemonType.GROUND, MoveCategory.PHYSICAL, 80, 95, 10, -1, 0, 5) // .attr(HighCritAttr), - new AttackMove(MoveId.DUAL_CHOP, PokemonType.DRAGON, MoveCategory.PHYSICAL, 40, 90, 15, -1, 0, 5) + new AttackMove(MoveId.DUAL_CHOP, PokemonType.DRAGON, MoveCategory.PHYSICAL, 40, 90, 15, -1, 0, 5) // .attr(MultiHitAttr, MultiHitType._2), - new AttackMove(MoveId.HEART_STAMP, PokemonType.PSYCHIC, MoveCategory.PHYSICAL, 60, 100, 25, 30, 0, 5) + new AttackMove(MoveId.HEART_STAMP, PokemonType.PSYCHIC, MoveCategory.PHYSICAL, 60, 100, 25, 30, 0, 5) // .attr(FlinchAttr), new AttackMove(MoveId.HORN_LEECH, PokemonType.GRASS, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 5) .attr(HitHealAttr) @@ -10279,25 +10939,25 @@ export function initMoves() { .attr(IgnoreOpponentStatStagesAttr) .slicingMove(), new AttackMove(MoveId.RAZOR_SHELL, PokemonType.WATER, MoveCategory.PHYSICAL, 75, 95, 10, 50, 0, 5) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1) + .attr(StatStageChangeAttr, [Stat.DEF], -1) .slicingMove(), new AttackMove(MoveId.HEAT_CRASH, PokemonType.FIRE, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 5) .attr(AlwaysHitMinimizeAttr) .attr(CompareWeightPowerAttr) .attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED), - new AttackMove(MoveId.LEAF_TORNADO, PokemonType.GRASS, MoveCategory.SPECIAL, 65, 90, 10, 50, 0, 5) - .attr(StatStageChangeAttr, [ Stat.ACC ], -1), + new AttackMove(MoveId.LEAF_TORNADO, PokemonType.GRASS, MoveCategory.SPECIAL, 65, 90, 10, 50, 0, 5) // + .attr(StatStageChangeAttr, [Stat.ACC], -1), new AttackMove(MoveId.STEAMROLLER, PokemonType.BUG, MoveCategory.PHYSICAL, 65, 100, 20, 30, 0, 5) .attr(AlwaysHitMinimizeAttr) .attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED) .attr(FlinchAttr), - new SelfStatusMove(MoveId.COTTON_GUARD, PokemonType.GRASS, -1, 10, -1, 0, 5) - .attr(StatStageChangeAttr, [ Stat.DEF ], 3, true), - new AttackMove(MoveId.NIGHT_DAZE, PokemonType.DARK, MoveCategory.SPECIAL, 85, 95, 10, 40, 0, 5) - .attr(StatStageChangeAttr, [ Stat.ACC ], -1), - new AttackMove(MoveId.PSYSTRIKE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 10, -1, 0, 5) + new SelfStatusMove(MoveId.COTTON_GUARD, PokemonType.GRASS, -1, 10, -1, 0, 5) // + .attr(StatStageChangeAttr, [Stat.DEF], 3, true), + new AttackMove(MoveId.NIGHT_DAZE, PokemonType.DARK, MoveCategory.SPECIAL, 85, 95, 10, 40, 0, 5) // + .attr(StatStageChangeAttr, [Stat.ACC], -1), + new AttackMove(MoveId.PSYSTRIKE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 10, -1, 0, 5) // .attr(DefDefAttr), - new AttackMove(MoveId.TAIL_SLAP, PokemonType.NORMAL, MoveCategory.PHYSICAL, 25, 85, 10, -1, 0, 5) + new AttackMove(MoveId.TAIL_SLAP, PokemonType.NORMAL, MoveCategory.PHYSICAL, 25, 85, 10, -1, 0, 5) // .attr(MultiHitAttr), new AttackMove(MoveId.HURRICANE, PokemonType.FLYING, MoveCategory.SPECIAL, 110, 70, 10, 30, 0, 5) .attr(ThunderAccuracyAttr) @@ -10307,13 +10967,13 @@ export function initMoves() { new AttackMove(MoveId.HEAD_CHARGE, PokemonType.NORMAL, MoveCategory.PHYSICAL, 120, 100, 15, -1, 0, 5) .attr(RecoilAttr) .recklessMove(), - new AttackMove(MoveId.GEAR_GRIND, PokemonType.STEEL, MoveCategory.PHYSICAL, 50, 85, 15, -1, 0, 5) + new AttackMove(MoveId.GEAR_GRIND, PokemonType.STEEL, MoveCategory.PHYSICAL, 50, 85, 15, -1, 0, 5) // .attr(MultiHitAttr, MultiHitType._2), new AttackMove(MoveId.SEARING_SHOT, PokemonType.FIRE, MoveCategory.SPECIAL, 100, 100, 5, 30, 0, 5) .attr(StatusEffectAttr, StatusEffect.BURN) .ballBombMove() .target(MoveTarget.ALL_NEAR_OTHERS), - new AttackMove(MoveId.TECHNO_BLAST, PokemonType.NORMAL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 5) + new AttackMove(MoveId.TECHNO_BLAST, PokemonType.NORMAL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 5) // .attr(TechnoBlastTypeAttr), new AttackMove(MoveId.RELIC_SONG, PokemonType.NORMAL, MoveCategory.SPECIAL, 75, 100, 10, 10, 0, 5) .attr(StatusEffectAttr, StatusEffect.SLEEP) @@ -10323,14 +10983,14 @@ export function initMoves() { .attr(DefDefAttr) .slicingMove(), new AttackMove(MoveId.GLACIATE, PokemonType.ICE, MoveCategory.SPECIAL, 65, 95, 10, 100, 0, 5) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1) + .attr(StatStageChangeAttr, [Stat.SPD], -1) .target(MoveTarget.ALL_NEAR_ENEMIES), - new AttackMove(MoveId.BOLT_STRIKE, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 130, 85, 5, 20, 0, 5) + new AttackMove(MoveId.BOLT_STRIKE, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 130, 85, 5, 20, 0, 5) // .attr(StatusEffectAttr, StatusEffect.PARALYSIS), - new AttackMove(MoveId.BLUE_FLARE, PokemonType.FIRE, MoveCategory.SPECIAL, 130, 85, 5, 20, 0, 5) + new AttackMove(MoveId.BLUE_FLARE, PokemonType.FIRE, MoveCategory.SPECIAL, 130, 85, 5, 20, 0, 5) // .attr(StatusEffectAttr, StatusEffect.BURN), new AttackMove(MoveId.FIERY_DANCE, PokemonType.FIRE, MoveCategory.SPECIAL, 80, 100, 10, 50, 0, 5) - .attr(StatStageChangeAttr, [ Stat.SPATK ], 1, true) + .attr(StatStageChangeAttr, [Stat.SPATK], 1, true) .danceMove(), new ChargingAttackMove(MoveId.FREEZE_SHOCK, PokemonType.ICE, MoveCategory.PHYSICAL, 140, 90, 5, 30, 0, 5) .chargeText(i18next.t("moveTriggers:becameCloakedInFreezingLight", { pokemonName: "{USER}" })) @@ -10340,14 +11000,14 @@ export function initMoves() { .chargeText(i18next.t("moveTriggers:becameCloakedInFreezingAir", { pokemonName: "{USER}" })) .attr(StatusEffectAttr, StatusEffect.BURN), new AttackMove(MoveId.SNARL, PokemonType.DARK, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 5) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -1) + .attr(StatStageChangeAttr, [Stat.SPATK], -1) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(MoveId.ICICLE_CRASH, PokemonType.ICE, MoveCategory.PHYSICAL, 85, 90, 10, 30, 0, 5) .attr(FlinchAttr) .makesContact(false), - new AttackMove(MoveId.V_CREATE, PokemonType.FIRE, MoveCategory.PHYSICAL, 180, 95, 5, -1, 0, 5) - .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF, Stat.SPD ], -1, true), + new AttackMove(MoveId.V_CREATE, PokemonType.FIRE, MoveCategory.PHYSICAL, 180, 95, 5, -1, 0, 5) // + .attr(StatStageChangeAttr, [Stat.DEF, Stat.SPDEF, Stat.SPD], -1, true), new AttackMove(MoveId.FUSION_FLARE, PokemonType.FIRE, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 5) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) .attr(LastMoveDoublePowerAttr, MoveId.FUSION_BOLT), @@ -10364,21 +11024,25 @@ export function initMoves() { .attr(AddArenaTagAttr, ArenaTagType.MAT_BLOCK, 1, true, true) .condition(new FirstMoveCondition(), 3) .condition(failIfLastCondition, 3), - new AttackMove(MoveId.BELCH, PokemonType.POISON, MoveCategory.SPECIAL, 120, 90, 10, -1, 0, 6) + new AttackMove(MoveId.BELCH, PokemonType.POISON, MoveCategory.SPECIAL, 120, 90, 10, -1, 0, 6) // .restriction(user => !user.battleData.hasEatenBerry, "battle:moveDisabledBelch", true), new StatusMove(MoveId.ROTOTILLER, PokemonType.GROUND, -1, 10, -1, 0, 6) .target(MoveTarget.ALL) - .condition((user, target, move) => { + .condition((_user, _target, _move) => { // If any fielded pokémon is grass-type and grounded. - return [ ...globalScene.getEnemyParty(), ...globalScene.getPlayerParty() ].some((poke) => poke.isOfType(PokemonType.GRASS) && poke.isGrounded()); + return [...globalScene.getEnemyParty(), ...globalScene.getPlayerParty()].some( + poke => poke.isOfType(PokemonType.GRASS) && poke.isGrounded(), + ); }) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, false, { condition: (user, target, move) => target.isOfType(PokemonType.GRASS) && target.isGrounded() }), + .attr(StatStageChangeAttr, [Stat.ATK, Stat.SPATK], 1, false, { + condition: (_user, target, _move) => target.isOfType(PokemonType.GRASS) && target.isGrounded(), + }), new StatusMove(MoveId.STICKY_WEB, PokemonType.BUG, -1, 20, -1, 0, 6) .attr(AddArenaTrapTagAttr, ArenaTagType.STICKY_WEB) .target(MoveTarget.ENEMY_SIDE) .reflectable(), - new AttackMove(MoveId.FELL_STINGER, PokemonType.BUG, MoveCategory.PHYSICAL, 50, 100, 25, -1, 0, 6) - .attr(PostVictoryStatStageChangeAttr, [ Stat.ATK ], 3, true ), + new AttackMove(MoveId.FELL_STINGER, PokemonType.BUG, MoveCategory.PHYSICAL, 50, 100, 25, -1, 0, 6) // + .attr(PostVictoryStatStageChangeAttr, [Stat.ATK], 3, true), new ChargingAttackMove(MoveId.PHANTOM_FORCE, PokemonType.GHOST, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 6) .chargeText(i18next.t("moveTriggers:vanishedInstantly", { pokemonName: "{USER}" })) .chargeAttr(SemiInvulnerableAttr, BattlerTagType.HIDDEN) @@ -10387,7 +11051,7 @@ export function initMoves() { .attr(AddTypeAttr, PokemonType.GHOST) .reflectable(), new StatusMove(MoveId.NOBLE_ROAR, PokemonType.NORMAL, 100, 30, -1, 0, 6) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.SPATK], -1) .soundBased() .reflectable(), new StatusMove(MoveId.ION_DELUGE, PokemonType.ELECTRIC, -1, 25, -1, 1, 6) @@ -10411,11 +11075,11 @@ export function initMoves() { .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES), new StatusMove(MoveId.PARTING_SHOT, PokemonType.DARK, 100, 20, -1, 0, 6) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1, false, { trigger: MoveEffectTrigger.PRE_APPLY }) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.SPATK], -1, false, { trigger: MoveEffectTrigger.PRE_APPLY }) .attr(ForceSwitchOutAttr, true) .soundBased() .reflectable(), - new StatusMove(MoveId.TOPSY_TURVY, PokemonType.DARK, -1, 20, -1, 0, 6) + new StatusMove(MoveId.TOPSY_TURVY, PokemonType.DARK, -1, 20, -1, 0, 6) // .attr(InvertStatsAttr) .reflectable(), new AttackMove(MoveId.DRAINING_KISS, PokemonType.FAIRY, MoveCategory.SPECIAL, 50, 100, 10, -1, 0, 6) @@ -10428,21 +11092,24 @@ export function initMoves() { .condition(failIfLastCondition, 3), new StatusMove(MoveId.FLOWER_SHIELD, PokemonType.FAIRY, -1, 10, -1, 0, 6) .target(MoveTarget.ALL) - .attr(StatStageChangeAttr, [ Stat.DEF ], 1, false, { condition: (user, target, move) => target.getTypes().includes(PokemonType.GRASS) && !target.getTag(SemiInvulnerableTag) }), + .attr(StatStageChangeAttr, [Stat.DEF], 1, false, { + condition: (_user, target, _move) => + target.getTypes().includes(PokemonType.GRASS) && !target.getTag(SemiInvulnerableTag), + }), new StatusMove(MoveId.GRASSY_TERRAIN, PokemonType.GRASS, -1, 10, -1, 0, 6) .attr(TerrainChangeAttr, TerrainType.GRASSY) .target(MoveTarget.BOTH_SIDES), new StatusMove(MoveId.MISTY_TERRAIN, PokemonType.FAIRY, -1, 10, -1, 0, 6) .attr(TerrainChangeAttr, TerrainType.MISTY) .target(MoveTarget.BOTH_SIDES), - new StatusMove(MoveId.ELECTRIFY, PokemonType.ELECTRIC, -1, 20, -1, 0, 6) + new StatusMove(MoveId.ELECTRIFY, PokemonType.ELECTRIC, -1, 20, -1, 0, 6) // .attr(AddBattlerTagAttr, BattlerTagType.ELECTRIFIED, false, true), - new AttackMove(MoveId.PLAY_ROUGH, PokemonType.FAIRY, MoveCategory.PHYSICAL, 90, 90, 10, 10, 0, 6) - .attr(StatStageChangeAttr, [ Stat.ATK ], -1), - new AttackMove(MoveId.FAIRY_WIND, PokemonType.FAIRY, MoveCategory.SPECIAL, 40, 100, 30, -1, 0, 6) + new AttackMove(MoveId.PLAY_ROUGH, PokemonType.FAIRY, MoveCategory.PHYSICAL, 90, 90, 10, 10, 0, 6) // + .attr(StatStageChangeAttr, [Stat.ATK], -1), + new AttackMove(MoveId.FAIRY_WIND, PokemonType.FAIRY, MoveCategory.SPECIAL, 40, 100, 30, -1, 0, 6) // .windMove(), - new AttackMove(MoveId.MOONBLAST, PokemonType.FAIRY, MoveCategory.SPECIAL, 95, 100, 15, 30, 0, 6) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -1), + new AttackMove(MoveId.MOONBLAST, PokemonType.FAIRY, MoveCategory.SPECIAL, 95, 100, 15, 30, 0, 6) // + .attr(StatStageChangeAttr, [Stat.SPATK], -1), new AttackMove(MoveId.BOOMBURST, PokemonType.NORMAL, MoveCategory.SPECIAL, 140, 100, 10, -1, 0, 6) .soundBased() .target(MoveTarget.ALL_NEAR_OTHERS), @@ -10455,15 +11122,15 @@ export function initMoves() { .attr(ProtectAttr, BattlerTagType.KINGS_SHIELD) .condition(failIfLastCondition, 3), new StatusMove(MoveId.PLAY_NICE, PokemonType.NORMAL, -1, 20, -1, 0, 6) - .attr(StatStageChangeAttr, [ Stat.ATK ], -1) + .attr(StatStageChangeAttr, [Stat.ATK], -1) .ignoresSubstitute() .reflectable(), new StatusMove(MoveId.CONFIDE, PokemonType.NORMAL, -1, 20, -1, 0, 6) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -1) + .attr(StatStageChangeAttr, [Stat.SPATK], -1) .soundBased() .reflectable(), new AttackMove(MoveId.DIAMOND_STORM, PokemonType.ROCK, MoveCategory.PHYSICAL, 100, 95, 5, 50, 0, 6) - .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true, { firstTargetOnly: true }) + .attr(StatStageChangeAttr, [Stat.DEF], 2, true, { firstTargetOnly: true }) .makesContact(false) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(MoveId.STEAM_ERUPTION, PokemonType.WATER, MoveCategory.SPECIAL, 110, 95, 5, 30, 0, 6) @@ -10477,21 +11144,24 @@ export function initMoves() { .attr(MultiHitAttr) .attr(WaterShurikenPowerAttr) .attr(WaterShurikenMultiHitTypeAttr), - new AttackMove(MoveId.MYSTICAL_FIRE, PokemonType.FIRE, MoveCategory.SPECIAL, 75, 100, 10, 100, 0, 6) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -1), + new AttackMove(MoveId.MYSTICAL_FIRE, PokemonType.FIRE, MoveCategory.SPECIAL, 75, 100, 10, 100, 0, 6) // + .attr(StatStageChangeAttr, [Stat.SPATK], -1), new SelfStatusMove(MoveId.SPIKY_SHIELD, PokemonType.GRASS, -1, 10, -1, 4, 6) .attr(ProtectAttr, BattlerTagType.SPIKY_SHIELD) .condition(failIfLastCondition, 3), new StatusMove(MoveId.AROMATIC_MIST, PokemonType.FAIRY, -1, 20, -1, 0, 6) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], 1) + .attr(StatStageChangeAttr, [Stat.SPDEF], 1) .ignoresSubstitute() .condition(failIfSingleBattle) .target(MoveTarget.NEAR_ALLY), new StatusMove(MoveId.EERIE_IMPULSE, PokemonType.ELECTRIC, 100, 15, -1, 0, 6) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -2) + .attr(StatStageChangeAttr, [Stat.SPATK], -2) .reflectable(), new StatusMove(MoveId.VENOM_DRENCH, PokemonType.POISON, 100, 20, -1, 0, 6) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK, Stat.SPD ], -1, false, { condition: (user, target, move) => target.status?.effect === StatusEffect.POISON || target.status?.effect === StatusEffect.TOXIC }) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.SPATK, Stat.SPD], -1, false, { + condition: (_user, target, _move) => + target.status?.effect === StatusEffect.POISON || target.status?.effect === StatusEffect.TOXIC, + }) .target(MoveTarget.ALL_NEAR_ENEMIES) .reflectable(), new StatusMove(MoveId.POWDER, PokemonType.BUG, 100, 20, -1, 1, 6) @@ -10501,19 +11171,26 @@ export function initMoves() { .reflectable(), new ChargingSelfStatusMove(MoveId.GEOMANCY, PokemonType.FAIRY, -1, 10, -1, 0, 6) .chargeText(i18next.t("moveTriggers:isChargingPower", { pokemonName: "{USER}" })) - .attr(StatStageChangeAttr, [ Stat.SPATK, Stat.SPDEF, Stat.SPD ], 2, true), + .attr(StatStageChangeAttr, [Stat.SPATK, Stat.SPDEF, Stat.SPD], 2, true), new StatusMove(MoveId.MAGNETIC_FLUX, PokemonType.ELECTRIC, -1, 20, -1, 0, 6) - .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], 1, false, { condition: (user, target, move) => !![ AbilityId.PLUS, AbilityId.MINUS ].find(a => target.hasAbility(a, false)) }) + .attr(StatStageChangeAttr, [Stat.DEF, Stat.SPDEF], 1, false, { + condition: (_user, target, _move) => !![AbilityId.PLUS, AbilityId.MINUS].find(a => target.hasAbility(a, false)), + }) .ignoresSubstitute() .target(MoveTarget.USER_AND_ALLIES) - .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ AbilityId.PLUS, AbilityId.MINUS ].find(a => p?.hasAbility(a, false)))), + .condition( + (user, _target, _move) => + !![user, user.getAlly()] + .filter(p => p?.isActive()) + .find(p => !![AbilityId.PLUS, AbilityId.MINUS].find(a => p?.hasAbility(a, false))), + ), new StatusMove(MoveId.HAPPY_HOUR, PokemonType.NORMAL, -1, 30, -1, 0, 6) // No animation .attr(AddArenaTagAttr, ArenaTagType.HAPPY_HOUR, 0, true) .target(MoveTarget.USER_SIDE), new StatusMove(MoveId.ELECTRIC_TERRAIN, PokemonType.ELECTRIC, -1, 10, -1, 0, 6) .attr(TerrainChangeAttr, TerrainType.ELECTRIC) .target(MoveTarget.BOTH_SIDES), - new AttackMove(MoveId.DAZZLING_GLEAM, PokemonType.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 6) + new AttackMove(MoveId.DAZZLING_GLEAM, PokemonType.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 6) // .target(MoveTarget.ALL_NEAR_ENEMIES), new SelfStatusMove(MoveId.CELEBRATE, PokemonType.NORMAL, -1, 40, -1, 0, 6) // NB: This needs a lambda function as the user will not be logged in by the time the moves are initialized @@ -10522,17 +11199,17 @@ export function initMoves() { .ignoresSubstitute() .target(MoveTarget.NEAR_ALLY), new StatusMove(MoveId.BABY_DOLL_EYES, PokemonType.FAIRY, 100, 30, -1, 1, 6) - .attr(StatStageChangeAttr, [ Stat.ATK ], -1) + .attr(StatStageChangeAttr, [Stat.ATK], -1) .reflectable(), - new AttackMove(MoveId.NUZZLE, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 20, 100, 20, 100, 0, 6) + new AttackMove(MoveId.NUZZLE, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 20, 100, 20, 100, 0, 6) // .attr(StatusEffectAttr, StatusEffect.PARALYSIS), - new AttackMove(MoveId.HOLD_BACK, PokemonType.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 6) + new AttackMove(MoveId.HOLD_BACK, PokemonType.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 6) // .attr(SurviveDamageAttr), new AttackMove(MoveId.INFESTATION, PokemonType.BUG, MoveCategory.SPECIAL, 20, 100, 20, -1, 0, 6) .makesContact() .attr(TrapAttr, BattlerTagType.INFESTATION), new AttackMove(MoveId.POWER_UP_PUNCH, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 20, 100, 0, 6) - .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true) + .attr(StatStageChangeAttr, [Stat.ATK], 1, true) .punchingMove(), new AttackMove(MoveId.OBLIVION_WING, PokemonType.FLYING, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 6) .attr(HitHealAttr, 0.75) @@ -10543,7 +11220,7 @@ export function initMoves() { .attr(HitsTagAttr, BattlerTagType.FLYING) .attr(HitsTagAttr, BattlerTagType.FLOATING) .attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED) - .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.FLOATING, BattlerTagType.TELEKINESIS ]) + .attr(RemoveBattlerTagAttr, [BattlerTagType.FLYING, BattlerTagType.FLOATING, BattlerTagType.TELEKINESIS]) .makesContact(false) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(MoveId.THOUSAND_WAVES, PokemonType.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, 100, 0, 6) @@ -10562,93 +11239,95 @@ export function initMoves() { new AttackMove(MoveId.PRECIPICE_BLADES, PokemonType.GROUND, MoveCategory.PHYSICAL, 120, 85, 10, -1, 0, 6) .makesContact(false) .target(MoveTarget.ALL_NEAR_ENEMIES), - new AttackMove(MoveId.DRAGON_ASCENT, PokemonType.FLYING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 6) - .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], -1, true), + new AttackMove(MoveId.DRAGON_ASCENT, PokemonType.FLYING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 6) // + .attr(StatStageChangeAttr, [Stat.DEF, Stat.SPDEF], -1, true), new AttackMove(MoveId.HYPERSPACE_FURY, PokemonType.DARK, MoveCategory.PHYSICAL, 100, -1, 5, -1, 0, 6) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1, true) + .attr(StatStageChangeAttr, [Stat.DEF], -1, true) .ignoresSubstitute() .makesContact(false) .ignoresProtect(), /* Unused */ - new AttackMove(MoveId.BREAKNECK_BLITZ__PHYSICAL, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.BREAKNECK_BLITZ__PHYSICAL, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.BREAKNECK_BLITZ__SPECIAL, PokemonType.NORMAL, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.BREAKNECK_BLITZ__SPECIAL, PokemonType.NORMAL, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.ALL_OUT_PUMMELING__PHYSICAL, PokemonType.FIGHTING, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.ALL_OUT_PUMMELING__PHYSICAL, PokemonType.FIGHTING, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.ALL_OUT_PUMMELING__SPECIAL, PokemonType.FIGHTING, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.ALL_OUT_PUMMELING__SPECIAL, PokemonType.FIGHTING, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), + // biome-ignore format: slightly too long new AttackMove(MoveId.SUPERSONIC_SKYSTRIKE__PHYSICAL, PokemonType.FLYING, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) .unimplemented(), - new AttackMove(MoveId.SUPERSONIC_SKYSTRIKE__SPECIAL, PokemonType.FLYING, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.SUPERSONIC_SKYSTRIKE__SPECIAL, PokemonType.FLYING, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.ACID_DOWNPOUR__PHYSICAL, PokemonType.POISON, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.ACID_DOWNPOUR__PHYSICAL, PokemonType.POISON, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.ACID_DOWNPOUR__SPECIAL, PokemonType.POISON, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.ACID_DOWNPOUR__SPECIAL, PokemonType.POISON, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.TECTONIC_RAGE__PHYSICAL, PokemonType.GROUND, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.TECTONIC_RAGE__PHYSICAL, PokemonType.GROUND, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.TECTONIC_RAGE__SPECIAL, PokemonType.GROUND, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.TECTONIC_RAGE__SPECIAL, PokemonType.GROUND, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.CONTINENTAL_CRUSH__PHYSICAL, PokemonType.ROCK, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.CONTINENTAL_CRUSH__PHYSICAL, PokemonType.ROCK, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.CONTINENTAL_CRUSH__SPECIAL, PokemonType.ROCK, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.CONTINENTAL_CRUSH__SPECIAL, PokemonType.ROCK, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.SAVAGE_SPIN_OUT__PHYSICAL, PokemonType.BUG, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.SAVAGE_SPIN_OUT__PHYSICAL, PokemonType.BUG, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.SAVAGE_SPIN_OUT__SPECIAL, PokemonType.BUG, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.SAVAGE_SPIN_OUT__SPECIAL, PokemonType.BUG, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), + // biome-ignore format: slightly too long new AttackMove(MoveId.NEVER_ENDING_NIGHTMARE__PHYSICAL, PokemonType.GHOST, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) .unimplemented(), - new AttackMove(MoveId.NEVER_ENDING_NIGHTMARE__SPECIAL, PokemonType.GHOST, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.NEVER_ENDING_NIGHTMARE__SPECIAL, PokemonType.GHOST, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.CORKSCREW_CRASH__PHYSICAL, PokemonType.STEEL, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.CORKSCREW_CRASH__PHYSICAL, PokemonType.STEEL, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.CORKSCREW_CRASH__SPECIAL, PokemonType.STEEL, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.CORKSCREW_CRASH__SPECIAL, PokemonType.STEEL, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.INFERNO_OVERDRIVE__PHYSICAL, PokemonType.FIRE, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.INFERNO_OVERDRIVE__PHYSICAL, PokemonType.FIRE, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.INFERNO_OVERDRIVE__SPECIAL, PokemonType.FIRE, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.INFERNO_OVERDRIVE__SPECIAL, PokemonType.FIRE, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.HYDRO_VORTEX__PHYSICAL, PokemonType.WATER, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.HYDRO_VORTEX__PHYSICAL, PokemonType.WATER, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.HYDRO_VORTEX__SPECIAL, PokemonType.WATER, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.HYDRO_VORTEX__SPECIAL, PokemonType.WATER, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.BLOOM_DOOM__PHYSICAL, PokemonType.GRASS, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.BLOOM_DOOM__PHYSICAL, PokemonType.GRASS, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.BLOOM_DOOM__SPECIAL, PokemonType.GRASS, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.BLOOM_DOOM__SPECIAL, PokemonType.GRASS, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.GIGAVOLT_HAVOC__PHYSICAL, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.GIGAVOLT_HAVOC__PHYSICAL, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.GIGAVOLT_HAVOC__SPECIAL, PokemonType.ELECTRIC, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.GIGAVOLT_HAVOC__SPECIAL, PokemonType.ELECTRIC, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.SHATTERED_PSYCHE__PHYSICAL, PokemonType.PSYCHIC, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.SHATTERED_PSYCHE__PHYSICAL, PokemonType.PSYCHIC, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.SHATTERED_PSYCHE__SPECIAL, PokemonType.PSYCHIC, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.SHATTERED_PSYCHE__SPECIAL, PokemonType.PSYCHIC, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.SUBZERO_SLAMMER__PHYSICAL, PokemonType.ICE, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.SUBZERO_SLAMMER__PHYSICAL, PokemonType.ICE, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.SUBZERO_SLAMMER__SPECIAL, PokemonType.ICE, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.SUBZERO_SLAMMER__SPECIAL, PokemonType.ICE, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.DEVASTATING_DRAKE__PHYSICAL, PokemonType.DRAGON, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.DEVASTATING_DRAKE__PHYSICAL, PokemonType.DRAGON, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.DEVASTATING_DRAKE__SPECIAL, PokemonType.DRAGON, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.DEVASTATING_DRAKE__SPECIAL, PokemonType.DRAGON, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.BLACK_HOLE_ECLIPSE__PHYSICAL, PokemonType.DARK, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.BLACK_HOLE_ECLIPSE__PHYSICAL, PokemonType.DARK, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.BLACK_HOLE_ECLIPSE__SPECIAL, PokemonType.DARK, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.BLACK_HOLE_ECLIPSE__SPECIAL, PokemonType.DARK, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.TWINKLE_TACKLE__PHYSICAL, PokemonType.FAIRY, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.TWINKLE_TACKLE__PHYSICAL, PokemonType.FAIRY, MoveCategory.PHYSICAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.TWINKLE_TACKLE__SPECIAL, PokemonType.FAIRY, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.TWINKLE_TACKLE__SPECIAL, PokemonType.FAIRY, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.CATASTROPIKA, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 210, -1, 1, -1, 0, 7) + new AttackMove(MoveId.CATASTROPIKA, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 210, -1, 1, -1, 0, 7) // .unimplemented(), /* End Unused */ new SelfStatusMove(MoveId.SHORE_UP, PokemonType.GROUND, -1, 5, -1, 0, 7) .attr(SandHealAttr) .triageMove(), - new AttackMove(MoveId.FIRST_IMPRESSION, PokemonType.BUG, MoveCategory.PHYSICAL, 90, 100, 10, -1, 2, 7) + new AttackMove(MoveId.FIRST_IMPRESSION, PokemonType.BUG, MoveCategory.PHYSICAL, 90, 100, 10, -1, 2, 7) // .condition(new FirstMoveCondition(), 3), new SelfStatusMove(MoveId.BANEFUL_BUNKER, PokemonType.POISON, -1, 10, -1, 4, 7) .attr(ProtectAttr, BattlerTagType.BANEFUL_BUNKER) @@ -10656,32 +11335,39 @@ export function initMoves() { new AttackMove(MoveId.SPIRIT_SHACKLE, PokemonType.GHOST, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 7) .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1, 1, true) .makesContact(false), - new AttackMove(MoveId.DARKEST_LARIAT, PokemonType.DARK, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 7) + new AttackMove(MoveId.DARKEST_LARIAT, PokemonType.DARK, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 7) // .attr(IgnoreOpponentStatStagesAttr), new AttackMove(MoveId.SPARKLING_ARIA, PokemonType.WATER, MoveCategory.SPECIAL, 90, 100, 10, 100, 0, 7) .attr(HealStatusEffectAttr, false, StatusEffect.BURN) .soundBased() .target(MoveTarget.ALL_NEAR_OTHERS), new AttackMove(MoveId.ICE_HAMMER, PokemonType.ICE, MoveCategory.PHYSICAL, 100, 90, 10, -1, 0, 7) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1, true) + .attr(StatStageChangeAttr, [Stat.SPD], -1, true) .punchingMove(), new StatusMove(MoveId.FLORAL_HEALING, PokemonType.FAIRY, -1, 10, -1, 0, 7) - .attr(BoostHealAttr, 0.5, 2 / 3, true, false, (user, target, move) => globalScene.arena.terrain?.terrainType === TerrainType.GRASSY) + .attr( + BoostHealAttr, + 0.5, + 2 / 3, + true, + false, + (_user, _target, _move) => globalScene.arena.terrain?.terrainType === TerrainType.GRASSY, + ) .triageMove() .reflectable(), new AttackMove(MoveId.HIGH_HORSEPOWER, PokemonType.GROUND, MoveCategory.PHYSICAL, 95, 95, 10, -1, 0, 7), new StatusMove(MoveId.STRENGTH_SAP, PokemonType.GRASS, 100, 10, -1, 0, 7) .attr(HitHealAttr, null, Stat.ATK) - .attr(StatStageChangeAttr, [ Stat.ATK ], -1) - .condition((user, target, move) => target.getStatStage(Stat.ATK) > -6) + .attr(StatStageChangeAttr, [Stat.ATK], -1) + .condition((_user, target, _move) => target.getStatStage(Stat.ATK) > -6) .triageMove() .reflectable(), new ChargingAttackMove(MoveId.SOLAR_BLADE, PokemonType.GRASS, MoveCategory.PHYSICAL, 125, 100, 10, -1, 0, 7) .chargeText(i18next.t("moveTriggers:isGlowing", { pokemonName: "{USER}" })) - .chargeAttr(WeatherInstantChargeAttr, [ WeatherType.SUNNY, WeatherType.HARSH_SUN ]) + .chargeAttr(WeatherInstantChargeAttr, [WeatherType.SUNNY, WeatherType.HARSH_SUN]) .attr(AntiSunlightPowerDecreaseAttr) .slicingMove(), - new AttackMove(MoveId.LEAFAGE, PokemonType.GRASS, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 7) + new AttackMove(MoveId.LEAFAGE, PokemonType.GRASS, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 7) // .makesContact(false), new StatusMove(MoveId.SPOTLIGHT, PokemonType.NORMAL, -1, 15, -1, 3, 7) .attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, false) @@ -10689,46 +11375,58 @@ export function initMoves() { .reflectable(), new StatusMove(MoveId.TOXIC_THREAD, PokemonType.POISON, 100, 20, -1, 0, 7) .attr(StatusEffectAttr, StatusEffect.POISON) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1) + .attr(StatStageChangeAttr, [Stat.SPD], -1) .reflectable(), new SelfStatusMove(MoveId.LASER_FOCUS, PokemonType.NORMAL, -1, 30, -1, 0, 7) .attr(AddBattlerTagAttr, BattlerTagType.ALWAYS_CRIT, true, false) - .attr(MessageAttr, (user) => + .attr(MessageAttr, user => i18next.t("battlerTags:laserFocusOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user), }), ), new StatusMove(MoveId.GEAR_UP, PokemonType.STEEL, -1, 20, -1, 0, 7) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, false, { condition: (user, target, move) => !![ AbilityId.PLUS, AbilityId.MINUS ].find(a => target.hasAbility(a, false)) }) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.SPATK], 1, false, { + condition: (_user, target, _move) => !![AbilityId.PLUS, AbilityId.MINUS].find(a => target.hasAbility(a, false)), + }) .ignoresSubstitute() .target(MoveTarget.USER_AND_ALLIES) - .condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ AbilityId.PLUS, AbilityId.MINUS ].find(a => p?.hasAbility(a, false)))), - new AttackMove(MoveId.THROAT_CHOP, PokemonType.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7) + .condition( + (user, _target, _move) => + !![user, user.getAlly()] + .filter(p => p?.isActive()) + .find(p => !![AbilityId.PLUS, AbilityId.MINUS].find(a => p?.hasAbility(a, false))), + ), + new AttackMove(MoveId.THROAT_CHOP, PokemonType.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7) // .attr(AddBattlerTagAttr, BattlerTagType.THROAT_CHOPPED), new AttackMove(MoveId.POLLEN_PUFF, PokemonType.BUG, MoveCategory.SPECIAL, 90, 100, 15, -1, 0, 7) .attr(StatusCategoryOnAllyAttr) .attr(HealOnAllyAttr, 0.5, true, false) .ballBombMove() // Fail if used against an ally that is affected by heal block, during the second failure check - .condition((user, target) => target == null || target.isOpponent(user) || !target.getTag(BattlerTagType.HEAL_BLOCK), 2), - new AttackMove(MoveId.ANCHOR_SHOT, PokemonType.STEEL, MoveCategory.PHYSICAL, 80, 100, 20, 100, 0, 7) + .condition( + (user, target) => target == null || target.isOpponent(user) || !target.getTag(BattlerTagType.HEAL_BLOCK), + 2, + ), + new AttackMove(MoveId.ANCHOR_SHOT, PokemonType.STEEL, MoveCategory.PHYSICAL, 80, 100, 20, 100, 0, 7) // .attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1, 1, true), new StatusMove(MoveId.PSYCHIC_TERRAIN, PokemonType.PSYCHIC, -1, 10, -1, 0, 7) .attr(TerrainChangeAttr, TerrainType.PSYCHIC) .target(MoveTarget.BOTH_SIDES), - new AttackMove(MoveId.LUNGE, PokemonType.BUG, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7) - .attr(StatStageChangeAttr, [ Stat.ATK ], -1), - new AttackMove(MoveId.FIRE_LASH, PokemonType.FIRE, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1), - new AttackMove(MoveId.POWER_TRIP, PokemonType.DARK, MoveCategory.PHYSICAL, 20, 100, 10, -1, 0, 7) + new AttackMove(MoveId.LUNGE, PokemonType.BUG, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7) // + .attr(StatStageChangeAttr, [Stat.ATK], -1), + new AttackMove(MoveId.FIRE_LASH, PokemonType.FIRE, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7) // + .attr(StatStageChangeAttr, [Stat.DEF], -1), + new AttackMove(MoveId.POWER_TRIP, PokemonType.DARK, MoveCategory.PHYSICAL, 20, 100, 10, -1, 0, 7) // .attr(PositiveStatStagePowerAttr), new AttackMove(MoveId.BURN_UP, PokemonType.FIRE, MoveCategory.SPECIAL, 130, 100, 5, -1, 0, 7) // Pass `true` to `ForDefend` as it should fail if the user is terastallized to a type that is not FIRE .condition(user => user.isOfType(PokemonType.FIRE, true, true), 2) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) .attr(AddBattlerTagAttr, BattlerTagType.BURNED_UP, true, false) - .attr(RemoveTypeAttr, PokemonType.FIRE, (user) => { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:burnedItselfOut", { pokemonName: getPokemonNameWithAffix(user) })); + .attr(RemoveTypeAttr, PokemonType.FIRE, user => { + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:burnedItselfOut", { pokemonName: getPokemonNameWithAffix(user) }), + ); }), new StatusMove(MoveId.SPEED_SWAP, PokemonType.PSYCHIC, -1, 10, -1, 0, 7) // Note: the 3 is NOT a typo; unlike power split / guard split which happen in the second failure sequence, speed @@ -10739,7 +11437,7 @@ export function initMoves() { .ignoresSubstitute(), new AttackMove(MoveId.SMART_STRIKE, PokemonType.STEEL, MoveCategory.PHYSICAL, 70, -1, 10, -1, 0, 7), new StatusMove(MoveId.PURIFY, PokemonType.POISON, -1, 20, -1, 0, 7) - .condition((user, target, move) => { + .condition((_user, target, _move) => { if (!target.status) { return false; } @@ -10755,8 +11453,8 @@ export function initMoves() { new AttackMove(MoveId.CORE_ENFORCER, PokemonType.DRAGON, MoveCategory.SPECIAL, 100, 100, 10, -1, 0, 7) .target(MoveTarget.ALL_NEAR_ENEMIES) .attr(SuppressAbilitiesIfActedAttr), - new AttackMove(MoveId.TROP_KICK, PokemonType.GRASS, MoveCategory.PHYSICAL, 70, 100, 15, 100, 0, 7) - .attr(StatStageChangeAttr, [ Stat.ATK ], -1), + new AttackMove(MoveId.TROP_KICK, PokemonType.GRASS, MoveCategory.PHYSICAL, 70, 100, 15, 100, 0, 7) // + .attr(StatStageChangeAttr, [Stat.ATK], -1), new StatusMove(MoveId.INSTRUCT, PokemonType.PSYCHIC, -1, 15, -1, 0, 7) .ignoresSubstitute() .attr(RepeatMoveAttr) @@ -10772,23 +11470,20 @@ export function initMoves() { .ballBombMove() .makesContact(false), new AttackMove(MoveId.CLANGING_SCALES, PokemonType.DRAGON, MoveCategory.SPECIAL, 110, 100, 5, -1, 0, 7) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1, true, { firstTargetOnly: true }) + .attr(StatStageChangeAttr, [Stat.DEF], -1, true, { firstTargetOnly: true }) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(MoveId.DRAGON_HAMMER, PokemonType.DRAGON, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 7), - new AttackMove(MoveId.BRUTAL_SWING, PokemonType.DARK, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 7) + new AttackMove(MoveId.BRUTAL_SWING, PokemonType.DARK, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 7) // .target(MoveTarget.ALL_NEAR_OTHERS), new StatusMove(MoveId.AURORA_VEIL, PokemonType.ICE, -1, 20, -1, 0, 7) - .condition( - () => { - const weather = globalScene.arena.weather; - if (weather == null || weather.isEffectSuppressed()) { - return false; - } - return weather.weatherType === WeatherType.HAIL || weather.weatherType === WeatherType.SNOW; - }, - 3 - ) + .condition(() => { + const weather = globalScene.arena.weather; + if (weather == null || weather.isEffectSuppressed()) { + return false; + } + return weather.weatherType === WeatherType.HAIL || weather.weatherType === WeatherType.SNOW; + }, 3) .attr(AddArenaTagAttr, ArenaTagType.AURORA_VEIL, 5, true) .target(MoveTarget.USER_SIDE), /* Unused */ @@ -10804,9 +11499,9 @@ export function initMoves() { new AttackMove(MoveId.OCEANIC_OPERETTA, PokemonType.WATER, MoveCategory.SPECIAL, 195, -1, 1, -1, 0, 7) .unimplemented() .edgeCase(), // I assume it's because it needs sparkling aria and primarina - new AttackMove(MoveId.GUARDIAN_OF_ALOLA, PokemonType.FAIRY, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) + new AttackMove(MoveId.GUARDIAN_OF_ALOLA, PokemonType.FAIRY, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) // .unimplemented(), - new AttackMove(MoveId.SOUL_STEALING_7_STAR_STRIKE, PokemonType.GHOST, MoveCategory.PHYSICAL, 195, -1, 1, -1, 0, 7) + new AttackMove(MoveId.SOUL_STEALING_7_STAR_STRIKE, PokemonType.GHOST, MoveCategory.PHYSICAL, 195, -1, 1, -1, 0, 7) // .unimplemented(), new AttackMove(MoveId.STOKED_SPARKSURFER, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 175, -1, 1, 100, 0, 7) .unimplemented() @@ -10816,7 +11511,7 @@ export function initMoves() { .edgeCase(), // I assume it's because it needs giga impact and snorlax new SelfStatusMove(MoveId.EXTREME_EVOBOOST, PokemonType.NORMAL, -1, 1, -1, 0, 7) .unimplemented() - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 2, true), + .attr(StatStageChangeAttr, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], 2, true), new AttackMove(MoveId.GENESIS_SUPERNOVA, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 185, -1, 1, 100, 0, 7) .unimplemented() .attr(TerrainChangeAttr, TerrainType.PSYCHIC), @@ -10826,44 +11521,48 @@ export function initMoves() { .target(MoveTarget.ALL_NEAR_ENEMIES) // Fails if the user was not hit by a physical attack during the turn .condition(user => user.getTag(ShellTrapTag)?.activated === true, 3), - new AttackMove(MoveId.FLEUR_CANNON, PokemonType.FAIRY, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 7) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true), + new AttackMove(MoveId.FLEUR_CANNON, PokemonType.FAIRY, MoveCategory.SPECIAL, 130, 90, 5, -1, 0, 7) // + .attr(StatStageChangeAttr, [Stat.SPATK], -2, true), new AttackMove(MoveId.PSYCHIC_FANGS, PokemonType.PSYCHIC, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 7) .bitingMove() .attr(RemoveScreensAttr), new AttackMove(MoveId.STOMPING_TANTRUM, PokemonType.GROUND, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 7) - .attr(MovePowerMultiplierAttr, (user) => { + .attr(MovePowerMultiplierAttr, user => { // Stomping tantrum triggers on most failures (including sleep/freeze) const lastNonDancerMove = user.getLastXMoves(2)[1] as TurnMove | undefined; - return lastNonDancerMove && (lastNonDancerMove.result === MoveResult.MISS || lastNonDancerMove.result === MoveResult.FAIL) ? 2 : 1 + return lastNonDancerMove + && (lastNonDancerMove.result === MoveResult.MISS || lastNonDancerMove.result === MoveResult.FAIL) + ? 2 + : 1; }) // TODO: Review mainline accuracy and draft tests as needed .edgeCase(), new AttackMove(MoveId.SHADOW_BONE, PokemonType.GHOST, MoveCategory.PHYSICAL, 85, 100, 10, 20, 0, 7) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1) + .attr(StatStageChangeAttr, [Stat.DEF], -1) .makesContact(false), new AttackMove(MoveId.ACCELEROCK, PokemonType.ROCK, MoveCategory.PHYSICAL, 40, 100, 20, -1, 1, 7), - new AttackMove(MoveId.LIQUIDATION, PokemonType.WATER, MoveCategory.PHYSICAL, 85, 100, 10, 20, 0, 7) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1), - new AttackMove(MoveId.PRISMATIC_LASER, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 160, 100, 10, -1, 0, 7) + new AttackMove(MoveId.LIQUIDATION, PokemonType.WATER, MoveCategory.PHYSICAL, 85, 100, 10, 20, 0, 7) // + .attr(StatStageChangeAttr, [Stat.DEF], -1), + new AttackMove(MoveId.PRISMATIC_LASER, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 160, 100, 10, -1, 0, 7) // .attr(RechargeAttr), new AttackMove(MoveId.SPECTRAL_THIEF, PokemonType.GHOST, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 7) .attr(SpectralThiefAttr) .ignoresSubstitute(), - new AttackMove(MoveId.SUNSTEEL_STRIKE, PokemonType.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 7) + new AttackMove(MoveId.SUNSTEEL_STRIKE, PokemonType.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 7) // .ignoresAbilities(), - new AttackMove(MoveId.MOONGEIST_BEAM, PokemonType.GHOST, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7) + new AttackMove(MoveId.MOONGEIST_BEAM, PokemonType.GHOST, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7) // .ignoresAbilities(), new StatusMove(MoveId.TEARFUL_LOOK, PokemonType.NORMAL, -1, 20, -1, 0, 7) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.SPATK], -1) .reflectable(), - new AttackMove(MoveId.ZING_ZAP, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 80, 100, 10, 30, 0, 7) + new AttackMove(MoveId.ZING_ZAP, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 80, 100, 10, 30, 0, 7) // .attr(FlinchAttr), - new AttackMove(MoveId.NATURES_MADNESS, PokemonType.FAIRY, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 7) + new AttackMove(MoveId.NATURES_MADNESS, PokemonType.FAIRY, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 7) // .attr(TargetHalfHpDamageAttr), - new AttackMove(MoveId.MULTI_ATTACK, PokemonType.NORMAL, MoveCategory.PHYSICAL, 120, 100, 10, -1, 0, 7) + new AttackMove(MoveId.MULTI_ATTACK, PokemonType.NORMAL, MoveCategory.PHYSICAL, 120, 100, 10, -1, 0, 7) // .attr(FormChangeItemTypeAttr), /* Unused */ + // biome-ignore format: slightly too long new AttackMove(MoveId.TEN_MILLION_VOLT_THUNDERBOLT, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 195, -1, 1, -1, 0, 7) .unimplemented() .edgeCase(), // I assume it's because it needs thunderbolt and pikachu in a cap @@ -10898,7 +11597,9 @@ export function initMoves() { .makesContact(false), new AttackMove(MoveId.CLANGOROUS_SOULBLAZE, PokemonType.DRAGON, MoveCategory.SPECIAL, 185, -1, 1, 100, 0, 7) .unimplemented() - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true, { firstTargetOnly: true }) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], 1, true, { + firstTargetOnly: true, + }) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES) .edgeCase(), // I assume it needs clanging scales and Kommo-O @@ -10911,27 +11612,27 @@ export function initMoves() { new AttackMove(MoveId.FLOATY_FALL, PokemonType.FLYING, MoveCategory.PHYSICAL, 90, 95, 15, 30, 0, 7) .attr(FlinchAttr) .affectedByGravity(), - new AttackMove(MoveId.PIKA_PAPOW, PokemonType.ELECTRIC, MoveCategory.SPECIAL, -1, -1, 20, -1, 0, 7) + new AttackMove(MoveId.PIKA_PAPOW, PokemonType.ELECTRIC, MoveCategory.SPECIAL, -1, -1, 20, -1, 0, 7) // .attr(FriendshipPowerAttr), new AttackMove(MoveId.BOUNCY_BUBBLE, PokemonType.WATER, MoveCategory.SPECIAL, 60, 100, 20, -1, 0, 7) .attr(HitHealAttr, 1) .triageMove(), - new AttackMove(MoveId.BUZZY_BUZZ, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 60, 100, 20, 100, 0, 7) + new AttackMove(MoveId.BUZZY_BUZZ, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 60, 100, 20, 100, 0, 7) // .attr(StatusEffectAttr, StatusEffect.PARALYSIS), - new AttackMove(MoveId.SIZZLY_SLIDE, PokemonType.FIRE, MoveCategory.PHYSICAL, 60, 100, 20, 100, 0, 7) + new AttackMove(MoveId.SIZZLY_SLIDE, PokemonType.FIRE, MoveCategory.PHYSICAL, 60, 100, 20, 100, 0, 7) // .attr(StatusEffectAttr, StatusEffect.BURN), - new AttackMove(MoveId.GLITZY_GLOW, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 80, 95, 15, -1, 0, 7) + new AttackMove(MoveId.GLITZY_GLOW, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 80, 95, 15, -1, 0, 7) // .attr(AddArenaTagAttr, ArenaTagType.LIGHT_SCREEN, 5, false, true), - new AttackMove(MoveId.BADDY_BAD, PokemonType.DARK, MoveCategory.SPECIAL, 80, 95, 15, -1, 0, 7) + new AttackMove(MoveId.BADDY_BAD, PokemonType.DARK, MoveCategory.SPECIAL, 80, 95, 15, -1, 0, 7) // .attr(AddArenaTagAttr, ArenaTagType.REFLECT, 5, false, true), new AttackMove(MoveId.SAPPY_SEED, PokemonType.GRASS, MoveCategory.PHYSICAL, 100, 90, 10, -1, 0, 7) .attr(LeechSeedAttr) .makesContact(false), - new AttackMove(MoveId.FREEZY_FROST, PokemonType.ICE, MoveCategory.SPECIAL, 100, 90, 10, -1, 0, 7) + new AttackMove(MoveId.FREEZY_FROST, PokemonType.ICE, MoveCategory.SPECIAL, 100, 90, 10, -1, 0, 7) // .attr(ResetStatsAttr, true), - new AttackMove(MoveId.SPARKLY_SWIRL, PokemonType.FAIRY, MoveCategory.SPECIAL, 120, 85, 5, -1, 0, 7) + new AttackMove(MoveId.SPARKLY_SWIRL, PokemonType.FAIRY, MoveCategory.SPECIAL, 120, 85, 5, -1, 0, 7) // .attr(PartyStatusCureAttr, null, AbilityId.NONE), - new AttackMove(MoveId.VEEVEE_VOLLEY, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, -1, 20, -1, 0, 7) + new AttackMove(MoveId.VEEVEE_VOLLEY, PokemonType.NORMAL, MoveCategory.PHYSICAL, -1, -1, 20, -1, 0, 7) // .attr(FriendshipPowerAttr), new AttackMove(MoveId.DOUBLE_IRON_BASH, PokemonType.STEEL, MoveCategory.PHYSICAL, 60, 100, 5, 30, 0, 7) .attr(MultiHitAttr, MultiHitType._2) @@ -10943,16 +11644,18 @@ export function initMoves() { .attr(ProtectAttr) .condition(failIfLastCondition), /* End Unused */ - new AttackMove(MoveId.DYNAMAX_CANNON, PokemonType.DRAGON, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 8) - .attr(MovePowerMultiplierAttr, (user, target, move) => { - // Move is only stronger against overleveled foes. + new AttackMove(MoveId.DYNAMAX_CANNON, PokemonType.DRAGON, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 8) // + .attr(MovePowerMultiplierAttr, (_user, target, _move) => { + // Move is only stronger against overleveled foes. if (target.level > globalScene.getMaxExpLevel()) { - const dynamaxCannonPercentMarginBeforeFullDamage = 0.05; // How much % above MaxExpLevel of wave will the target need to be to take full damage. + // How much % above MaxExpLevel of wave will the target need to be to take full damage. + const dynamaxCannonPercentMarginBeforeFullDamage = 0.05; + const overLevel = target.level - globalScene.getMaxExpLevel(); + const damageFactor = globalScene.getMaxExpLevel() * dynamaxCannonPercentMarginBeforeFullDamage; // The move's power scales as the margin is approached, reaching double power when it does or goes over it. - return 1 + Math.min(1, (target.level - globalScene.getMaxExpLevel()) / (globalScene.getMaxExpLevel() * dynamaxCannonPercentMarginBeforeFullDamage)); - } else { - return 1; + return 1 + Math.min(1, overLevel / damageFactor); } + return 1; }), new AttackMove(MoveId.SNIPE_SHOT, PokemonType.WATER, MoveCategory.SPECIAL, 80, 100, 15, -1, 0, 8) @@ -10963,20 +11666,20 @@ export function initMoves() { .bitingMove(), new SelfStatusMove(MoveId.STUFF_CHEEKS, PokemonType.NORMAL, -1, 10, -1, 0, 8) .attr(EatBerryAttr, true) - .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true) + .attr(StatStageChangeAttr, [Stat.DEF], 2, true) .restriction( user => globalScene.findModifiers(m => m instanceof BerryModifier, user.isPlayer()).length === 0, "battle:moveDisabledNoBerry", true, - 3 + 3, ), new SelfStatusMove(MoveId.NO_RETREAT, PokemonType.FIGHTING, -1, 5, -1, 0, 8) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], 1, true) .attr(AddBattlerTagAttr, BattlerTagType.NO_RETREAT, true, true /* NOT ADDED if already trapped */) // fails if the user is currently trapped specifically from no retreat .condition(user => user.getTag(TrappedTag)?.tagType !== BattlerTagType.NO_RETREAT, 2), new StatusMove(MoveId.TAR_SHOT, PokemonType.ROCK, 100, 15, -1, 0, 8) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1) + .attr(StatStageChangeAttr, [Stat.SPD], -1) .attr(AddBattlerTagAttr, BattlerTagType.TAR_SHOT, false) .reflectable(), new StatusMove(MoveId.MAGIC_POWDER, PokemonType.PSYCHIC, 100, 20, -1, 0, 8) @@ -10993,13 +11696,27 @@ export function initMoves() { new StatusMove(MoveId.OCTOLOCK, PokemonType.FIGHTING, 100, 15, -1, 0, 8) .condition(failIfGhostTypeCondition) .attr(AddBattlerTagAttr, BattlerTagType.OCTOLOCK, false, true, 1), - new AttackMove(MoveId.BOLT_BEAK, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 8) - .attr(MovePowerMultiplierAttr, (_user, target) => target.turnData.acted ? 1 : 2), + new AttackMove(MoveId.BOLT_BEAK, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 8) // + .attr(MovePowerMultiplierAttr, (_user, target) => (target.turnData.acted ? 1 : 2)), new AttackMove(MoveId.FISHIOUS_REND, PokemonType.WATER, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 8) - .attr(MovePowerMultiplierAttr, (_user, target) => target.turnData.acted ? 1 : 2) + .attr(MovePowerMultiplierAttr, (_user, target) => (target.turnData.acted ? 1 : 2)) .bitingMove(), - new StatusMove(MoveId.COURT_CHANGE, PokemonType.NORMAL, 100, 10, -1, 0, 8) - .attr(SwapArenaTagsAttr, [ ArenaTagType.AURORA_VEIL, ArenaTagType.LIGHT_SCREEN, ArenaTagType.MIST, ArenaTagType.REFLECT, ArenaTagType.SPIKES, ArenaTagType.STEALTH_ROCK, ArenaTagType.STICKY_WEB, ArenaTagType.TAILWIND, ArenaTagType.TOXIC_SPIKES, ArenaTagType.SAFEGUARD, ArenaTagType.FIRE_GRASS_PLEDGE, ArenaTagType.WATER_FIRE_PLEDGE, ArenaTagType.GRASS_WATER_PLEDGE ]), + new StatusMove(MoveId.COURT_CHANGE, PokemonType.NORMAL, 100, 10, -1, 0, 8) // + .attr(SwapArenaTagsAttr, [ + ArenaTagType.AURORA_VEIL, + ArenaTagType.LIGHT_SCREEN, + ArenaTagType.MIST, + ArenaTagType.REFLECT, + ArenaTagType.SPIKES, + ArenaTagType.STEALTH_ROCK, + ArenaTagType.STICKY_WEB, + ArenaTagType.TAILWIND, + ArenaTagType.TOXIC_SPIKES, + ArenaTagType.SAFEGUARD, + ArenaTagType.FIRE_GRASS_PLEDGE, + ArenaTagType.WATER_FIRE_PLEDGE, + ArenaTagType.GRASS_WATER_PLEDGE, + ]), /* Unused */ new AttackMove(MoveId.MAX_FLARE, PokemonType.FIRE, MoveCategory.PHYSICAL, 10, -1, 10, -1, 0, 8) .target(MoveTarget.NEAR_ENEMY) @@ -11057,48 +11774,50 @@ export function initMoves() { .unimplemented(), /* End Unused */ new SelfStatusMove(MoveId.CLANGOROUS_SOUL, PokemonType.DRAGON, 100, 5, -1, 0, 8) - .attr(CutHpStatStageBoostAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, 3) + .attr(CutHpStatStageBoostAttr, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], 1, 3) .soundBased() .danceMove() .condition(new FailIfInsufficientHpCondition(3), 3), - new AttackMove(MoveId.BODY_PRESS, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8) + new AttackMove(MoveId.BODY_PRESS, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8) // .attr(DefAtkAttr), new StatusMove(MoveId.DECORATE, PokemonType.FAIRY, -1, 15, -1, 0, 8) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 2) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.SPATK], 2) .ignoresProtect(), new AttackMove(MoveId.DRUM_BEATING, PokemonType.GRASS, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 8) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1) + .attr(StatStageChangeAttr, [Stat.SPD], -1) .makesContact(false), - new AttackMove(MoveId.SNAP_TRAP, PokemonType.GRASS, MoveCategory.PHYSICAL, 35, 100, 15, -1, 0, 8) + new AttackMove(MoveId.SNAP_TRAP, PokemonType.GRASS, MoveCategory.PHYSICAL, 35, 100, 15, -1, 0, 8) // .attr(TrapAttr, BattlerTagType.SNAP_TRAP), new AttackMove(MoveId.PYRO_BALL, PokemonType.FIRE, MoveCategory.PHYSICAL, 120, 90, 5, 10, 0, 8) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) .attr(StatusEffectAttr, StatusEffect.BURN) .ballBombMove() .makesContact(false), - new AttackMove(MoveId.BEHEMOTH_BLADE, PokemonType.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 8) + new AttackMove(MoveId.BEHEMOTH_BLADE, PokemonType.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 8) // .slicingMove(), new AttackMove(MoveId.BEHEMOTH_BASH, PokemonType.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 8), new AttackMove(MoveId.AURA_WHEEL, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 110, 100, 10, 100, 0, 8) - .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true) + .attr(StatStageChangeAttr, [Stat.SPD], 1, true) .makesContact(false) .attr(AuraWheelTypeAttr), new AttackMove(MoveId.BREAKING_SWIPE, PokemonType.DRAGON, MoveCategory.PHYSICAL, 60, 100, 15, 100, 0, 8) .target(MoveTarget.ALL_NEAR_ENEMIES) - .attr(StatStageChangeAttr, [ Stat.ATK ], -1), + .attr(StatStageChangeAttr, [Stat.ATK], -1), new AttackMove(MoveId.BRANCH_POKE, PokemonType.GRASS, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 8), new AttackMove(MoveId.OVERDRIVE, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 8) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES), - new AttackMove(MoveId.APPLE_ACID, PokemonType.GRASS, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 8) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1), + new AttackMove(MoveId.APPLE_ACID, PokemonType.GRASS, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 8) // + .attr(StatStageChangeAttr, [Stat.SPDEF], -1), new AttackMove(MoveId.GRAV_APPLE, PokemonType.GRASS, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 8) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1) - .attr(MovePowerMultiplierAttr, (user, target, move) => globalScene.arena.getTag(ArenaTagType.GRAVITY) ? 1.5 : 1) + .attr(StatStageChangeAttr, [Stat.DEF], -1) + .attr(MovePowerMultiplierAttr, (_user, _target, _move) => + globalScene.arena.getTag(ArenaTagType.GRAVITY) ? 1.5 : 1, + ) .makesContact(false), - new AttackMove(MoveId.SPIRIT_BREAK, PokemonType.FAIRY, MoveCategory.PHYSICAL, 75, 100, 15, 100, 0, 8) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -1), - new AttackMove(MoveId.STRANGE_STEAM, PokemonType.FAIRY, MoveCategory.SPECIAL, 90, 95, 10, 20, 0, 8) + new AttackMove(MoveId.SPIRIT_BREAK, PokemonType.FAIRY, MoveCategory.PHYSICAL, 75, 100, 15, 100, 0, 8) // + .attr(StatStageChangeAttr, [Stat.SPATK], -1), + new AttackMove(MoveId.STRANGE_STEAM, PokemonType.FAIRY, MoveCategory.SPECIAL, 90, 95, 10, 20, 0, 8) // .attr(ConfuseAttr), new StatusMove(MoveId.LIFE_DEW, PokemonType.WATER, -1, 10, -1, 0, 8) .attr(HealAttr, 0.25, true, false) @@ -11112,24 +11831,30 @@ export function initMoves() { new AttackMove(MoveId.METEOR_ASSAULT, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 5, -1, 0, 8) .attr(RechargeAttr) .makesContact(false), - new AttackMove(MoveId.ETERNABEAM, PokemonType.DRAGON, MoveCategory.SPECIAL, 160, 90, 5, -1, 0, 8) + new AttackMove(MoveId.ETERNABEAM, PokemonType.DRAGON, MoveCategory.SPECIAL, 160, 90, 5, -1, 0, 8) // .attr(RechargeAttr), - new AttackMove(MoveId.STEEL_BEAM, PokemonType.STEEL, MoveCategory.SPECIAL, 140, 95, 5, -1, 0, 8) + new AttackMove(MoveId.STEEL_BEAM, PokemonType.STEEL, MoveCategory.SPECIAL, 140, 95, 5, -1, 0, 8) // .attr(HalfSacrificialAttr), new AttackMove(MoveId.EXPANDING_FORCE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 8) - .attr(MovePowerMultiplierAttr, (user, target, move) => globalScene.arena.getTerrainType() === TerrainType.PSYCHIC && user.isGrounded() ? 1.5 : 1) - .attr(VariableTargetAttr, (user, target, move) => globalScene.arena.getTerrainType() === TerrainType.PSYCHIC && user.isGrounded() ? MoveTarget.ALL_NEAR_ENEMIES : MoveTarget.NEAR_OTHER), + .attr(MovePowerMultiplierAttr, (user, _target, _move) => + globalScene.arena.getTerrainType() === TerrainType.PSYCHIC && user.isGrounded() ? 1.5 : 1, + ) + .attr(VariableTargetAttr, (user, _target, _move) => + globalScene.arena.getTerrainType() === TerrainType.PSYCHIC && user.isGrounded() + ? MoveTarget.ALL_NEAR_ENEMIES + : MoveTarget.NEAR_OTHER, + ), new AttackMove(MoveId.STEEL_ROLLER, PokemonType.STEEL, MoveCategory.PHYSICAL, 130, 100, 5, -1, 0, 8) .attr(ClearTerrainAttr) .condition(() => !!globalScene.arena.terrain, 3), new AttackMove(MoveId.SCALE_SHOT, PokemonType.DRAGON, MoveCategory.PHYSICAL, 25, 90, 20, -1, 0, 8) - .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true, { lastHitOnly: true }) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1, true, { lastHitOnly: true }) + .attr(StatStageChangeAttr, [Stat.SPD], 1, true, { lastHitOnly: true }) + .attr(StatStageChangeAttr, [Stat.DEF], -1, true, { lastHitOnly: true }) .attr(MultiHitAttr) .makesContact(false), new ChargingAttackMove(MoveId.METEOR_BEAM, PokemonType.ROCK, MoveCategory.SPECIAL, 120, 90, 10, -1, 0, 8) .chargeText(i18next.t("moveTriggers:isOverflowingWithSpacePower", { pokemonName: "{USER}" })) - .chargeAttr(StatStageChangeAttr, [ Stat.SPATK ], 1, true), + .chargeAttr(StatStageChangeAttr, [Stat.SPATK], 1, true), new AttackMove(MoveId.SHELL_SIDE_ARM, PokemonType.POISON, MoveCategory.SPECIAL, 90, 100, 10, 20, 0, 8) .attr(ShellSideArmCategoryAttr) .attr(StatusEffectAttr, StatusEffect.POISON) @@ -11137,24 +11862,33 @@ export function initMoves() { new AttackMove(MoveId.MISTY_EXPLOSION, PokemonType.FAIRY, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 8) .attr(SacrificialAttr) .target(MoveTarget.ALL_NEAR_OTHERS) - .attr(MovePowerMultiplierAttr, (user, target, move) => globalScene.arena.getTerrainType() === TerrainType.MISTY && user.isGrounded() ? 1.5 : 1) + .attr(MovePowerMultiplierAttr, (user, _target, _move) => + globalScene.arena.getTerrainType() === TerrainType.MISTY && user.isGrounded() ? 1.5 : 1, + ) .condition(failIfDampCondition, 3) .makesContact(false), - new AttackMove(MoveId.GRASSY_GLIDE, PokemonType.GRASS, MoveCategory.PHYSICAL, 55, 100, 20, -1, 0, 8) - .attr(IncrementMovePriorityAttr, (user, target, move) => globalScene.arena.getTerrainType() === TerrainType.GRASSY && user.isGrounded()), - new AttackMove(MoveId.RISING_VOLTAGE, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 8) - .attr(MovePowerMultiplierAttr, (user, target, move) => globalScene.arena.getTerrainType() === TerrainType.ELECTRIC && target.isGrounded() ? 2 : 1), + new AttackMove(MoveId.GRASSY_GLIDE, PokemonType.GRASS, MoveCategory.PHYSICAL, 55, 100, 20, -1, 0, 8) // + .attr( + IncrementMovePriorityAttr, + (user, _target, _move) => globalScene.arena.getTerrainType() === TerrainType.GRASSY && user.isGrounded(), + ), + new AttackMove(MoveId.RISING_VOLTAGE, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 8) // + .attr(MovePowerMultiplierAttr, (_user, target, _move) => + globalScene.arena.getTerrainType() === TerrainType.ELECTRIC && target.isGrounded() ? 2 : 1, + ), new AttackMove(MoveId.TERRAIN_PULSE, PokemonType.NORMAL, MoveCategory.SPECIAL, 50, 100, 10, -1, 0, 8) .attr(TerrainPulseTypeAttr) - .attr(MovePowerMultiplierAttr, (user, target, move) => globalScene.arena.getTerrainType() !== TerrainType.NONE && user.isGrounded() ? 2 : 1) + .attr(MovePowerMultiplierAttr, (user, _target, _move) => + globalScene.arena.getTerrainType() !== TerrainType.NONE && user.isGrounded() ? 2 : 1, + ) .pulseMove(), - new AttackMove(MoveId.SKITTER_SMACK, PokemonType.BUG, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -1), + new AttackMove(MoveId.SKITTER_SMACK, PokemonType.BUG, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8) // + .attr(StatStageChangeAttr, [Stat.SPATK], -1), new AttackMove(MoveId.BURNING_JEALOUSY, PokemonType.FIRE, MoveCategory.SPECIAL, 70, 100, 5, 100, 0, 8) .attr(StatusIfBoostedAttr, StatusEffect.BURN) .target(MoveTarget.ALL_NEAR_ENEMIES), - new AttackMove(MoveId.LASH_OUT, PokemonType.DARK, MoveCategory.PHYSICAL, 75, 100, 5, -1, 0, 8) - .attr(MovePowerMultiplierAttr, (user, _target, _move) => user.turnData.statStagesDecreased ? 2 : 1), + new AttackMove(MoveId.LASH_OUT, PokemonType.DARK, MoveCategory.PHYSICAL, 75, 100, 5, -1, 0, 8) // + .attr(MovePowerMultiplierAttr, (user, _target, _move) => (user.turnData.statStagesDecreased ? 2 : 1)), new AttackMove(MoveId.POLTERGEIST, PokemonType.GHOST, MoveCategory.PHYSICAL, 110, 90, 5, -1, 0, 8) .condition(failIfNoTargetHeldItemsCondition, 3) .attr(PreMoveMessageAttr, attackedByItemMessageFunc) @@ -11164,16 +11898,16 @@ export function initMoves() { .reflectable() .unimplemented(), new StatusMove(MoveId.COACHING, PokemonType.FIGHTING, -1, 10, -1, 0, 8) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], 1) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.DEF], 1) .target(MoveTarget.NEAR_ALLY) .condition(failIfSingleBattle), - new AttackMove(MoveId.FLIP_TURN, PokemonType.WATER, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 8) + new AttackMove(MoveId.FLIP_TURN, PokemonType.WATER, MoveCategory.PHYSICAL, 60, 100, 20, -1, 0, 8) // .attr(ForceSwitchOutAttr, true), new AttackMove(MoveId.TRIPLE_AXEL, PokemonType.ICE, MoveCategory.PHYSICAL, 20, 90, 10, -1, 0, 8) .attr(MultiHitAttr, MultiHitType._3) .attr(MultiHitPowerIncrementAttr, 3) .checkAllHits(), - new AttackMove(MoveId.DUAL_WINGBEAT, PokemonType.FLYING, MoveCategory.PHYSICAL, 40, 90, 10, -1, 0, 8) + new AttackMove(MoveId.DUAL_WINGBEAT, PokemonType.FLYING, MoveCategory.PHYSICAL, 40, 90, 10, -1, 0, 8) // .attr(MultiHitAttr, MultiHitType._2), new AttackMove(MoveId.SCORCHING_SANDS, PokemonType.GROUND, MoveCategory.SPECIAL, 70, 100, 10, 30, 0, 8) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) @@ -11191,30 +11925,30 @@ export function initMoves() { .attr(MultiHitAttr, MultiHitType._3) .attr(CritOnlyAttr) .punchingMove(), - new AttackMove(MoveId.THUNDER_CAGE, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 80, 90, 15, -1, 0, 8) + new AttackMove(MoveId.THUNDER_CAGE, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 80, 90, 15, -1, 0, 8) // .attr(TrapAttr, BattlerTagType.THUNDER_CAGE), new AttackMove(MoveId.DRAGON_ENERGY, PokemonType.DRAGON, MoveCategory.SPECIAL, 150, 100, 5, -1, 0, 8) .attr(HpPowerAttr) .target(MoveTarget.ALL_NEAR_ENEMIES), - new AttackMove(MoveId.FREEZING_GLARE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 8) + new AttackMove(MoveId.FREEZING_GLARE, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 90, 100, 10, 10, 0, 8) // .attr(StatusEffectAttr, StatusEffect.FREEZE), new AttackMove(MoveId.FIERY_WRATH, PokemonType.DARK, MoveCategory.SPECIAL, 90, 100, 10, 20, 0, 8) .attr(FlinchAttr) .target(MoveTarget.ALL_NEAR_ENEMIES), - new AttackMove(MoveId.THUNDEROUS_KICK, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 10, 100, 0, 8) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1), + new AttackMove(MoveId.THUNDEROUS_KICK, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 10, 100, 0, 8) // + .attr(StatStageChangeAttr, [Stat.DEF], -1), new AttackMove(MoveId.GLACIAL_LANCE, PokemonType.ICE, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 8) .target(MoveTarget.ALL_NEAR_ENEMIES) .makesContact(false), - new AttackMove(MoveId.ASTRAL_BARRAGE, PokemonType.GHOST, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 8) + new AttackMove(MoveId.ASTRAL_BARRAGE, PokemonType.GHOST, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 8) // .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(MoveId.EERIE_SPELL, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 5, 100, 0, 8) .attr(AttackReducePpMoveAttr, 3) .soundBased(), - new AttackMove(MoveId.DIRE_CLAW, PokemonType.POISON, MoveCategory.PHYSICAL, 80, 100, 15, 50, 0, 8) - .attr(MultiStatusEffectAttr, [ StatusEffect.POISON, StatusEffect.PARALYSIS, StatusEffect.SLEEP ]), - new AttackMove(MoveId.PSYSHIELD_BASH, PokemonType.PSYCHIC, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8) - .attr(StatStageChangeAttr, [ Stat.DEF ], 1, true), + new AttackMove(MoveId.DIRE_CLAW, PokemonType.POISON, MoveCategory.PHYSICAL, 80, 100, 15, 50, 0, 8) // + .attr(MultiStatusEffectAttr, [StatusEffect.POISON, StatusEffect.PARALYSIS, StatusEffect.SLEEP]), + new AttackMove(MoveId.PSYSHIELD_BASH, PokemonType.PSYCHIC, MoveCategory.PHYSICAL, 70, 90, 10, 100, 0, 8) // + .attr(StatStageChangeAttr, [Stat.DEF], 1, true), new SelfStatusMove(MoveId.POWER_SHIFT, PokemonType.NORMAL, -1, 10, -1, 0, 8) .target(MoveTarget.USER) .attr(ShiftStatAttr, Stat.ATK, Stat.DEF), @@ -11222,11 +11956,11 @@ export function initMoves() { .attr(AddArenaTrapTagHitAttr, ArenaTagType.STEALTH_ROCK) .slicingMove(), new AttackMove(MoveId.SPRINGTIDE_STORM, PokemonType.FAIRY, MoveCategory.SPECIAL, 100, 80, 5, 30, 0, 8) - .attr(StatStageChangeAttr, [ Stat.ATK ], -1) + .attr(StatStageChangeAttr, [Stat.ATK], -1) .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), - new AttackMove(MoveId.MYSTICAL_POWER, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 70, 90, 10, 100, 0, 8) - .attr(StatStageChangeAttr, [ Stat.SPATK ], 1, true), + new AttackMove(MoveId.MYSTICAL_POWER, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 70, 90, 10, 100, 0, 8) // + .attr(StatStageChangeAttr, [Stat.SPATK], 1, true), new AttackMove(MoveId.RAGING_FURY, PokemonType.FIRE, MoveCategory.PHYSICAL, 120, 100, 10, -1, 0, 8) .makesContact(false) .attr(FrenzyAttr) @@ -11236,42 +11970,46 @@ export function initMoves() { new AttackMove(MoveId.WAVE_CRASH, PokemonType.WATER, MoveCategory.PHYSICAL, 120, 100, 10, -1, 0, 8) .attr(RecoilAttr, false, 0.33) .recklessMove(), - new AttackMove(MoveId.CHLOROBLAST, PokemonType.GRASS, MoveCategory.SPECIAL, 150, 95, 5, -1, 0, 8) + new AttackMove(MoveId.CHLOROBLAST, PokemonType.GRASS, MoveCategory.SPECIAL, 150, 95, 5, -1, 0, 8) // .attr(RecoilAttr, true, 0.5), new AttackMove(MoveId.MOUNTAIN_GALE, PokemonType.ICE, MoveCategory.PHYSICAL, 100, 85, 10, 30, 0, 8) .makesContact(false) .attr(FlinchAttr), new SelfStatusMove(MoveId.VICTORY_DANCE, PokemonType.FIGHTING, -1, 10, -1, 0, 8) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPD ], 1, true) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.DEF, Stat.SPD], 1, true) .danceMove(), new AttackMove(MoveId.HEADLONG_RUSH, PokemonType.GROUND, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 8) - .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], -1, true) + .attr(StatStageChangeAttr, [Stat.DEF, Stat.SPDEF], -1, true) .punchingMove(), new AttackMove(MoveId.BARB_BARRAGE, PokemonType.POISON, MoveCategory.PHYSICAL, 60, 100, 10, 50, 0, 8) .makesContact(false) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.status && (target.status.effect === StatusEffect.POISON || target.status.effect === StatusEffect.TOXIC) ? 2 : 1) + .attr(MovePowerMultiplierAttr, (_user, target, _move) => + target.status && (target.status.effect === StatusEffect.POISON || target.status.effect === StatusEffect.TOXIC) + ? 2 + : 1, + ) .attr(StatusEffectAttr, StatusEffect.POISON), new AttackMove(MoveId.ESPER_WING, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 8) .attr(HighCritAttr) - .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true), - new AttackMove(MoveId.BITTER_MALICE, PokemonType.GHOST, MoveCategory.SPECIAL, 75, 100, 10, 100, 0, 8) - .attr(StatStageChangeAttr, [ Stat.ATK ], -1), - new SelfStatusMove(MoveId.SHELTER, PokemonType.STEEL, -1, 10, -1, 0, 8) - .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true), + .attr(StatStageChangeAttr, [Stat.SPD], 1, true), + new AttackMove(MoveId.BITTER_MALICE, PokemonType.GHOST, MoveCategory.SPECIAL, 75, 100, 10, 100, 0, 8) // + .attr(StatStageChangeAttr, [Stat.ATK], -1), + new SelfStatusMove(MoveId.SHELTER, PokemonType.STEEL, -1, 10, -1, 0, 8) // + .attr(StatStageChangeAttr, [Stat.DEF], 2, true), new AttackMove(MoveId.TRIPLE_ARROWS, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 10, 30, 0, 8) .makesContact(false) .attr(HighCritAttr) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1, false, { effectChanceOverride: 50 }) + .attr(StatStageChangeAttr, [Stat.DEF], -1, false, { effectChanceOverride: 50 }) .attr(FlinchAttr), new AttackMove(MoveId.INFERNAL_PARADE, PokemonType.GHOST, MoveCategory.SPECIAL, 60, 100, 15, 30, 0, 8) .attr(StatusEffectAttr, StatusEffect.BURN) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.status ? 2 : 1), + .attr(MovePowerMultiplierAttr, (_user, target, _move) => (target.status ? 2 : 1)), new AttackMove(MoveId.CEASELESS_EDGE, PokemonType.DARK, MoveCategory.PHYSICAL, 65, 90, 15, 100, 0, 8) .attr(AddArenaTrapTagHitAttr, ArenaTagType.SPIKES) .slicingMove(), new AttackMove(MoveId.BLEAKWIND_STORM, PokemonType.FLYING, MoveCategory.SPECIAL, 100, 80, 10, 30, 0, 8) .attr(StormAccuracyAttr) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1) + .attr(StatStageChangeAttr, [Stat.SPD], -1) .windMove() .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(MoveId.WILDBOLT_STORM, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 100, 80, 10, 20, 0, 8) @@ -11290,8 +12028,14 @@ export function initMoves() { .target(MoveTarget.USER_AND_ALLIES) .triageMove(), new SelfStatusMove(MoveId.TAKE_HEART, PokemonType.PSYCHIC, -1, 10, -1, 0, 8) - .attr(StatStageChangeAttr, [ Stat.SPATK, Stat.SPDEF ], 1, true) - .attr(HealStatusEffectAttr, true, [ StatusEffect.PARALYSIS, StatusEffect.POISON, StatusEffect.TOXIC, StatusEffect.BURN, StatusEffect.SLEEP ]), + .attr(StatStageChangeAttr, [Stat.SPATK, Stat.SPDEF], 1, true) + .attr(HealStatusEffectAttr, true, [ + StatusEffect.PARALYSIS, + StatusEffect.POISON, + StatusEffect.TOXIC, + StatusEffect.BURN, + StatusEffect.SLEEP, + ]), /* Unused new AttackMove(MoveId.G_MAX_WILDFIRE, PokemonType.Fire, MoveCategory.PHYSICAL, 10, -1, 10, -1, 0, 8) .target(MoveTarget.ALL_NEAR_ENEMIES) @@ -11397,7 +12141,9 @@ export function initMoves() { .attr(TeraMoveCategoryAttr) .attr(TeraBlastTypeAttr) .attr(TeraBlastPowerAttr) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1, true, { condition: (user, target, move) => user.isTerastallized && user.isOfType(PokemonType.STELLAR) }), + .attr(StatStageChangeAttr, [Stat.ATK, Stat.SPATK], -1, true, { + condition: (user, _target, _move) => user.isTerastallized && user.isOfType(PokemonType.STELLAR), + }), new SelfStatusMove(MoveId.SILK_TRAP, PokemonType.BUG, -1, 10, -1, 4, 9) .attr(ProtectAttr, BattlerTagType.SILK_TRAP) .condition(failIfLastCondition, 3), @@ -11407,25 +12153,29 @@ export function initMoves() { .attr(ConfuseAttr) .recklessMove(), new AttackMove(MoveId.LAST_RESPECTS, PokemonType.GHOST, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 9) - .attr(MovePowerMultiplierAttr, (user, target, move) => 1 + Math.min(user.isPlayer() ? globalScene.arena.playerFaints : globalScene.currentBattle.enemyFaints, 100)) + .attr( + MovePowerMultiplierAttr, + (user, _target, _move) => + 1 + Math.min(user.isPlayer() ? globalScene.arena.playerFaints : globalScene.currentBattle.enemyFaints, 100), + ) .makesContact(false), - new AttackMove(MoveId.LUMINA_CRASH, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9) - .attr(StatStageChangeAttr, [ Stat.SPDEF ], -2), + new AttackMove(MoveId.LUMINA_CRASH, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9) // + .attr(StatStageChangeAttr, [Stat.SPDEF], -2), new AttackMove(MoveId.ORDER_UP, PokemonType.DRAGON, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 9) .attr(OrderUpStatBoostAttr) .makesContact(false), - new AttackMove(MoveId.JET_PUNCH, PokemonType.WATER, MoveCategory.PHYSICAL, 60, 100, 15, -1, 1, 9) + new AttackMove(MoveId.JET_PUNCH, PokemonType.WATER, MoveCategory.PHYSICAL, 60, 100, 15, -1, 1, 9) // .punchingMove(), new StatusMove(MoveId.SPICY_EXTRACT, PokemonType.GRASS, -1, 15, -1, 0, 9) - .attr(StatStageChangeAttr, [ Stat.ATK ], 2) - .attr(StatStageChangeAttr, [ Stat.DEF ], -2), - new AttackMove(MoveId.SPIN_OUT, PokemonType.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) - .attr(StatStageChangeAttr, [ Stat.SPD ], -2, true), + .attr(StatStageChangeAttr, [Stat.ATK], 2) + .attr(StatStageChangeAttr, [Stat.DEF], -2), + new AttackMove(MoveId.SPIN_OUT, PokemonType.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) // + .attr(StatStageChangeAttr, [Stat.SPD], -2, true), new AttackMove(MoveId.POPULATION_BOMB, PokemonType.NORMAL, MoveCategory.PHYSICAL, 20, 90, 10, -1, 0, 9) .attr(MultiHitAttr, MultiHitType._10) .slicingMove() .checkAllHits(), - new AttackMove(MoveId.ICE_SPINNER, PokemonType.ICE, MoveCategory.PHYSICAL, 80, 100, 15, -1, 0, 9) + new AttackMove(MoveId.ICE_SPINNER, PokemonType.ICE, MoveCategory.PHYSICAL, 80, 100, 15, -1, 0, 9) // .attr(ClearTerrainAttr), new AttackMove(MoveId.GLAIVE_RUSH, PokemonType.DRAGON, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 9) .attr(AddBattlerTagAttr, BattlerTagType.ALWAYS_GET_HIT, true, false, 0, 0, true) @@ -11437,66 +12187,78 @@ export function initMoves() { new AttackMove(MoveId.SALT_CURE, PokemonType.ROCK, MoveCategory.PHYSICAL, 40, 100, 15, 100, 0, 9) .attr(AddBattlerTagAttr, BattlerTagType.SALT_CURED) .makesContact(false), - new AttackMove(MoveId.TRIPLE_DIVE, PokemonType.WATER, MoveCategory.PHYSICAL, 30, 95, 10, -1, 0, 9) + new AttackMove(MoveId.TRIPLE_DIVE, PokemonType.WATER, MoveCategory.PHYSICAL, 30, 95, 10, -1, 0, 9) // .attr(MultiHitAttr, MultiHitType._3), new AttackMove(MoveId.MORTAL_SPIN, PokemonType.POISON, MoveCategory.PHYSICAL, 30, 100, 15, 100, 0, 9) - .attr(RemoveBattlerTagAttr, [ - BattlerTagType.BIND, - BattlerTagType.WRAP, - BattlerTagType.FIRE_SPIN, - BattlerTagType.WHIRLPOOL, - BattlerTagType.CLAMP, - BattlerTagType.SAND_TOMB, - BattlerTagType.MAGMA_STORM, - BattlerTagType.SNAP_TRAP, - BattlerTagType.THUNDER_CAGE, - BattlerTagType.SEEDED, - BattlerTagType.INFESTATION - ], true) + .attr( + RemoveBattlerTagAttr, + [ + BattlerTagType.BIND, + BattlerTagType.WRAP, + BattlerTagType.FIRE_SPIN, + BattlerTagType.WHIRLPOOL, + BattlerTagType.CLAMP, + BattlerTagType.SAND_TOMB, + BattlerTagType.MAGMA_STORM, + BattlerTagType.SNAP_TRAP, + BattlerTagType.THUNDER_CAGE, + BattlerTagType.SEEDED, + BattlerTagType.INFESTATION, + ], + true, + ) .attr(StatusEffectAttr, StatusEffect.POISON) .attr(RemoveArenaTrapAttr) .target(MoveTarget.ALL_NEAR_ENEMIES), - new StatusMove(MoveId.DOODLE, PokemonType.NORMAL, 100, 10, -1, 0, 9) + new StatusMove(MoveId.DOODLE, PokemonType.NORMAL, 100, 10, -1, 0, 9) // .attr(AbilityCopyAttr, true), new SelfStatusMove(MoveId.FILLET_AWAY, PokemonType.NORMAL, -1, 10, -1, 0, 9) - .attr(CutHpStatStageBoostAttr, [ Stat.ATK, Stat.SPATK, Stat.SPD ], 2, 2) + .attr(CutHpStatStageBoostAttr, [Stat.ATK, Stat.SPATK, Stat.SPD], 2, 2) .condition(new FailIfInsufficientHpCondition(2), 3), - new AttackMove(MoveId.KOWTOW_CLEAVE, PokemonType.DARK, MoveCategory.PHYSICAL, 85, -1, 10, -1, 0, 9) + new AttackMove(MoveId.KOWTOW_CLEAVE, PokemonType.DARK, MoveCategory.PHYSICAL, 85, -1, 10, -1, 0, 9) // .slicingMove(), new AttackMove(MoveId.FLOWER_TRICK, PokemonType.GRASS, MoveCategory.PHYSICAL, 70, -1, 10, -1, 0, 9) .attr(CritOnlyAttr) .makesContact(false), new AttackMove(MoveId.TORCH_SONG, PokemonType.FIRE, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9) - .attr(StatStageChangeAttr, [ Stat.SPATK ], 1, true) + .attr(StatStageChangeAttr, [Stat.SPATK], 1, true) .soundBased(), new AttackMove(MoveId.AQUA_STEP, PokemonType.WATER, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 9) - .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true) + .attr(StatStageChangeAttr, [Stat.SPD], 1, true) .danceMove(), new AttackMove(MoveId.RAGING_BULL, PokemonType.NORMAL, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 9) .attr(RagingBullTypeAttr) .attr(RemoveScreensAttr), new AttackMove(MoveId.MAKE_IT_RAIN, PokemonType.STEEL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9) .attr(MoneyAttr) - .attr(StatStageChangeAttr, [ Stat.SPATK ], -1, true, { firstTargetOnly: true }) + .attr(StatStageChangeAttr, [Stat.SPATK], -1, true, { firstTargetOnly: true }) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(MoveId.PSYBLADE, PokemonType.PSYCHIC, MoveCategory.PHYSICAL, 80, 100, 15, -1, 0, 9) - .attr(MovePowerMultiplierAttr, (user, target, move) => globalScene.arena.getTerrainType() === TerrainType.ELECTRIC && user.isGrounded() ? 1.5 : 1) + .attr(MovePowerMultiplierAttr, (user, _target, _move) => + globalScene.arena.getTerrainType() === TerrainType.ELECTRIC && user.isGrounded() ? 1.5 : 1, + ) .slicingMove(), new AttackMove(MoveId.HYDRO_STEAM, PokemonType.WATER, MoveCategory.SPECIAL, 80, 100, 15, -1, 0, 9) .attr(IgnoreWeatherTypeDebuffAttr, WeatherType.SUNNY) - .attr(MovePowerMultiplierAttr, (user, target, move) => { + .attr(MovePowerMultiplierAttr, (_user, _target, _move) => { const weather = globalScene.arena.weather; if (!weather) { return 1; } - return [ WeatherType.SUNNY, WeatherType.HARSH_SUN ].includes(weather.weatherType) && !weather.isEffectSuppressed() ? 1.5 : 1; + return [WeatherType.SUNNY, WeatherType.HARSH_SUN].includes(weather.weatherType) && !weather.isEffectSuppressed() + ? 1.5 + : 1; }), - new AttackMove(MoveId.RUINATION, PokemonType.DARK, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 9) + new AttackMove(MoveId.RUINATION, PokemonType.DARK, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 9) // .attr(TargetHalfHpDamageAttr), - new AttackMove(MoveId.COLLISION_COURSE, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2 ? 4 / 3 : 1), + new AttackMove(MoveId.COLLISION_COURSE, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) // + .attr(MovePowerMultiplierAttr, (user, target, move) => + target.getAttackTypeEffectiveness(move.type, user) >= 2 ? 4 / 3 : 1, + ), new AttackMove(MoveId.ELECTRO_DRIFT, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 9) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2 ? 4 / 3 : 1) + .attr(MovePowerMultiplierAttr, (user, target, move) => + target.getAttackTypeEffectiveness(move.type, user) >= 2 ? 4 / 3 : 1, + ) .makesContact(), new SelfStatusMove(MoveId.SHED_TAIL, PokemonType.NORMAL, -1, 10, -1, 0, 9) .attr(AddSubstituteAttr, 0.5, true) @@ -11507,30 +12269,31 @@ export function initMoves() { // Don't display text if current move phase is follow up (ie move called indirectly) isVirtual((globalScene.phaseManager.getCurrentPhase() as MovePhase).useMode) ? "" - : i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(user) })) + : i18next.t("moveTriggers:chillyReception", { pokemonName: getPokemonNameWithAffix(user) }), + ) .attr(ChillyReceptionAttr, true), new SelfStatusMove(MoveId.TIDY_UP, PokemonType.NORMAL, -1, 10, -1, 0, 9) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPD ], 1, true) + .attr(StatStageChangeAttr, [Stat.ATK, Stat.SPD], 1, true) .attr(RemoveArenaTrapAttr, true) .attr(RemoveAllSubstitutesAttr), new StatusMove(MoveId.SNOWSCAPE, PokemonType.ICE, -1, 10, -1, 0, 9) .attr(WeatherChangeAttr, WeatherType.SNOW) .target(MoveTarget.BOTH_SIDES), - new AttackMove(MoveId.POUNCE, PokemonType.BUG, MoveCategory.PHYSICAL, 50, 100, 20, 100, 0, 9) - .attr(StatStageChangeAttr, [ Stat.SPD ], -1), - new AttackMove(MoveId.TRAILBLAZE, PokemonType.GRASS, MoveCategory.PHYSICAL, 50, 100, 20, 100, 0, 9) - .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true), - new AttackMove(MoveId.CHILLING_WATER, PokemonType.WATER, MoveCategory.SPECIAL, 50, 100, 20, 100, 0, 9) - .attr(StatStageChangeAttr, [ Stat.ATK ], -1), - new AttackMove(MoveId.HYPER_DRILL, PokemonType.NORMAL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) + new AttackMove(MoveId.POUNCE, PokemonType.BUG, MoveCategory.PHYSICAL, 50, 100, 20, 100, 0, 9) // + .attr(StatStageChangeAttr, [Stat.SPD], -1), + new AttackMove(MoveId.TRAILBLAZE, PokemonType.GRASS, MoveCategory.PHYSICAL, 50, 100, 20, 100, 0, 9) // + .attr(StatStageChangeAttr, [Stat.SPD], 1, true), + new AttackMove(MoveId.CHILLING_WATER, PokemonType.WATER, MoveCategory.SPECIAL, 50, 100, 20, 100, 0, 9) // + .attr(StatStageChangeAttr, [Stat.ATK], -1), + new AttackMove(MoveId.HYPER_DRILL, PokemonType.NORMAL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) // .ignoresProtect(), - new AttackMove(MoveId.TWIN_BEAM, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 40, 100, 10, -1, 0, 9) + new AttackMove(MoveId.TWIN_BEAM, PokemonType.PSYCHIC, MoveCategory.SPECIAL, 40, 100, 10, -1, 0, 9) // .attr(MultiHitAttr, MultiHitType._2), new AttackMove(MoveId.RAGE_FIST, PokemonType.GHOST, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 9) .attr(RageFistPowerAttr) .punchingMove(), - new AttackMove(MoveId.ARMOR_CANNON, PokemonType.FIRE, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9) - .attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], -1, true), + new AttackMove(MoveId.ARMOR_CANNON, PokemonType.FIRE, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9) // + .attr(StatStageChangeAttr, [Stat.DEF, Stat.SPDEF], -1, true), new AttackMove(MoveId.BITTER_BLADE, PokemonType.FIRE, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 9) .attr(HitHealAttr) .slicingMove() @@ -11539,8 +12302,10 @@ export function initMoves() { // Pass `true` to `isOfType` to fail if the user is terastallized to a type other than ELECTRIC .condition(user => user.isOfType(PokemonType.ELECTRIC, true, true), 2) .attr(AddBattlerTagAttr, BattlerTagType.DOUBLE_SHOCKED, true, false) - .attr(RemoveTypeAttr, PokemonType.ELECTRIC, (user) => { - globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:usedUpAllElectricity", { pokemonName: getPokemonNameWithAffix(user) })); + .attr(RemoveTypeAttr, PokemonType.ELECTRIC, user => { + globalScene.phaseManager.queueMessage( + i18next.t("moveTriggers:usedUpAllElectricity", { pokemonName: getPokemonNameWithAffix(user) }), + ); }), new AttackMove(MoveId.GIGATON_HAMMER, PokemonType.STEEL, MoveCategory.PHYSICAL, 160, 100, 5, -1, 0, 9) .makesContact(false) @@ -11569,7 +12334,7 @@ export function initMoves() { new AttackMove(MoveId.MAGICAL_TORQUE, PokemonType.FAIRY, MoveCategory.PHYSICAL, 100, 100, 10, 30, 0, 9) .attr(ConfuseAttr) .makesContact(false), - new AttackMove(MoveId.BLOOD_MOON, PokemonType.NORMAL, MoveCategory.SPECIAL, 140, 100, 5, -1, 0, 9) + new AttackMove(MoveId.BLOOD_MOON, PokemonType.NORMAL, MoveCategory.SPECIAL, 140, 100, 5, -1, 0, 9) // .restriction(consecutiveUseRestriction), new AttackMove(MoveId.MATCHA_GOTCHA, PokemonType.GRASS, MoveCategory.SPECIAL, 80, 90, 15, 20, 0, 9) .attr(HitHealAttr) @@ -11587,20 +12352,26 @@ export function initMoves() { .makesContact(false), new ChargingAttackMove(MoveId.ELECTRO_SHOT, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 130, 100, 10, -1, 0, 9) .chargeText(i18next.t("moveTriggers:absorbedElectricity", { pokemonName: "{USER}" })) - .chargeAttr(StatStageChangeAttr, [ Stat.SPATK ], 1, true) - .chargeAttr(WeatherInstantChargeAttr, [ WeatherType.RAIN, WeatherType.HEAVY_RAIN ]), + .chargeAttr(StatStageChangeAttr, [Stat.SPATK], 1, true) + .chargeAttr(WeatherInstantChargeAttr, [WeatherType.RAIN, WeatherType.HEAVY_RAIN]), new AttackMove(MoveId.TERA_STARSTORM, PokemonType.NORMAL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 9) .attr(TeraMoveCategoryAttr) .attr(TeraStarstormTypeAttr) - .attr(VariableTargetAttr, (user, target, move) => user.hasSpecies(SpeciesId.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} */ + .attr(VariableTargetAttr, (user, _target, _move) => + user.hasSpecies(SpeciesId.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 (cf `TeraMoveCategoryAttr`) new AttackMove(MoveId.FICKLE_BEAM, PokemonType.DRAGON, MoveCategory.SPECIAL, 80, 100, 5, -1, 0, 9) .attr(PreMoveMessageAttr, doublePowerChanceMessageFunc(30)) .attr(DoublePowerChanceAttr, 30), new SelfStatusMove(MoveId.BURNING_BULWARK, PokemonType.FIRE, -1, 10, -1, 4, 9) .attr(ProtectAttr, BattlerTagType.BURNING_BULWARK) .condition(failIfLastCondition, 3), - new AttackMove(MoveId.THUNDERCLAP, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, -1, 1, 9) + new AttackMove(MoveId.THUNDERCLAP, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, -1, 1, 9) // .condition(failIfTargetNotAttackingCondition, 3), new AttackMove(MoveId.MIGHTY_CLEAVE, PokemonType.ROCK, MoveCategory.PHYSICAL, 95, 100, 5, -1, 0, 9) .slicingMove() @@ -11608,7 +12379,7 @@ export function initMoves() { new AttackMove(MoveId.TACHYON_CUTTER, PokemonType.STEEL, MoveCategory.SPECIAL, 50, -1, 10, -1, 0, 9) .attr(MultiHitAttr, MultiHitType._2) .slicingMove(), - new AttackMove(MoveId.HARD_PRESS, PokemonType.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 9) + new AttackMove(MoveId.HARD_PRESS, PokemonType.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 9) // .attr(OpponentHighHpPowerAttr, 100), new StatusMove(MoveId.DRAGON_CHEER, PokemonType.DRAGON, -1, 15, -1, 0, 9) .attr(AddBattlerTagAttr, BattlerTagType.DRAGON_CHEER, false, true) @@ -11618,8 +12389,12 @@ export function initMoves() { new AttackMove(MoveId.ALLURING_VOICE, PokemonType.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, 100, 0, 9) .attr(AddBattlerTagIfBoostedAttr, BattlerTagType.CONFUSED) .soundBased(), - new AttackMove(MoveId.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(MoveId.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(MoveId.SUPERCELL_SLAM, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 100, 95, 15, -1, 0, 9) .attr(AlwaysHitMinimizeAttr) .attr(HitsTagForDoubleDamageAttr, BattlerTagType.MINIMIZED) @@ -11632,7 +12407,7 @@ export function initMoves() { new AttackMove(MoveId.UPPER_HAND, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, 100, 3, 9) .attr(FlinchAttr) .condition(upperHandCondition, 3), - new AttackMove(MoveId.MALIGNANT_CHAIN, PokemonType.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9) - .attr(StatusEffectAttr, StatusEffect.TOXIC) + new AttackMove(MoveId.MALIGNANT_CHAIN, PokemonType.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9) // + .attr(StatusEffectAttr, StatusEffect.TOXIC), ); }