mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-19 22:09:27 +02:00
Adjust restriction for stuff cheeks
This commit is contained in:
parent
ce752c9d07
commit
92c7ece725
@ -26,10 +26,8 @@ export class MoveCondition {
|
|||||||
/**
|
/**
|
||||||
* @param func - A condition function that determines if the move can be used successfully
|
* @param func - A condition function that determines if the move can be used successfully
|
||||||
*/
|
*/
|
||||||
constructor(func?: MoveConditionFunc) {
|
constructor(func: MoveConditionFunc) {
|
||||||
if (func) {
|
this.func = func;
|
||||||
this.func = func;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move): boolean {
|
||||||
@ -46,9 +44,9 @@ export class MoveCondition {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export class FirstMoveCondition extends MoveCondition {
|
export class FirstMoveCondition extends MoveCondition {
|
||||||
public override readonly func: MoveConditionFunc = user => {
|
constructor() {
|
||||||
return user.tempSummonData.waveTurnCount === 1;
|
super(user => user.tempSummonData.waveTurnCount === 0 && user.tempSummonData.turnCount === 0);
|
||||||
};
|
}
|
||||||
|
|
||||||
// TODO: Update AI move selection logic to not require this method at all
|
// TODO: Update AI move selection logic to not require this method at all
|
||||||
// Currently, it is used to avoid having the AI select the move if its condition will fail
|
// Currently, it is used to avoid having the AI select the move if its condition will fail
|
||||||
@ -173,6 +171,7 @@ export const UpperHandCondition = new MoveCondition((_user, target) => {
|
|||||||
* - The user does not know at least one other move
|
* - The user does not know at least one other move
|
||||||
* - The user has not directly used each other move in its moveset since it was sent into battle
|
* - The user has not directly used each other move in its moveset since it was sent into battle
|
||||||
* - A move is considered *used* for this purpose if it passed the first failure check sequence in the move phase
|
* - A move is considered *used* for this purpose if it passed the first failure check sequence in the move phase
|
||||||
|
* (i.e. its usage message was displayed)
|
||||||
*/
|
*/
|
||||||
export const lastResortCondition = new MoveCondition((user, _target, move) => {
|
export const lastResortCondition = new MoveCondition((user, _target, move) => {
|
||||||
const otherMovesInMoveset = new Set<MoveId>(user.getMoveset().map(m => m.moveId));
|
const otherMovesInMoveset = new Set<MoveId>(user.getMoveset().map(m => m.moveId));
|
||||||
|
@ -450,8 +450,8 @@ export abstract class Move implements Localizable {
|
|||||||
* @param restriction - The function or `MoveRestriction` that evaluates to `true` if the move is restricted from
|
* @param restriction - The function or `MoveRestriction` that evaluates to `true` if the move is restricted from
|
||||||
* being selected
|
* being selected
|
||||||
* @param i18nkey - The i18n key for the restriction text
|
* @param i18nkey - The i18n key for the restriction text
|
||||||
* @param alsoCondition - If `true`, also adds a {@linkcode MoveCondition} that checks the same condition when the
|
* @param alsoCondition - If `true`, also adds an equivalent {@linkcode MoveCondition} that checks the same condition when the
|
||||||
* move is used; default `false`
|
* move is used (while taking care to invert the return value); default `false`
|
||||||
* @param conditionSeq - The sequence number where the failure check occurs; default `4`
|
* @param conditionSeq - The sequence number where the failure check occurs; default `4`
|
||||||
* @returns `this` for method chaining
|
* @returns `this` for method chaining
|
||||||
*/
|
*/
|
||||||
@ -11033,9 +11033,11 @@ export function initMoves() {
|
|||||||
.attr(EatBerryAttr, true)
|
.attr(EatBerryAttr, true)
|
||||||
.attr(StatStageChangeAttr, [ Stat.DEF ], 2, true)
|
.attr(StatStageChangeAttr, [ Stat.DEF ], 2, true)
|
||||||
.restriction(
|
.restriction(
|
||||||
user => globalScene.findModifiers(m => m instanceof BerryModifier, user.isPlayer()).length > 0,
|
user => globalScene.findModifiers(m => m instanceof BerryModifier, user.isPlayer()).length === 0,
|
||||||
"battle:moveDisabledNoBerry",
|
"battle:moveDisabledNoBerry",
|
||||||
true),
|
true,
|
||||||
|
3
|
||||||
|
),
|
||||||
new SelfStatusMove(MoveId.NO_RETREAT, PokemonType.FIGHTING, -1, 5, -1, 0, 8)
|
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 */)
|
.attr(AddBattlerTagAttr, BattlerTagType.NO_RETREAT, true, true /* NOT ADDED if already trapped */)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import type { MoveUseMode } from "#enums/move-use-mode";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum representing the possible ways a given BattlerTag can activate and/or tick down.
|
* Enum representing the possible ways a given BattlerTag can activate and/or tick down.
|
||||||
* Each tag can have multiple different behaviors attached to different lapse types.
|
* Each tag can have multiple different behaviors attached to different lapse types.
|
||||||
@ -12,12 +14,13 @@ export enum BattlerTagLapseType {
|
|||||||
*/
|
*/
|
||||||
MOVE,
|
MOVE,
|
||||||
/**
|
/**
|
||||||
* Tag activates during (or just after) the first failure check sequence in the move phase.
|
* Tag activates during (or just after) the first failure check sequence in the move phase
|
||||||
*
|
*
|
||||||
* @remarks
|
* @remarks
|
||||||
*
|
*
|
||||||
* Note tags with this lapse type will lapse immediately after the first failure check sequence,
|
* Note tags with this lapse type will lapse immediately after the first failure check sequence,
|
||||||
* regardless of whether the move was successful or not.
|
* regardless of whether the move was successful or not, but is skipped if the move is a
|
||||||
|
* {@linkcode MoveUseMode.FOLLOW_UP | follow-up} move.
|
||||||
*
|
*
|
||||||
* To only lapse the tag between the first and second failure check sequences, use
|
* To only lapse the tag between the first and second failure check sequences, use
|
||||||
* {@linkcode BattlerTagLapseType.MOVE} instead.
|
* {@linkcode BattlerTagLapseType.MOVE} instead.
|
||||||
|
@ -138,28 +138,44 @@ export class MovePhase extends BattlePhase {
|
|||||||
*/
|
*/
|
||||||
protected firstFailureCheck(): boolean {
|
protected firstFailureCheck(): boolean {
|
||||||
// A big if statement will handle the checks (that each have side effects!) in the correct order
|
// A big if statement will handle the checks (that each have side effects!) in the correct order
|
||||||
if (
|
return (
|
||||||
this.checkSleep() ||
|
this.checkSleep() ||
|
||||||
this.checkFreeze() ||
|
this.checkFreeze() ||
|
||||||
this.checkPP() ||
|
this.checkPP() ||
|
||||||
this.checkValidity() ||
|
this.checkValidity() ||
|
||||||
this.checkTagCancel(BattlerTagType.TRUANT, true) ||
|
this.checkTagCancel(BattlerTagType.TRUANT) ||
|
||||||
this.checkPreUseInterrupt() ||
|
this.checkPreUseInterrupt() ||
|
||||||
this.checkTagCancel(BattlerTagType.FLINCHED) ||
|
this.checkTagCancel(BattlerTagType.FLINCHED) ||
|
||||||
this.checkTagCancel(BattlerTagType.DISABLED, true) ||
|
this.checkTagCancel(BattlerTagType.DISABLED) ||
|
||||||
this.checkTagCancel(BattlerTagType.HEAL_BLOCK) ||
|
this.checkTagCancel(BattlerTagType.HEAL_BLOCK) ||
|
||||||
this.checkTagCancel(BattlerTagType.THROAT_CHOPPED) ||
|
this.checkTagCancel(BattlerTagType.THROAT_CHOPPED) ||
|
||||||
this.checkGravity() ||
|
this.checkGravity() ||
|
||||||
this.checkTagCancel(BattlerTagType.TAUNT, true) ||
|
this.checkTagCancel(BattlerTagType.TAUNT) ||
|
||||||
this.checkTagCancel(BattlerTagType.IMPRISON) ||
|
this.checkTagCancel(BattlerTagType.IMPRISON) ||
|
||||||
this.checkTagCancel(BattlerTagType.CONFUSED) ||
|
this.checkTagCancel(BattlerTagType.CONFUSED) ||
|
||||||
this.checkPara() ||
|
this.checkPara() ||
|
||||||
this.checkTagCancel(BattlerTagType.INFATUATED)
|
this.checkTagCancel(BattlerTagType.INFATUATED)
|
||||||
) {
|
);
|
||||||
this.handlePreMoveFailures();
|
}
|
||||||
return true;
|
|
||||||
}
|
/**
|
||||||
return false;
|
* Follow up moves need to check a subset of the first failure checks
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
*
|
||||||
|
* Based on smogon battle mechanics research, checks happen in the following order:
|
||||||
|
* 1. Invalid move (skipped in pokerogue)
|
||||||
|
* 2. Move prevented by heal block
|
||||||
|
* 3. Move prevented by throat chop
|
||||||
|
* 4. Gravity
|
||||||
|
* 5. sky battle (unused in Pokerogue)
|
||||||
|
*/
|
||||||
|
protected followUpMoveFirstFailureCheck(): boolean {
|
||||||
|
return (
|
||||||
|
this.checkTagCancel(BattlerTagType.HEAL_BLOCK) ||
|
||||||
|
this.checkTagCancel(BattlerTagType.THROAT_CHOPPED) ||
|
||||||
|
this.checkGravity()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -302,33 +318,29 @@ export class MovePhase extends BattlePhase {
|
|||||||
user.turnData.acted = true;
|
user.turnData.acted = true;
|
||||||
const useMode = this.useMode;
|
const useMode = this.useMode;
|
||||||
const ignoreStatus = isIgnoreStatus(useMode);
|
const ignoreStatus = isIgnoreStatus(useMode);
|
||||||
if (!ignoreStatus && this.firstFailureCheck()) {
|
if (!ignoreStatus) {
|
||||||
// Lapse all other pre-move tags
|
this.firstFailureCheck();
|
||||||
user.lapseTags(BattlerTagLapseType.PRE_MOVE);
|
user.lapseTags(BattlerTagLapseType.PRE_MOVE);
|
||||||
|
// At this point, called moves should be decided.
|
||||||
|
// For now, this comment works as a placeholder until called moves are reworked
|
||||||
|
// For correct alignment with mainline, this SHOULD go here, and this phase SHOULD rewrite its own move
|
||||||
|
} else if (useMode === MoveUseMode.FOLLOW_UP) {
|
||||||
|
this.followUpMoveFirstFailureCheck();
|
||||||
|
}
|
||||||
|
// If the first failure check did not pass, then the move is cancelled
|
||||||
|
// Note: This only checks `cancelled`, as `failed` should NEVER be set by anything in the first failure check
|
||||||
|
if (this.cancelled) {
|
||||||
|
this.handlePreMoveFailures();
|
||||||
this.end();
|
this.end();
|
||||||
|
|
||||||
/*
|
|
||||||
On cartridge, certain things *react* to move failures, depending on failure reason
|
|
||||||
The following would happen at this time on cartridge:
|
|
||||||
- Steadfast giving user speed boost if failed due to flinch
|
|
||||||
- Protect, detect, ally switch, etc, resetting consecutive use count
|
|
||||||
- Rollout / ice ball "unlocking"
|
|
||||||
- protect / ally switch / other moves resetting their consecutive use count
|
|
||||||
- and many others
|
|
||||||
In Pokerogue, these are instead handled elsewhere, and generally work in a way that aligns with cartridge behavior
|
|
||||||
*/
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Tags still need to be lapsed if no failure occured
|
// If this is a follow-up move , at this point, we need to re-check a few conditions
|
||||||
user.lapseTags(BattlerTagLapseType.PRE_MOVE);
|
|
||||||
|
|
||||||
// At this point, called moves should be decided.
|
// If the first failure check passes (and this is not a sub-move) then thaw the user if its move will thaw it.
|
||||||
// For now, this comment works as a placeholder until we rework how called moves are handled
|
// The sleep message and animation should also play if the user is asleep but using a move anyway (snore, sleep talk, etc)
|
||||||
// For correct alignment with mainline, this SHOULD go here, and this phase SHOULD rewrite its own move
|
if (useMode !== MoveUseMode.FOLLOW_UP) {
|
||||||
|
this.post1stFailSleepOrThaw();
|
||||||
// If the first failure check passes, then thaw the user if its move will thaw it.
|
}
|
||||||
// The sleep message and animation are also played if the user is asleep but using a move anyway (snore, sleep talk, etc)
|
|
||||||
this.post1stFailSleepOrThaw();
|
|
||||||
|
|
||||||
// Reset hit-related turn data when starting follow-up moves (e.g. Metronomed moves, Dancer repeats)
|
// Reset hit-related turn data when starting follow-up moves (e.g. Metronomed moves, Dancer repeats)
|
||||||
if (isVirtual(useMode)) {
|
if (isVirtual(useMode)) {
|
||||||
@ -618,10 +630,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
* @param tag - The tag type whose lapse method will be called with {@linkcode BattlerTagLapseType.PRE_MOVE}
|
* @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
|
* @param checkIgnoreStatus - Whether to check {@link isIgnoreStatus} for the current {@linkcode MoveUseMode} to skip this check
|
||||||
*/
|
*/
|
||||||
private checkTagCancel(tag: BattlerTagType, checkIgnoreStatus = false): boolean {
|
private checkTagCancel(tag: BattlerTagType): boolean {
|
||||||
if (checkIgnoreStatus && isIgnoreStatus(this.useMode)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.pokemon.lapseTag(tag, BattlerTagLapseType.PRE_MOVE);
|
this.pokemon.lapseTag(tag, BattlerTagLapseType.PRE_MOVE);
|
||||||
return this.cancelled;
|
return this.cancelled;
|
||||||
}
|
}
|
||||||
|
@ -118,8 +118,10 @@ describe("Abilities - Cud Chew", () => {
|
|||||||
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
|
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
|
||||||
|
|
||||||
const farigiraf = game.field.getPlayerPokemon();
|
const farigiraf = game.field.getPlayerPokemon();
|
||||||
|
const enemy = game.field.getEnemyPokemon();
|
||||||
farigiraf.hp = 1; // needed to allow berry procs
|
farigiraf.hp = 1; // needed to allow berry procs
|
||||||
vi.spyOn(farigiraf, "randBattleSeedInt").mockReturnValue(0);
|
vi.spyOn(farigiraf, "randBattleSeedInt").mockReturnValue(0);
|
||||||
|
vi.spyOn(enemy, "randBattleSeedInt").mockReturnValue(0);
|
||||||
|
|
||||||
game.move.select(MoveId.STUFF_CHEEKS);
|
game.move.select(MoveId.STUFF_CHEEKS);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
Loading…
Reference in New Issue
Block a user