PR review changes

Fix type hints test name

Update Dig/Dive test name

Separate TSDoc imports in `pokemon-utils.ts`

Add missing `@returns` in `move-phase.ts`

Fix comment typos

Separate TSDoc imports in `move-phase.ts`

Add return hints to `trySelectMove`

Minor formatting

Remove duplicate `.affectedByGravity()` on Telekinesis

Fix docs for `checkRestrictions`

Manually format method definition

Fix comment spacing

Fix variable naming
This commit is contained in:
NightKev 2025-08-19 17:23:11 -07:00 committed by Sirz Benjie
parent 2eed38dc55
commit 9e49162012
No known key found for this signature in database
GPG Key ID: 4A524B4D196C759E
8 changed files with 50 additions and 49 deletions

View File

@ -156,7 +156,7 @@ export const failAgainstFinalBossCondition = new MoveCondition((_user, target) =
* a high-priority attack (after factoring in priority-boosting effects) and
* hasn't moved yet this turn.
*/
export const UpperHandCondition = new MoveCondition((_user, target) => {
export const upperHandCondition = new MoveCondition((_user, target) => {
const targetCommand = globalScene.currentBattle.turnCommands[target.getBattlerIndex()];
return (
targetCommand?.command === Command.FIGHT &&
@ -252,13 +252,13 @@ export class MoveRestriction {
* @remarks
* Used by {@link https://bulbapedia.bulbagarden.net/wiki/Blood_Moon_(move) | Blood Moon} and {@link https://bulbapedia.bulbagarden.net/wiki/Gigaton_Hammer_(move) | Gigaton Hammer}
*/
export const ConsecutiveUseRestriction = new MoveRestriction(
export const consecutiveUseRestriction = new MoveRestriction(
(user, move) => user.getLastXMoves(1)[0]?.move === move.id,
"battle:moveDisabledConsecutive",
);
/** Prevents a move from being selected if Gravity is in effect */
export const GravityUseRestriction = new MoveRestriction(
export const gravityUseRestriction = new MoveRestriction(
() => globalScene.arena.hasTag(ArenaTagType.GRAVITY),
"battle:moveDisabledGravity",
);

View File

@ -82,7 +82,7 @@ import {
} 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 { consecutiveUseRestriction, counterAttackConditionBoth, counterAttackConditionPhysical, counterAttackConditionSpecial, failAgainstFinalBossCondition, FailIfInsufficientHpCondition, failIfTargetNotAttackingCondition, failTeleportCondition, FirstMoveCondition, 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";
@ -172,8 +172,14 @@ 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[] = [];
<<<<<<< HEAD
/** Conditions that must be false for a move to be able to be selected.
*
=======
/**
* Conditions that must be false for a move to be able to be selected.
*
>>>>>>> 02f941b3a05 (PR review changes)
* @remarks Different from {@linkcode conditions}, which is checked when the move is invoked
*/
private restrictions: MoveRestriction[] = [];
@ -476,7 +482,12 @@ export abstract class Move implements Localizable {
* is false
* @returns `this` for method chaining
*/
public restriction<T extends UserMoveConditionFunc | MoveRestriction>(restriction: T, i18nkey?: string, alsoCondition: typeof restriction extends MoveRestriction ? false : boolean = false, conditionSeq = 4): this {
public restriction<T extends UserMoveConditionFunc | MoveRestriction>(
restriction: T,
i18nkey?: string,
alsoCondition: typeof restriction extends MoveRestriction ? false : boolean = false,
conditionSeq = 4,
): this {
if (typeof restriction === "function") {
this.restrictions.push(new MoveRestriction(restriction));
if (alsoCondition) {
@ -700,7 +711,7 @@ export abstract class Move implements Localizable {
}
/**
* Sets the {@linkcode MoveFlags.GRAVITY} flag for the calling Move and adds {@linkcode GravityUseRestriction} to the
* Sets the {@linkcode MoveFlags.GRAVITY} flag for the calling Move and adds {@linkcode gravityUseRestriction} to the
* move's restrictions.
*
* @returns `this`
@ -713,7 +724,7 @@ export abstract class Move implements Localizable {
*/
affectedByGravity(): this {
this.setFlag(MoveFlags.GRAVITY, true);
this.restrictions.push(GravityUseRestriction);
this.restrictions.push(gravityUseRestriction);
return this;
}
@ -859,9 +870,9 @@ export abstract class Move implements Localizable {
*
* @param user - The Pokemon using the move
* @returns - An array whose first element is `false` if the move is restricted, and the second element is a string
* with the reason for the restriction, otherwise, `false` and the empty string.
* with the reason for the restriction, otherwise, `true` and the empty string.
*/
public checkRestrictions(user: Pokemon): [boolean, string] {
public checkRestrictions(user: Pokemon): [isUsable: boolean, restrictionMessage: string] {
for (const restriction of this.restrictions) {
if (restriction.apply(user, this)) {
return [false, restriction.getSelectionDeniedText(user, this)];
@ -10034,7 +10045,6 @@ export function initMoves() {
.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)
.affectedByGravity()
.reflectable(),
new StatusMove(MoveId.MAGIC_ROOM, PokemonType.PSYCHIC, -1, 10, -1, 0, 5)
.ignoresProtect()
@ -10677,10 +10687,7 @@ export function initMoves() {
.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.isOpponent(user) || !!target.getTag(BattlerTagType.HEAL_BLOCK),
2
),
.condition((user, target) => 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)
@ -10693,11 +10700,8 @@ export function initMoves() {
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)
.condition(
// Pass `true` to `ForDefend` as it should fail if the user is terastallized to a type that is not FIRE
user => user.isOfType(PokemonType.FIRE, true, true),
2
)
.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) => {
@ -10798,10 +10802,7 @@ export function initMoves() {
.attr(AddBattlerTagHeaderAttr, BattlerTagType.SHELL_TRAP)
.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
),
.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.PSYCHIC_FANGS, PokemonType.PSYCHIC, MoveCategory.PHYSICAL, 85, 100, 10, -1, 0, 7)
@ -10949,11 +10950,8 @@ export function initMoves() {
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(AddBattlerTagAttr, BattlerTagType.NO_RETREAT, true, true /* NOT ADDED if already trapped */)
.condition(
// fails if the user is currently trapped specifically from no retreat
user => user.getTag(TrappedTag)?.tagType !== BattlerTagType.NO_RETREAT,
2
),
.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(AddBattlerTagAttr, BattlerTagType.TAR_SHOT, false)
@ -11515,18 +11513,15 @@ export function initMoves() {
.slicingMove()
.triageMove(),
new AttackMove(MoveId.DOUBLE_SHOCK, PokemonType.ELECTRIC, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 9)
.condition(
// Pass `true` to `isOfType` to fail if the user is terastallized to a type other than ELECTRIC
user => user.isOfType(PokemonType.ELECTRIC, true, true),
2
)
.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) }));
}),
new AttackMove(MoveId.GIGATON_HAMMER, PokemonType.STEEL, MoveCategory.PHYSICAL, 160, 100, 5, -1, 0, 9)
.makesContact(false)
.restriction(ConsecutiveUseRestriction),
.restriction(consecutiveUseRestriction),
new AttackMove(MoveId.COMEUPPANCE, PokemonType.DARK, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 9)
.attr(CounterDamageAttr, 1.5)
.attr(CounterRedirectAttr)
@ -11552,7 +11547,7 @@ export function initMoves() {
.attr(ConfuseAttr)
.makesContact(false),
new AttackMove(MoveId.BLOOD_MOON, PokemonType.NORMAL, MoveCategory.SPECIAL, 140, 100, 5, -1, 0, 9)
.restriction(ConsecutiveUseRestriction),
.restriction(consecutiveUseRestriction),
new AttackMove(MoveId.MATCHA_GOTCHA, PokemonType.GRASS, MoveCategory.SPECIAL, 80, 90, 15, 20, 0, 9)
.attr(HitHealAttr)
.attr(HealStatusEffectAttr, true, StatusEffect.FREEZE)
@ -11613,7 +11608,7 @@ export function initMoves() {
.attr(AddBattlerTagAttr, BattlerTagType.HEAL_BLOCK, false, false, 2),
new AttackMove(MoveId.UPPER_HAND, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, 100, 3, 9)
.attr(FlinchAttr)
.condition(UpperHandCondition, 3),
.condition(upperHandCondition, 3),
new AttackMove(MoveId.MALIGNANT_CHAIN, PokemonType.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9)
.attr(StatusEffectAttr, StatusEffect.TOXIC)
);

View File

@ -3138,7 +3138,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* @param ignorePp - Whether to ignore PP when checking if the move is usable (defaults to false)
* @returns A tuple containing a boolean indicating if the move can be selected, and a string with the reason if it cannot be selected
*/
public trySelectMove(moveIndex: number, ignorePp?: boolean): [boolean, string] {
public trySelectMove(moveIndex: number, ignorePp?: boolean): [isUsable: boolean, failureMessage: string] {
const move = this.getMoveset().length > moveIndex ? this.getMoveset()[moveIndex] : null;
return move?.isUsable(this, ignorePp, true) ?? [false, ""];
}

View File

@ -1,3 +1,7 @@
// biome-ignore-start lint/correctness/noUnusedImports: Used in a tsdoc comment
import type { Move, PreUseInterruptAttr } from "#types/move-types";
// biome-ignore-end lint/correctness/noUnusedImports: Used in a tsdoc comment
import { applyAbAttrs } from "#abilities/apply-ab-attrs";
import { MOVE_COLOR } from "#app/constants/colors";
import { globalScene } from "#app/global-scene";
@ -28,8 +32,6 @@ import type { Pokemon } from "#field/pokemon";
import { applyMoveAttrs } from "#moves/apply-attrs";
import { frenzyMissFunc } from "#moves/move-utils";
import type { PokemonMove } from "#moves/pokemon-move";
// biome-ignore lint/correctness/noUnusedImports: Used in a tsdoc comment
import type { Move, PreUseInterruptAttr } from "#types/move-types";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder, NumberHolder } from "#utils/common";
import { enumValueToKey } from "#utils/enums";
@ -205,7 +207,7 @@ export class MovePhase extends PokemonPhase {
*
* @remarks
* Other than powder, each failure condition is mutually exclusive (as they are tied to specific moves), so order does not matter.
* Notably, this failure check only includes failure conditions intrinsic to the move itself, ther than Powder (which marks the end of this failure check)
* Notably, this failure check only includes failure conditions intrinsic to the move itself, other than Powder (which marks the end of this failure check)
*
*
* - Pollen puff used on an ally that is under effect of heal block
@ -512,13 +514,14 @@ export class MovePhase extends PokemonPhase {
* - Checking if the pokemon will thaw from random chance, OR from a thawing move.
* Thawing from a freeze move is not applied until AFTER all other failure checks.
* - Activating the freeze status effect (cancelling the move, playing the message, and displaying the animation)
* @returns Whether the move was cancelled due to the pokemon being frozen
*/
protected checkFreeze(): boolean {
if (this.pokemon.status?.effect !== StatusEffect.FREEZE) {
return false;
}
// For some reason, dancer will immediately its user
// For some reason, dancer will immediately thaw its user
if (this.useMode === MoveUseMode.INDIRECT) {
this.pokemon.resetStatus(false);
return false;
@ -644,6 +647,7 @@ export class MovePhase extends PokemonPhase {
* Lapse the tag type and check if the move is cancelled from it. Meant to be used during the first failure check
* @param tag - The tag type whose lapse method will be called with {@linkcode BattlerTagLapseType.PRE_MOVE}
* @param checkIgnoreStatus - Whether to check {@link isIgnoreStatus} for the current {@linkcode MoveUseMode} to skip this check
* @returns Whether the move was cancelled due to a `BattlerTag` effect
*/
private checkTagCancel(tag: BattlerTagType): boolean {
this.pokemon.lapseTag(tag, BattlerTagLapseType.PRE_MOVE);
@ -652,7 +656,7 @@ export class MovePhase extends PokemonPhase {
/**
* Handle move failures due to Gravity, cancelling the move and showing the failure text
* @returns - Whether the move was cancelled due to Gravity
* @returns Whether the move was cancelled due to Gravity
*/
private checkGravity(): boolean {
const move = this.move.getMove();

View File

@ -1,11 +1,13 @@
// biome-ignore-start lint/correctness/noUnusedImports: Used in a TSDoc comment
import type { Pokemon } from "#field/pokemon";
// biome-ignore-end lint/correctness/noUnusedImports: Used in a TSDoc comment
import { globalScene } from "#app/global-scene";
import { POKERUS_STARTER_COUNT, speciesStarterCosts } from "#balance/starters";
import { allSpecies } from "#data/data-lists";
import type { PokemonSpecies, PokemonSpeciesForm } from "#data/pokemon-species";
import { BattlerIndex } from "#enums/battler-index";
import type { SpeciesId } from "#enums/species-id";
// biome-ignore lint/correctness/noUnusedImports: Used in a TSDoc comment
import type { Pokemon } from "#field/pokemon";
import { randSeedItem } from "./common";
/**

View File

@ -90,7 +90,7 @@ describe("Moves - Dig", () => {
expect(enemyPokemon.getLastXMoves(1)[0].result).toBe(MoveResult.SUCCESS);
});
it("should expend PP when the attack phase is cancelled", async () => {
it("should expend PP when the attack phase is cancelled by sleep", async () => {
game.override.enemyAbility(AbilityId.NO_GUARD).enemyMoveset(MoveId.SPORE);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);

View File

@ -74,7 +74,7 @@ describe("Moves - Dive", () => {
expect(enemyPokemon.getLastXMoves(1)[0].result).toBe(MoveResult.SUCCESS);
});
it("should expend PP when the attack phase is cancelled", async () => {
it("should expend PP when the attack phase is cancelled by sleep", async () => {
game.override.enemyAbility(AbilityId.NO_GUARD).enemyMoveset(MoveId.SPORE);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);

View File

@ -84,7 +84,7 @@ describe("UI - Type Hints", () => {
await game.phaseInterceptor.to("CommandPhase");
});
it("should show the proper hint for a move in doubles after one of the enemy pokemon flees", async () => {
it("should show the proper hint for a move in doubles after one of the enemy pokemon faints", async () => {
game.override
.enemySpecies(SpeciesId.ABRA)
.moveset([MoveId.SPLASH, MoveId.SHADOW_BALL, MoveId.SOAK])