diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index ce25b56157c..bfbb42de324 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -2782,6 +2782,32 @@ export class ImprisonTag extends MoveRestrictionBattlerTag { } } +export class StuffCheeksTag extends MoveRestrictionBattlerTag { + // Stuff Cheeks + private moveId: Moves = Moves.STUFF_CHEEKS; + /** + * This Tag only lasts the turn the {@linkcode MoveUnselectableTag} was added. + * @param move {@linkcode Moves} that is selected + */ + constructor(move: Moves) { + super(BattlerTagType.STUFF_CHEEKS, BattlerTagLapseType.TURN_END, 0, Moves.STUFF_CHEEKS); + } + + /** + * This function always returns true since {@linkcode MoveUnselectableTag} is only added if condition {@linkcode MoveSelectCondition} fails + * @param move {@linkcode Moves} that is selected + * @returns true if the move matches the ID of Stuff Cheeks + */ + override isMoveRestricted(move: Moves): boolean { + return move === this.moveId; + } + + override selectionDeniedText(pokemon: Pokemon, move: Moves): string { + return i18next.t("battle:moveCannotBeSelected", { moveName: allMoves[move].name }); + } +} + + /** * Battler Tag that applies the effects of Syrup Bomb to the target Pokemon. * For three turns, starting from the turn of hit, at the end of each turn, the target Pokemon's speed will decrease by 1. @@ -3123,6 +3149,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new GrudgeTag(); case BattlerTagType.PSYCHO_SHIFT: return new PsychoShiftTag(); + case BattlerTagType.STUFF_CHEEKS: + return new StuffCheeksTag(sourceMove); case BattlerTagType.NONE: default: return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); diff --git a/src/data/move.ts b/src/data/move.ts index 106e1cb6811..2b916a0414a 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -7539,15 +7539,13 @@ export class MoveSelectCondition { } /** - * {@linkcode func} is being called in order to check if the {@linkcode user} is able to - * select the {@linkcode move} and if the move should fail - * - * @param user {@linkcode Pokemon} that want to use this {@linkcode move} - * @param move {@linkcode Move} being selected - * @returns true if the move can be selected/doesn't fail, otherwise false + * Checks condition and adds appropriate MoveRestrictionTag accordingly + * @param user {@linkcode Pokemon} that uses the move + * @param move {@linkcode Move} that is used + * @returns true if Tag was added successfully */ apply(user: Pokemon, move: Move): boolean { - return this.func(user, move); + return true; } } @@ -7560,10 +7558,36 @@ export class StuffCheeksCondition extends MoveSelectCondition { * contains function that checks if the {@linkcode user} is currently holding a berry or not */ constructor() { - super((user: Pokemon, move: Move) => user.scene.findModifiers(m => m instanceof BerryModifier, user.isPlayer()).length > 0); + super((user, move) => this.selectableCondition(user)); + } + + /** + * Checks if the user is holding a berry + * @param user {@linkcode Pokemon} whose berries to check + * @returns true if the user is holding a berry, otherwise false + */ + private selectableCondition(user: Pokemon): boolean { + return user.scene.findModifiers(m => m instanceof BerryModifier, user.isPlayer()).length > 0; + } + + /** + * {@linkcode func} is being called in order to check if the {@linkcode user} is able to + * select the {@linkcode move} and adds {@linkcode StuffCheeksTag} if condition fails + * + * @param user {@linkcode Pokemon} that want to use this {@linkcode move} + * @param move {@linkcode Move} being selected + * @returns true if the move can be selected/doesn't fail, otherwise false + */ + apply(user: Pokemon, move: Move): boolean { + if (!this.selectableCondition(user)) { + user.addTag(BattlerTagType.STUFF_CHEEKS, 0, move.id); + } + return this.func(user, move); } } +const hasBerryCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => user.scene.findModifiers(m => m instanceof BerryModifier, user.isPlayer()).length > 0; + export class MoveCondition { protected func: MoveConditionFunc; @@ -10057,7 +10081,8 @@ export function initMoves() { new SelfStatusMove(Moves.STUFF_CHEEKS, Type.NORMAL, -1, 10, -1, 0, 8) .attr(EatBerryAttr) .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true) - .selectableCondition(new StuffCheeksCondition()), + .selectableCondition(new StuffCheeksCondition()) + .condition(hasBerryCondition), new SelfStatusMove(Moves.NO_RETREAT, Type.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, false) diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index bb969386630..dd6e395ef05 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -93,4 +93,5 @@ export enum BattlerTagType { GRUDGE = "GRUDGE", PSYCHO_SHIFT = "PSYCHO_SHIFT", ENDURE_TOKEN = "ENDURE_TOKEN", + STUFF_CHEEKS = "STUFF_CHEEKS", } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 79859e058cc..961cb943731 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -5368,10 +5368,6 @@ export class PokemonMove { return false; } - if (!this.isSelectable(pokemon)) { - return false; - } - if (this.getMove().name.endsWith(" (N)")) { return false; } @@ -5379,20 +5375,6 @@ export class PokemonMove { return (ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1); } - /** - * This function checks if the current move can be selected or not - * - * @param pokemon {@linkcode Pokemon} that selected this {@linkcode PokemonMove} - * @returns true if move can be selected, otherwise false - */ - isSelectable(pokemon: Pokemon): boolean { - const move = this.getMove(); - if (!move.applySelectableConditions(pokemon)) { - return false; - } - return true; - } - getMove(): Move { return allMoves[this.moveId]; } diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index b4d67eae8b5..9453c297615 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -75,6 +75,19 @@ export class CommandPhase extends FieldPhase { const moveQueue = playerPokemon.getMoveQueue(); + /** + * Checks if the playerPokemon has a move that might be unselectable + */ + const moveset = playerPokemon.getMoveset(); + const conditionalMove = moveset.find(m => { + const move = m?.getMove(); + return move && move.selectableCondition && move.selectableCondition.length > 0; + }); + + if (conditionalMove) { + conditionalMove.getMove().applySelectableConditions(playerPokemon); + } + while (moveQueue.length && moveQueue[0] && moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move) || !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable(playerPokemon, moveQueue[0].ignorePP))) { // TODO: is the bang correct? @@ -140,8 +153,7 @@ export class CommandPhase extends FieldPhase { const errorMessage = playerPokemon.isMoveRestricted(move.moveId, playerPokemon) ? playerPokemon.getRestrictingTag(move.moveId, playerPokemon)!.selectionDeniedText(playerPokemon, move.moveId) - : !move.isSelectable(playerPokemon) ? "battle:moveCannotBeSelected" - : move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP"; + : move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP"; const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator this.scene.ui.showText(i18next.t(errorMessage, { moveName: moveName }), null, () => {