mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-01 05:52:17 +02:00
Fixed things
This commit is contained in:
parent
d30995f040
commit
5a75169493
@ -398,13 +398,10 @@ export class GorillaTacticsTag extends MoveRestrictionBattlerTag {
|
|||||||
* @param pokemon - The {@linkcode Pokemon} to add the tag to
|
* @param pokemon - The {@linkcode Pokemon} to add the tag to
|
||||||
*/
|
*/
|
||||||
override onAdd(pokemon: Pokemon): void {
|
override onAdd(pokemon: Pokemon): void {
|
||||||
const lastMove = pokemon.getLastNonVirtualMove();
|
|
||||||
if (!lastMove) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onAdd(pokemon);
|
super.onAdd(pokemon);
|
||||||
this.moveId = lastMove.move;
|
|
||||||
|
// Bang is justified as tag is not added if prior move doesn't exist
|
||||||
|
this.moveId = pokemon.getLastNonVirtualMove()!.move;
|
||||||
pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.ATK, false) * 1.5, false);
|
pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.ATK, false) * 1.5, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ import { MoveEffectTrigger } from "#enums/MoveEffectTrigger";
|
|||||||
import { MultiHitType } from "#enums/MultiHitType";
|
import { MultiHitType } from "#enums/MultiHitType";
|
||||||
import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves, invalidSketchMoves } from "./invalid-moves";
|
import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves, invalidSketchMoves } from "./invalid-moves";
|
||||||
import { SelectBiomePhase } from "#app/phases/select-biome-phase";
|
import { SelectBiomePhase } from "#app/phases/select-biome-phase";
|
||||||
import { isFollowUp, MoveUseType } from "#enums/move-use-type";
|
import { isVirtual, MoveUseType } from "#enums/move-use-type";
|
||||||
|
|
||||||
type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
|
type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
|
||||||
type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean;
|
type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean;
|
||||||
@ -7022,6 +7022,7 @@ export class CopyMoveAttr extends CallMoveAttr {
|
|||||||
* Used for [Instruct](https://bulbapedia.bulbagarden.net/wiki/Instruct_(move)).
|
* Used for [Instruct](https://bulbapedia.bulbagarden.net/wiki/Instruct_(move)).
|
||||||
*/
|
*/
|
||||||
export class RepeatMoveAttr extends MoveEffectAttr {
|
export class RepeatMoveAttr extends MoveEffectAttr {
|
||||||
|
private movesetMove: PokemonMove;
|
||||||
constructor() {
|
constructor() {
|
||||||
super(false, { trigger: MoveEffectTrigger.POST_APPLY }); // needed to ensure correct protect interaction
|
super(false, { trigger: MoveEffectTrigger.POST_APPLY }); // needed to ensure correct protect interaction
|
||||||
}
|
}
|
||||||
@ -7034,20 +7035,16 @@ export class RepeatMoveAttr extends MoveEffectAttr {
|
|||||||
*/
|
*/
|
||||||
apply(user: Pokemon, target: Pokemon): boolean {
|
apply(user: Pokemon, target: Pokemon): boolean {
|
||||||
// get the last move used (excluding status based failures) as well as the corresponding moveset slot
|
// get the last move used (excluding status based failures) as well as the corresponding moveset slot
|
||||||
|
// bangs are justified as Instruct fails if no prior move or moveset move exists
|
||||||
// TODO: How does instruct work when copying a move called via Copycat that the user itself knows?
|
// TODO: How does instruct work when copying a move called via Copycat that the user itself knows?
|
||||||
const lastMove = target.getLastNonVirtualMove();
|
const lastMove = target.getLastNonVirtualMove()!;
|
||||||
const movesetMove = target.getMoveset().find(m => m.moveId === lastMove?.move)
|
const movesetMove = target.getMoveset().find(m => m.moveId === lastMove?.move)!
|
||||||
|
|
||||||
// never happens due to condition func, but makes TS compiler not sad about nullishness
|
|
||||||
if (!lastMove || !movesetMove) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the last move used can hit more than one target or has variable targets,
|
// 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)
|
// 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,
|
// Rampaging moves (e.g. Outrage) are not included due to being incompatible with Instruct,
|
||||||
// nor is Dragon Darts (due to its smart targeting bypassing normal target selection)
|
// 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;
|
let moveTargets = this.movesetMove.getMove().isMultiTarget() ? getMoveTargets(target, this.movesetMove.moveId).targets : lastMove.targets;
|
||||||
|
|
||||||
// In the event the instructed move's only target is a fainted opponent, redirect it to an alive ally if possible.
|
// In the event the instructed move's only target is a fainted opponent, redirect it to an alive ally if possible.
|
||||||
// Normally, all yet-unexecuted move phases would swap targets after any foe faints or flees (see `redirectPokemonMoves` in `battle-scene.ts`),
|
// Normally, all yet-unexecuted move phases would swap targets after any foe faints or flees (see `redirectPokemonMoves` in `battle-scene.ts`),
|
||||||
@ -7153,6 +7150,7 @@ export class RepeatMoveAttr extends MoveEffectAttr {
|
|||||||
|| uninstructableMoves.includes(lastMove.move)) { // called move is in the banlist
|
|| uninstructableMoves.includes(lastMove.move)) { // called move is in the banlist
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
this.movesetMove = movesetMove;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -7816,7 +7814,7 @@ export class LastResortAttr extends MoveAttr {
|
|||||||
|
|
||||||
const movesInHistory = new Set<Moves>(
|
const movesInHistory = new Set<Moves>(
|
||||||
user.getMoveHistory()
|
user.getMoveHistory()
|
||||||
.filter(m => !isFollowUp(m.useType)) // Last resort ignores virtual moves
|
.filter(m => !isVirtual(m.useType)) // Last resort ignores virtual moves
|
||||||
.map(m => m.move)
|
.map(m => m.move)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import type { PostDancingMoveAbAttr } from "#app/data/abilities/ability";
|
|||||||
|
|
||||||
* Callers should refrain from performing non-equality checks on `MoveUseTypes` directly,
|
* Callers should refrain from performing non-equality checks on `MoveUseTypes` directly,
|
||||||
* instead using the available helper functions
|
* instead using the available helper functions
|
||||||
* ({@linkcode isFollowUp}, {@linkcode isIgnorePP} and {@linkcode isReflected}).
|
* ({@linkcode isVirtual}, {@linkcode isIgnorePP} and {@linkcode isReflected}).
|
||||||
*/
|
*/
|
||||||
export enum MoveUseType {
|
export enum MoveUseType {
|
||||||
/**
|
/**
|
||||||
@ -72,10 +72,10 @@ export enum MoveUseType {
|
|||||||
// Please update the markdown tables if any new `MoveUseType`s get added.
|
// 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).
|
* Check if a given {@linkcode MoveUseType} is virtual (i.e. called by another move or effect).
|
||||||
* Follow-up moves are ignored by most moveset-related effects and pre-move cancellation checks.
|
* Virtual moves are ignored by most moveset-related effects and pre-move cancellation checks.
|
||||||
* @param useType - The {@linkcode MoveUseType} to check.
|
* @param useType - The {@linkcode MoveUseType} to check.
|
||||||
* @returns Whether {@linkcode useType} is follow-up.
|
* @returns Whether {@linkcode useType} is virtual.
|
||||||
* @remarks
|
* @remarks
|
||||||
* This function is equivalent to the following truth table:
|
* This function is equivalent to the following truth table:
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ export enum MoveUseType {
|
|||||||
* | {@linkcode MoveUseType.FOLLOW_UP} | `true` |
|
* | {@linkcode MoveUseType.FOLLOW_UP} | `true` |
|
||||||
* | {@linkcode MoveUseType.REFLECTED} | `true` |
|
* | {@linkcode MoveUseType.REFLECTED} | `true` |
|
||||||
*/
|
*/
|
||||||
export function isFollowUp(useType: MoveUseType): boolean {
|
export function isVirtual(useType: MoveUseType): boolean {
|
||||||
return useType >= MoveUseType.INDIRECT
|
return useType >= MoveUseType.INDIRECT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,7 +260,7 @@ import { MoveFlags } from "#enums/MoveFlags";
|
|||||||
import { timedEventManager } from "#app/global-event-manager";
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
import { loadMoveAnimations } from "#app/sprites/pokemon-asset-loader";
|
import { loadMoveAnimations } from "#app/sprites/pokemon-asset-loader";
|
||||||
import { ResetStatusPhase } from "#app/phases/reset-status-phase";
|
import { ResetStatusPhase } from "#app/phases/reset-status-phase";
|
||||||
import { isFollowUp, isIgnorePP, MoveUseType } from "#enums/move-use-type";
|
import { isVirtual, isIgnorePP, MoveUseType } from "#enums/move-use-type";
|
||||||
|
|
||||||
export enum LearnMoveSituation {
|
export enum LearnMoveSituation {
|
||||||
MISC,
|
MISC,
|
||||||
@ -5172,7 +5172,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return this.getLastXMoves(-1).find(m =>
|
return this.getLastXMoves(-1).find(m =>
|
||||||
m.move !== Moves.NONE
|
m.move !== Moves.NONE
|
||||||
&& (!ignoreStruggle || m.move !== Moves.STRUGGLE)
|
&& (!ignoreStruggle || m.move !== Moves.STRUGGLE)
|
||||||
&& (m.useType < MoveUseType.INDIRECT ||
|
&& (!isVirtual(m.useType) ||
|
||||||
(!ignoreFollowUp && m.useType === MoveUseType.FOLLOW_UP))
|
(!ignoreFollowUp && m.useType === MoveUseType.FOLLOW_UP))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -7270,7 +7270,7 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
// Otherwise, ensure that the move being used is actually usable
|
// Otherwise, ensure that the move being used is actually usable
|
||||||
// TODO: Virtual moves shouldn't use the move queue
|
// TODO: Virtual moves shouldn't use the move queue
|
||||||
if (
|
if (
|
||||||
isFollowUp(queuedMove.useType) ||
|
isVirtual(queuedMove.useType) ||
|
||||||
(moveIndex > -1 &&
|
(moveIndex > -1 &&
|
||||||
this.getMoveset()[moveIndex].isUsable(
|
this.getMoveset()[moveIndex].isUsable(
|
||||||
this,
|
this,
|
||||||
|
@ -23,7 +23,7 @@ import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
|||||||
import { isNullOrUndefined } from "#app/utils/common";
|
import { isNullOrUndefined } from "#app/utils/common";
|
||||||
import { ArenaTagSide } from "#app/data/arena-tag";
|
import { ArenaTagSide } from "#app/data/arena-tag";
|
||||||
import { ArenaTagType } from "#app/enums/arena-tag-type";
|
import { ArenaTagType } from "#app/enums/arena-tag-type";
|
||||||
import { isFollowUp, isIgnorePP, MoveUseType } from "#enums/move-use-type";
|
import { isVirtual, isIgnorePP, MoveUseType } from "#enums/move-use-type";
|
||||||
|
|
||||||
export class CommandPhase extends FieldPhase {
|
export class CommandPhase extends FieldPhase {
|
||||||
protected fieldIndex: number;
|
protected fieldIndex: number;
|
||||||
@ -104,7 +104,7 @@ export class CommandPhase extends FieldPhase {
|
|||||||
moveQueue.length &&
|
moveQueue.length &&
|
||||||
moveQueue[0] &&
|
moveQueue[0] &&
|
||||||
moveQueue[0].move &&
|
moveQueue[0].move &&
|
||||||
!isFollowUp(moveQueue[0].useType) &&
|
!isVirtual(moveQueue[0].useType) &&
|
||||||
(!playerPokemon.getMoveset().find(m => m.moveId === moveQueue[0].move) ||
|
(!playerPokemon.getMoveset().find(m => m.moveId === moveQueue[0].move) ||
|
||||||
!playerPokemon
|
!playerPokemon
|
||||||
.getMoveset()
|
.getMoveset()
|
||||||
@ -126,7 +126,7 @@ export class CommandPhase extends FieldPhase {
|
|||||||
if (
|
if (
|
||||||
(moveIndex > -1 &&
|
(moveIndex > -1 &&
|
||||||
playerPokemon.getMoveset()[moveIndex].isUsable(playerPokemon, isIgnorePP(queuedMove.useType))) ||
|
playerPokemon.getMoveset()[moveIndex].isUsable(playerPokemon, isIgnorePP(queuedMove.useType))) ||
|
||||||
isFollowUp(queuedMove.useType)
|
isVirtual(queuedMove.useType)
|
||||||
) {
|
) {
|
||||||
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.useType, queuedMove);
|
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.useType, queuedMove);
|
||||||
} else {
|
} else {
|
||||||
|
@ -78,7 +78,7 @@ import type Move from "#app/data/moves/move";
|
|||||||
import { isFieldTargeted } from "#app/data/moves/move-utils";
|
import { isFieldTargeted } from "#app/data/moves/move-utils";
|
||||||
import { FaintPhase } from "./faint-phase";
|
import { FaintPhase } from "./faint-phase";
|
||||||
import { DamageAchv } from "#app/system/achv";
|
import { DamageAchv } from "#app/system/achv";
|
||||||
import { isFollowUp, MoveUseType } from "#enums/move-use-type";
|
import { isVirtual, isReflected, MoveUseType } from "#enums/move-use-type";
|
||||||
|
|
||||||
export type HitCheckEntry = [HitCheckResult, TypeDamageMultiplier];
|
export type HitCheckEntry = [HitCheckResult, TypeDamageMultiplier];
|
||||||
|
|
||||||
@ -302,7 +302,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
this.getFirstTarget() ?? null,
|
this.getFirstTarget() ?? null,
|
||||||
move,
|
move,
|
||||||
overridden,
|
overridden,
|
||||||
isFollowUp(this.useType),
|
isVirtual(this.useType),
|
||||||
);
|
);
|
||||||
|
|
||||||
// If other effects were overriden, stop this phase before they can be applied
|
// If other effects were overriden, stop this phase before they can be applied
|
||||||
@ -567,10 +567,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reflected moves cannot be reflected again
|
// Reflected moves cannot be reflected again
|
||||||
if (
|
if (!isReflected(this.useType) && move.doesFlagEffectApply({ flag: MoveFlags.REFLECTABLE, user, target })) {
|
||||||
this.useType < MoveUseType.REFLECTED &&
|
|
||||||
move.doesFlagEffectApply({ flag: MoveFlags.REFLECTABLE, user, target })
|
|
||||||
) {
|
|
||||||
return [HitCheckResult.REFLECTED, 0];
|
return [HitCheckResult.REFLECTED, 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ import { BattlerTagType } from "#enums/battler-tag-type";
|
|||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { isFollowUp, isIgnorePP, isReflected, MoveUseType } from "#enums/move-use-type";
|
import { isVirtual, isIgnorePP, isReflected, MoveUseType } from "#enums/move-use-type";
|
||||||
|
|
||||||
export class MovePhase extends BattlePhase {
|
export class MovePhase extends BattlePhase {
|
||||||
protected _pokemon: Pokemon;
|
protected _pokemon: Pokemon;
|
||||||
@ -171,7 +171,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
this.pokemon.turnData.acted = true;
|
this.pokemon.turnData.acted = true;
|
||||||
|
|
||||||
// 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 (isFollowUp(this.useType)) {
|
if (isVirtual(this.useType)) {
|
||||||
this.pokemon.turnData.hitsLeft = -1;
|
this.pokemon.turnData.hitsLeft = -1;
|
||||||
this.pokemon.turnData.hitCount = 0;
|
this.pokemon.turnData.hitCount = 0;
|
||||||
}
|
}
|
||||||
@ -181,7 +181,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
this.move.getMove().doesFlagEffectApply({
|
this.move.getMove().doesFlagEffectApply({
|
||||||
flag: MoveFlags.IGNORE_ABILITIES,
|
flag: MoveFlags.IGNORE_ABILITIES,
|
||||||
user: this.pokemon,
|
user: this.pokemon,
|
||||||
isFollowUp: isFollowUp(this.useType), // Sunsteel strike and co. don't work when called indirectly
|
isFollowUp: isVirtual(this.useType), // Sunsteel strike and co. don't work when called indirectly
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
globalScene.arena.setIgnoreAbilities(true, this.pokemon.getBattlerIndex());
|
globalScene.arena.setIgnoreAbilities(true, this.pokemon.getBattlerIndex());
|
||||||
@ -321,7 +321,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
|
|
||||||
// TODO: does this intentionally happen before the no targets/Moves.NONE on queue cancellation case is checked?
|
// TODO: does this intentionally happen before the no targets/Moves.NONE on queue cancellation case is checked?
|
||||||
// (In other words, check if truant can proc on a move w/o targets)
|
// (In other words, check if truant can proc on a move w/o targets)
|
||||||
if (this.useType < MoveUseType.FOLLOW_UP && this.canMove() && !this.cancelled) {
|
if (!isVirtual(this.useType) && this.canMove() && !this.cancelled) {
|
||||||
this.pokemon.lapseTags(BattlerTagLapseType.MOVE);
|
this.pokemon.lapseTags(BattlerTagLapseType.MOVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -511,7 +511,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
*/
|
*/
|
||||||
public end(): void {
|
public end(): void {
|
||||||
globalScene.unshiftPhase(
|
globalScene.unshiftPhase(
|
||||||
new MoveEndPhase(this.pokemon.getBattlerIndex(), this.getActiveTargetPokemon(), isFollowUp(this.useType)),
|
new MoveEndPhase(this.pokemon.getBattlerIndex(), this.getActiveTargetPokemon(), isVirtual(this.useType)),
|
||||||
);
|
);
|
||||||
|
|
||||||
super.end();
|
super.end();
|
||||||
@ -641,7 +641,7 @@ export class MovePhase extends BattlePhase {
|
|||||||
protected handlePreMoveFailures(): void {
|
protected handlePreMoveFailures(): void {
|
||||||
if (this.cancelled || this.failed) {
|
if (this.cancelled || this.failed) {
|
||||||
if (this.failed) {
|
if (this.failed) {
|
||||||
const ppUsed = this.useType > MoveUseType.IGNORE_PP ? 1 : 0;
|
const ppUsed = isIgnorePP(this.useType) ? 0 : 1;
|
||||||
|
|
||||||
if (ppUsed) {
|
if (ppUsed) {
|
||||||
this.move.usePp();
|
this.move.usePp();
|
||||||
|
@ -5,7 +5,7 @@ import { Abilities } from "#enums/abilities";
|
|||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { BerryPhase } from "#app/phases/berry-phase";
|
import { BerryPhase } from "#app/phases/berry-phase";
|
||||||
import { MoveResult, PokemonMove } from "#app/field/pokemon";
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
@ -43,7 +43,7 @@ describe("Moves - Powder", () => {
|
|||||||
await game.classicMode.startBattle([Species.CHARIZARD]);
|
await game.classicMode.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
enemyPokemon.moveset = [new PokemonMove(Moves.EMBER)];
|
game.move.changeMoveset(enemyPokemon, Moves.EMBER);
|
||||||
|
|
||||||
game.move.select(Moves.POWDER);
|
game.move.select(Moves.POWDER);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user