mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-30 21:42:20 +02:00
Added helper functions complete with markdown tables
This commit is contained in:
parent
4a6b392c3f
commit
d30995f040
@ -123,7 +123,7 @@ import { MoveEffectTrigger } from "#enums/MoveEffectTrigger";
|
||||
import { MultiHitType } from "#enums/MultiHitType";
|
||||
import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves, invalidSketchMoves } from "./invalid-moves";
|
||||
import { SelectBiomePhase } from "#app/phases/select-biome-phase";
|
||||
import { MoveUseType } from "#enums/move-use-type";
|
||||
import { isFollowUp, MoveUseType } from "#enums/move-use-type";
|
||||
|
||||
type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
|
||||
type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean;
|
||||
@ -7038,7 +7038,7 @@ export class RepeatMoveAttr extends MoveEffectAttr {
|
||||
const lastMove = target.getLastNonVirtualMove();
|
||||
const movesetMove = target.getMoveset().find(m => m.moveId === lastMove?.move)
|
||||
|
||||
// never happens due to condition func, but makes TS compiler not sad
|
||||
// never happens due to condition func, but makes TS compiler not sad about nullishness
|
||||
if (!lastMove || !movesetMove) {
|
||||
return false;
|
||||
}
|
||||
@ -7046,7 +7046,7 @@ export class RepeatMoveAttr extends MoveEffectAttr {
|
||||
// If the last move used can hit more than one target or has variable targets,
|
||||
// re-compute the targets for the attack (mainly for alternating double/single battles)
|
||||
// Rampaging moves (e.g. Outrage) are not included due to being incompatible with Instruct,
|
||||
// nor is Dragon Darts (due to handling its smart targeting entirely within `MoveEffectPhase`)
|
||||
// nor is Dragon Darts (due to its smart targeting bypassing normal target selection)
|
||||
let moveTargets = movesetMove.getMove().isMultiTarget() ? getMoveTargets(target, lastMove.move).targets : lastMove.targets;
|
||||
|
||||
// In the event the instructed move's only target is a fainted opponent, redirect it to an alive ally if possible.
|
||||
@ -7816,12 +7816,11 @@ export class LastResortAttr extends MoveAttr {
|
||||
|
||||
const movesInHistory = new Set<Moves>(
|
||||
user.getMoveHistory()
|
||||
.filter(m => m.useType < MoveUseType.INDIRECT) // Last resort ignores virtual moves
|
||||
.filter(m => !isFollowUp(m.useType)) // Last resort ignores virtual moves
|
||||
.map(m => m.move)
|
||||
);
|
||||
|
||||
// Since `Set.intersection()` is only present in ESNext, we have to do this to check inclusion
|
||||
// grumble mumble grumble mumble...
|
||||
return [...otherMovesInMoveset].every(m => movesInHistory.has(m))
|
||||
};
|
||||
}
|
||||
|
@ -6,6 +6,10 @@ import type { PostDancingMoveAbAttr } from "#app/data/abilities/ability";
|
||||
* Each one inherits the properties (or exclusions) of all types preceding it.
|
||||
* Properties newly found on a given use type will be **bolded**,
|
||||
* while oddities breaking a previous trend will be listed in _italics_.
|
||||
|
||||
* Callers should refrain from performing non-equality checks on `MoveUseTypes` directly,
|
||||
* instead using the available helper functions
|
||||
* ({@linkcode isFollowUp}, {@linkcode isIgnorePP} and {@linkcode isReflected}).
|
||||
*/
|
||||
export enum MoveUseType {
|
||||
/**
|
||||
@ -63,3 +67,67 @@ export enum MoveUseType {
|
||||
* {@linkcode BattlerTagLapseType}
|
||||
* {@linkcode PostDancingMoveAbAttr}
|
||||
*/
|
||||
|
||||
// # HELPER FUNCTIONS
|
||||
// Please update the markdown tables if any new `MoveUseType`s get added.
|
||||
|
||||
/**
|
||||
* Check if a given {@linkcode MoveUseType} is follow-up (i.e. called by another move).
|
||||
* Follow-up moves are ignored by most moveset-related effects and pre-move cancellation checks.
|
||||
* @param useType - The {@linkcode MoveUseType} to check.
|
||||
* @returns Whether {@linkcode useType} is follow-up.
|
||||
* @remarks
|
||||
* This function is equivalent to the following truth table:
|
||||
|
||||
* | Use Type | Returns |
|
||||
* |------------------------------------|---------|
|
||||
* | {@linkcode MoveUseType.NORMAL} | `false` |
|
||||
* | {@linkcode MoveUseType.IGNORE_PP} | `false` |
|
||||
* | {@linkcode MoveUseType.INDIRECT} | `true` |
|
||||
* | {@linkcode MoveUseType.FOLLOW_UP} | `true` |
|
||||
* | {@linkcode MoveUseType.REFLECTED} | `true` |
|
||||
*/
|
||||
export function isFollowUp(useType: MoveUseType): boolean {
|
||||
return useType >= MoveUseType.INDIRECT
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given {@linkcode MoveUseType} should ignore PP.
|
||||
* PP-ignoring moves will ignore normal PP consumption as well as associated failure checks.
|
||||
* @param useType - The {@linkcode MoveUseType} to check.
|
||||
* @returns Whether {@linkcode useType} ignores PP.
|
||||
* @remarks
|
||||
* This function is equivalent to the following truth table:
|
||||
|
||||
* | Use Type | Returns |
|
||||
* |------------------------------------|---------|
|
||||
* | {@linkcode MoveUseType.NORMAL} | `false` |
|
||||
* | {@linkcode MoveUseType.IGNORE_PP} | `true` |
|
||||
* | {@linkcode MoveUseType.INDIRECT} | `true` |
|
||||
* | {@linkcode MoveUseType.FOLLOW_UP} | `true` |
|
||||
* | {@linkcode MoveUseType.REFLECTED} | `true` |
|
||||
*/
|
||||
export function isIgnorePP(useType: MoveUseType): boolean {
|
||||
return useType >= MoveUseType.IGNORE_PP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given {@linkcode MoveUseType} is reflected.
|
||||
* Reflected moves cannot be reflected, copied, or cancelled by status effects,
|
||||
* nor will they trigger {@linkcode PostDancingMoveAbAttr | Dancer}.
|
||||
* @param useType - The {@linkcode MoveUseType} to check.
|
||||
* @returns Whether {@linkcode useType} is reflected.
|
||||
* @remarks
|
||||
* This function is equivalent to the following truth table:
|
||||
*
|
||||
* | Use Type | Returns |
|
||||
* |------------------------------------|---------|
|
||||
* | {@linkcode MoveUseType.NORMAL} | `false` |
|
||||
* | {@linkcode MoveUseType.IGNORE_PP} | `false` |
|
||||
* | {@linkcode MoveUseType.INDIRECT} | `false` |
|
||||
* | {@linkcode MoveUseType.FOLLOW_UP} | `false` |
|
||||
* | {@linkcode MoveUseType.REFLECTED} | `true` |
|
||||
*/
|
||||
export function isReflected(useType: MoveUseType): boolean {
|
||||
return useType === MoveUseType.REFLECTED;
|
||||
}
|
||||
|
@ -260,7 +260,7 @@ import { MoveFlags } from "#enums/MoveFlags";
|
||||
import { timedEventManager } from "#app/global-event-manager";
|
||||
import { loadMoveAnimations } from "#app/sprites/pokemon-asset-loader";
|
||||
import { ResetStatusPhase } from "#app/phases/reset-status-phase";
|
||||
import { MoveUseType } from "#enums/move-use-type";
|
||||
import { isFollowUp, isIgnorePP, MoveUseType } from "#enums/move-use-type";
|
||||
|
||||
export enum LearnMoveSituation {
|
||||
MISC,
|
||||
@ -5164,16 +5164,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
* - Non-virtual ({@linkcode MoveUseType | useType} < {@linkcode MoveUseType.INDIRECT})
|
||||
* @param ignoreStruggle - Whether to additionally ignore {@linkcode Moves.STRUGGLE}; default `false`
|
||||
* @param ignoreFollowUp - Whether to ignore moves with a use type of {@linkcode MoveUseType.FOLLOW_UP}
|
||||
* (Copycat, Mirror Move, etc.); default `true`.
|
||||
* (e.g. ones called by Copycat/Mirror Move); default `true`.
|
||||
* @returns The last move this Pokemon has used satisfying the aforementioned conditions,
|
||||
* or `undefined` if no applicable moves have been used since switching in.
|
||||
*/
|
||||
getLastNonVirtualMove(ignoreStruggle = false, ignoreFollowUp = true): TurnMove | undefined {
|
||||
return this.getLastXMoves(-1).find(m =>
|
||||
m.move !== Moves.NONE
|
||||
&& (!ignoreStruggle || m.move !== Moves.STRUGGLE)
|
||||
&& (m.useType < MoveUseType.INDIRECT ||
|
||||
(!ignoreFollowUp && m.useType === MoveUseType.FOLLOW_UP))
|
||||
&& (!ignoreStruggle || m.move !== Moves.STRUGGLE)
|
||||
);
|
||||
}
|
||||
|
||||
@ -7270,11 +7270,11 @@ export class EnemyPokemon extends Pokemon {
|
||||
// Otherwise, ensure that the move being used is actually usable
|
||||
// TODO: Virtual moves shouldn't use the move queue
|
||||
if (
|
||||
queuedMove.useType >= MoveUseType.INDIRECT ||
|
||||
isFollowUp(queuedMove.useType) ||
|
||||
(moveIndex > -1 &&
|
||||
this.getMoveset()[moveIndex].isUsable(
|
||||
this,
|
||||
queuedMove.useType >= MoveUseType.IGNORE_PP)
|
||||
isIgnorePP(queuedMove.useType))
|
||||
)
|
||||
) {
|
||||
return queuedMove;
|
||||
|
@ -23,7 +23,7 @@ import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
import { isNullOrUndefined } from "#app/utils/common";
|
||||
import { ArenaTagSide } from "#app/data/arena-tag";
|
||||
import { ArenaTagType } from "#app/enums/arena-tag-type";
|
||||
import { MoveUseType } from "#enums/move-use-type";
|
||||
import { isFollowUp, isIgnorePP, MoveUseType } from "#enums/move-use-type";
|
||||
|
||||
export class CommandPhase extends FieldPhase {
|
||||
protected fieldIndex: number;
|
||||
@ -104,13 +104,13 @@ export class CommandPhase extends FieldPhase {
|
||||
moveQueue.length &&
|
||||
moveQueue[0] &&
|
||||
moveQueue[0].move &&
|
||||
moveQueue[0].useType < MoveUseType.INDIRECT &&
|
||||
!isFollowUp(moveQueue[0].useType) &&
|
||||
(!playerPokemon.getMoveset().find(m => m.moveId === moveQueue[0].move) ||
|
||||
!playerPokemon
|
||||
.getMoveset()
|
||||
[playerPokemon.getMoveset().findIndex(m => m.moveId === moveQueue[0].move)].isUsable(
|
||||
playerPokemon,
|
||||
moveQueue[0].useType >= MoveUseType.IGNORE_PP,
|
||||
isIgnorePP(moveQueue[0].useType),
|
||||
))
|
||||
) {
|
||||
moveQueue.shift();
|
||||
@ -125,10 +125,8 @@ export class CommandPhase extends FieldPhase {
|
||||
const moveIndex = playerPokemon.getMoveset().findIndex(m => m.moveId === queuedMove.move);
|
||||
if (
|
||||
(moveIndex > -1 &&
|
||||
playerPokemon
|
||||
.getMoveset()
|
||||
[moveIndex].isUsable(playerPokemon, queuedMove.useType >= MoveUseType.IGNORE_PP)) ||
|
||||
queuedMove.useType >= MoveUseType.INDIRECT
|
||||
playerPokemon.getMoveset()[moveIndex].isUsable(playerPokemon, isIgnorePP(queuedMove.useType))) ||
|
||||
isFollowUp(queuedMove.useType)
|
||||
) {
|
||||
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.useType, queuedMove);
|
||||
} else {
|
||||
|
@ -78,7 +78,7 @@ import type Move from "#app/data/moves/move";
|
||||
import { isFieldTargeted } from "#app/data/moves/move-utils";
|
||||
import { FaintPhase } from "./faint-phase";
|
||||
import { DamageAchv } from "#app/system/achv";
|
||||
import { MoveUseType } from "#enums/move-use-type";
|
||||
import { isFollowUp, MoveUseType } from "#enums/move-use-type";
|
||||
|
||||
export type HitCheckEntry = [HitCheckResult, TypeDamageMultiplier];
|
||||
|
||||
@ -302,7 +302,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
this.getFirstTarget() ?? null,
|
||||
move,
|
||||
overridden,
|
||||
this.useType >= MoveUseType.INDIRECT,
|
||||
isFollowUp(this.useType),
|
||||
);
|
||||
|
||||
// If other effects were overriden, stop this phase before they can be applied
|
||||
|
@ -50,7 +50,7 @@ import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import i18next from "i18next";
|
||||
import { MoveUseType } from "#enums/move-use-type";
|
||||
import { isFollowUp, isIgnorePP, isReflected, MoveUseType } from "#enums/move-use-type";
|
||||
|
||||
export class MovePhase extends BattlePhase {
|
||||
protected _pokemon: Pokemon;
|
||||
@ -123,7 +123,7 @@ export class MovePhase extends BattlePhase {
|
||||
public canMove(ignoreDisableTags = false): boolean {
|
||||
return (
|
||||
this.pokemon.isActive(true) &&
|
||||
this.move.isUsable(this.pokemon, this.useType >= MoveUseType.IGNORE_PP, ignoreDisableTags) &&
|
||||
this.move.isUsable(this.pokemon, isIgnorePP(this.useType), ignoreDisableTags) &&
|
||||
this.targets.length > 0
|
||||
);
|
||||
}
|
||||
@ -151,9 +151,14 @@ export class MovePhase extends BattlePhase {
|
||||
|
||||
console.log(Moves[this.move.moveId], MoveUseType[this.useType]);
|
||||
|
||||
// Check if move is unusable (e.g. running out of PP due to a mid-turn Spite
|
||||
// or the user no longer being on field).
|
||||
if (!this.useType) {
|
||||
console.warn(`Unexpected MoveUseType of ${this.useType} during move phase!`);
|
||||
this.useType = MoveUseType.NORMAL;
|
||||
}
|
||||
|
||||
if (!this.canMove(true)) {
|
||||
// Check if move is unusable (e.g. running out of PP due to a mid-turn Spite
|
||||
// or the user no longer being on field).
|
||||
if (this.pokemon.isActive(true)) {
|
||||
this.fail();
|
||||
this.showMoveText();
|
||||
@ -166,7 +171,7 @@ export class MovePhase extends BattlePhase {
|
||||
this.pokemon.turnData.acted = true;
|
||||
|
||||
// Reset hit-related turn data when starting follow-up moves (e.g. Metronomed moves, Dancer repeats)
|
||||
if (this.useType >= MoveUseType.INDIRECT) {
|
||||
if (isFollowUp(this.useType)) {
|
||||
this.pokemon.turnData.hitsLeft = -1;
|
||||
this.pokemon.turnData.hitCount = 0;
|
||||
}
|
||||
@ -176,7 +181,7 @@ export class MovePhase extends BattlePhase {
|
||||
this.move.getMove().doesFlagEffectApply({
|
||||
flag: MoveFlags.IGNORE_ABILITIES,
|
||||
user: this.pokemon,
|
||||
isFollowUp: this.useType >= MoveUseType.INDIRECT, // Sunsteel strike and co. don't work when called indirectly
|
||||
isFollowUp: isFollowUp(this.useType), // Sunsteel strike and co. don't work when called indirectly
|
||||
})
|
||||
) {
|
||||
globalScene.arena.setIgnoreAbilities(true, this.pokemon.getBattlerIndex());
|
||||
@ -376,7 +381,7 @@ export class MovePhase extends BattlePhase {
|
||||
this.pokemon.lapseTag(BattlerTagType.CHARGING);
|
||||
}
|
||||
|
||||
if (this.useType < MoveUseType.IGNORE_PP) {
|
||||
if (!isIgnorePP(this.useType)) {
|
||||
// "commit" to using the move, deducting PP.
|
||||
const ppUsed = 1 + this.getPpIncreaseFromPressure(targets);
|
||||
|
||||
@ -506,11 +511,7 @@ export class MovePhase extends BattlePhase {
|
||||
*/
|
||||
public end(): void {
|
||||
globalScene.unshiftPhase(
|
||||
new MoveEndPhase(
|
||||
this.pokemon.getBattlerIndex(),
|
||||
this.getActiveTargetPokemon(),
|
||||
this.useType >= MoveUseType.INDIRECT,
|
||||
),
|
||||
new MoveEndPhase(this.pokemon.getBattlerIndex(), this.getActiveTargetPokemon(), isFollowUp(this.useType)),
|
||||
);
|
||||
|
||||
super.end();
|
||||
@ -681,7 +682,7 @@ export class MovePhase extends BattlePhase {
|
||||
}
|
||||
|
||||
globalScene.queueMessage(
|
||||
i18next.t(this.useType === MoveUseType.REFLECTED ? "battle:magicCoatActivated" : "battle:useMove", {
|
||||
i18next.t(isReflected(this.useType) ? "battle:magicCoatActivated" : "battle:useMove", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon),
|
||||
moveName: this.move.getName(),
|
||||
}),
|
||||
|
@ -29,7 +29,8 @@ const fixMoveHistory: SessionSaveMigrator = {
|
||||
targets: tm.targets,
|
||||
result: tm.result,
|
||||
turn: tm.turn,
|
||||
// NOTE: This currently mis-classifies Dancer and Magic Bounce-induced moves, but not much we can do about it tbh
|
||||
// NOTE: This unfortuately has to mis-classify Dancer and Magic Bounce-induced moves as `FOLLOW_UP`,
|
||||
// given we previously had _no way_ of distinguishing them from follow-up moves post hoc.
|
||||
useType: tm.virtual ? MoveUseType.FOLLOW_UP : tm.ignorePP ? MoveUseType.IGNORE_PP : MoveUseType.NORMAL,
|
||||
});
|
||||
data.party.forEach(pkmn => {
|
||||
|
Loading…
Reference in New Issue
Block a user