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.
* 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);

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
* 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)

View File

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

View File

@ -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];
}

View File

@ -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, () => {