Refactoring implementation using MoveRestrictionTag

This commit is contained in:
geeil-han 2024-12-01 06:44:04 +01:00
parent 31fea8eafc
commit 7b5dc229a1
5 changed files with 77 additions and 29 deletions

View File

@ -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. * 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. * 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(); return new GrudgeTag();
case BattlerTagType.PSYCHO_SHIFT: case BattlerTagType.PSYCHO_SHIFT:
return new PsychoShiftTag(); return new PsychoShiftTag();
case BattlerTagType.STUFF_CHEEKS:
return new StuffCheeksTag(sourceMove);
case BattlerTagType.NONE: case BattlerTagType.NONE:
default: default:
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);

View File

@ -7539,15 +7539,13 @@ export class MoveSelectCondition {
} }
/** /**
* {@linkcode func} is being called in order to check if the {@linkcode user} is able to * Checks condition and adds appropriate MoveRestrictionTag accordingly
* select the {@linkcode move} and if the move should fail * @param user {@linkcode Pokemon} that uses the move
* * @param move {@linkcode Move} that is used
* @param user {@linkcode Pokemon} that want to use this {@linkcode move} * @returns true if Tag was added successfully
* @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 { 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 * contains function that checks if the {@linkcode user} is currently holding a berry or not
*/ */
constructor() { 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 { export class MoveCondition {
protected func: MoveConditionFunc; protected func: MoveConditionFunc;
@ -10057,7 +10081,8 @@ export function initMoves() {
new SelfStatusMove(Moves.STUFF_CHEEKS, Type.NORMAL, -1, 10, -1, 0, 8) new SelfStatusMove(Moves.STUFF_CHEEKS, Type.NORMAL, -1, 10, -1, 0, 8)
.attr(EatBerryAttr) .attr(EatBerryAttr)
.attr(StatStageChangeAttr, [ Stat.DEF ], 2, true) .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) 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(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true)
.attr(AddBattlerTagAttr, BattlerTagType.NO_RETREAT, true, false) .attr(AddBattlerTagAttr, BattlerTagType.NO_RETREAT, true, false)

View File

@ -93,4 +93,5 @@ export enum BattlerTagType {
GRUDGE = "GRUDGE", GRUDGE = "GRUDGE",
PSYCHO_SHIFT = "PSYCHO_SHIFT", PSYCHO_SHIFT = "PSYCHO_SHIFT",
ENDURE_TOKEN = "ENDURE_TOKEN", ENDURE_TOKEN = "ENDURE_TOKEN",
STUFF_CHEEKS = "STUFF_CHEEKS",
} }

View File

@ -5368,10 +5368,6 @@ export class PokemonMove {
return false; return false;
} }
if (!this.isSelectable(pokemon)) {
return false;
}
if (this.getMove().name.endsWith(" (N)")) { if (this.getMove().name.endsWith(" (N)")) {
return false; return false;
} }
@ -5379,20 +5375,6 @@ export class PokemonMove {
return (ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1); 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 { getMove(): Move {
return allMoves[this.moveId]; return allMoves[this.moveId];
} }

View File

@ -75,6 +75,19 @@ export class CommandPhase extends FieldPhase {
const moveQueue = playerPokemon.getMoveQueue(); 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] while (moveQueue.length && moveQueue[0]
&& moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move) && 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? || !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable(playerPokemon, moveQueue[0].ignorePP))) { // TODO: is the bang correct?
@ -140,7 +153,6 @@ export class CommandPhase extends FieldPhase {
const errorMessage = const errorMessage =
playerPokemon.isMoveRestricted(move.moveId, playerPokemon) playerPokemon.isMoveRestricted(move.moveId, playerPokemon)
? playerPokemon.getRestrictingTag(move.moveId, playerPokemon)!.selectionDeniedText(playerPokemon, move.moveId) ? 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 const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator