mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-19 13:59: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
|
||||
*/
|
||||
constructor(func?: MoveConditionFunc) {
|
||||
if (func) {
|
||||
this.func = func;
|
||||
}
|
||||
constructor(func: MoveConditionFunc) {
|
||||
this.func = func;
|
||||
}
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move): boolean {
|
||||
@ -46,9 +44,9 @@ export class MoveCondition {
|
||||
*/
|
||||
|
||||
export class FirstMoveCondition extends MoveCondition {
|
||||
public override readonly func: MoveConditionFunc = user => {
|
||||
return user.tempSummonData.waveTurnCount === 1;
|
||||
};
|
||||
constructor() {
|
||||
super(user => user.tempSummonData.waveTurnCount === 0 && user.tempSummonData.turnCount === 0);
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -173,6 +171,7 @@ export const UpperHandCondition = new MoveCondition((_user, target) => {
|
||||
* - 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
|
||||
* - 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) => {
|
||||
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
|
||||
* being selected
|
||||
* @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
|
||||
* move is used; default `false`
|
||||
* @param alsoCondition - If `true`, also adds an equivalent {@linkcode MoveCondition} that checks the same condition when the
|
||||
* 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`
|
||||
* @returns `this` for method chaining
|
||||
*/
|
||||
@ -11033,9 +11033,11 @@ export function initMoves() {
|
||||
.attr(EatBerryAttr, true)
|
||||
.attr(StatStageChangeAttr, [ Stat.DEF ], 2, true)
|
||||
.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",
|
||||
true),
|
||||
true,
|
||||
3
|
||||
),
|
||||
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 */)
|
||||
|
@ -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.
|
||||
* Each tag can have multiple different behaviors attached to different lapse types.
|
||||
@ -12,12 +14,13 @@ export enum BattlerTagLapseType {
|
||||
*/
|
||||
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
|
||||
*
|
||||
* 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
|
||||
* {@linkcode BattlerTagLapseType.MOVE} instead.
|
||||
|
@ -138,28 +138,44 @@ export class MovePhase extends BattlePhase {
|
||||
*/
|
||||
protected firstFailureCheck(): boolean {
|
||||
// A big if statement will handle the checks (that each have side effects!) in the correct order
|
||||
if (
|
||||
return (
|
||||
this.checkSleep() ||
|
||||
this.checkFreeze() ||
|
||||
this.checkPP() ||
|
||||
this.checkValidity() ||
|
||||
this.checkTagCancel(BattlerTagType.TRUANT, true) ||
|
||||
this.checkTagCancel(BattlerTagType.TRUANT) ||
|
||||
this.checkPreUseInterrupt() ||
|
||||
this.checkTagCancel(BattlerTagType.FLINCHED) ||
|
||||
this.checkTagCancel(BattlerTagType.DISABLED, true) ||
|
||||
this.checkTagCancel(BattlerTagType.DISABLED) ||
|
||||
this.checkTagCancel(BattlerTagType.HEAL_BLOCK) ||
|
||||
this.checkTagCancel(BattlerTagType.THROAT_CHOPPED) ||
|
||||
this.checkGravity() ||
|
||||
this.checkTagCancel(BattlerTagType.TAUNT, true) ||
|
||||
this.checkTagCancel(BattlerTagType.TAUNT) ||
|
||||
this.checkTagCancel(BattlerTagType.IMPRISON) ||
|
||||
this.checkTagCancel(BattlerTagType.CONFUSED) ||
|
||||
this.checkPara() ||
|
||||
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;
|
||||
const useMode = this.useMode;
|
||||
const ignoreStatus = isIgnoreStatus(useMode);
|
||||
if (!ignoreStatus && this.firstFailureCheck()) {
|
||||
// Lapse all other pre-move tags
|
||||
if (!ignoreStatus) {
|
||||
this.firstFailureCheck();
|
||||
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();
|
||||
|
||||
/*
|
||||
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;
|
||||
}
|
||||
// Tags still need to be lapsed if no failure occured
|
||||
user.lapseTags(BattlerTagLapseType.PRE_MOVE);
|
||||
// If this is a follow-up move , at this point, we need to re-check a few conditions
|
||||
|
||||
// At this point, called moves should be decided.
|
||||
// For now, this comment works as a placeholder until we rework how called moves are handled
|
||||
// For correct alignment with mainline, this SHOULD go here, and this phase SHOULD rewrite its own move
|
||||
|
||||
// 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();
|
||||
// If the first failure check passes (and this is not a sub-move) then thaw the user if its move will thaw it.
|
||||
// The sleep message and animation should also play if the user is asleep but using a move anyway (snore, sleep talk, etc)
|
||||
if (useMode !== MoveUseMode.FOLLOW_UP) {
|
||||
this.post1stFailSleepOrThaw();
|
||||
}
|
||||
|
||||
// Reset hit-related turn data when starting follow-up moves (e.g. Metronomed moves, Dancer repeats)
|
||||
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 checkIgnoreStatus - Whether to check {@link isIgnoreStatus} for the current {@linkcode MoveUseMode} to skip this check
|
||||
*/
|
||||
private checkTagCancel(tag: BattlerTagType, checkIgnoreStatus = false): boolean {
|
||||
if (checkIgnoreStatus && isIgnoreStatus(this.useMode)) {
|
||||
return false;
|
||||
}
|
||||
private checkTagCancel(tag: BattlerTagType): boolean {
|
||||
this.pokemon.lapseTag(tag, BattlerTagLapseType.PRE_MOVE);
|
||||
return this.cancelled;
|
||||
}
|
||||
|
@ -118,8 +118,10 @@ describe("Abilities - Cud Chew", () => {
|
||||
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
|
||||
|
||||
const farigiraf = game.field.getPlayerPokemon();
|
||||
const enemy = game.field.getEnemyPokemon();
|
||||
farigiraf.hp = 1; // needed to allow berry procs
|
||||
vi.spyOn(farigiraf, "randBattleSeedInt").mockReturnValue(0);
|
||||
vi.spyOn(enemy, "randBattleSeedInt").mockReturnValue(0);
|
||||
|
||||
game.move.select(MoveId.STUFF_CHEEKS);
|
||||
await game.toNextTurn();
|
||||
|
Loading…
Reference in New Issue
Block a user