mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-21 00:52:47 +02:00
[Bug] Fix moves/abilities which modify abilities (#5146)
* Add setAbility method to pokemon.ts * Edit SwitchAbilitiesAttr to use setAbility * Change AbilityGiveAttr to use setAbility * Rename setAbility to be more accurate * Fix AbilityCopyAttr * Fix AbilityChangeAttr * Fix Transform * Fix imposter * Fix PostDefendAbilityGiveAbAttr * Actually fix imposter * Actually fix transform * Fix CopyFaintedAllyAbilityAbAttr * Fix Trace * Fix PostDefendAbilitySwapAbAttr * Add tests for skill swap * Add tests for doodle * Add tests for entrainment * Add tests for role play * Add test for simple beam * Add test for transform * Add test for imposter * Add tests for mummy * Add tests for trace * Add tests for wandering spirit * Consider legendary weather when changing ability * Ensure that passives are not (re)applied when main abilities change * Add general ability swap test cases * Fix test name * Add NoMidTurnActivationAttr * Remove NoMidTurnActivationAttr from illusion * Remove extraneous call to triggerWeatherBasedFormChanges * Fix primal weather clearing * Change "MidTurn" to "OnGain" * Change NoOnGainActivationAttr to a field in PostSummonAbAttr * Add passive support * Remove redundant parentheses Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --------- Co-authored-by: damocleas <damocleas25@gmail.com> Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
This commit is contained in:
parent
7fafccf8de
commit
9cc1b17745
@ -1044,9 +1044,9 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr {
|
||||
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)
|
||||
&& !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr) && !move.hitsSubstitute(attacker, pokemon)) {
|
||||
if (!simulated) {
|
||||
const tempAbilityId = attacker.getAbility().id;
|
||||
attacker.summonData.ability = pokemon.getAbility().id;
|
||||
pokemon.summonData.ability = tempAbilityId;
|
||||
const tempAbility = attacker.getAbility();
|
||||
attacker.setTempAbility(pokemon.getAbility());
|
||||
pokemon.setTempAbility(tempAbility);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1071,7 +1071,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
|
||||
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr)
|
||||
&& !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr) && !move.hitsSubstitute(attacker, pokemon)) {
|
||||
if (!simulated) {
|
||||
attacker.summonData.ability = this.ability;
|
||||
attacker.setTempAbility(allAbilities[this.ability]);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1908,8 +1908,8 @@ export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr {
|
||||
applyPostKnockOut(pokemon: Pokemon, passive: boolean, simulated: boolean, knockedOut: Pokemon, args: any[]): boolean | Promise<boolean> {
|
||||
if (pokemon.isPlayer() === knockedOut.isPlayer() && !knockedOut.getAbility().hasAttr(UncopiableAbilityAbAttr)) {
|
||||
if (!simulated) {
|
||||
pokemon.summonData.ability = knockedOut.getAbility().id;
|
||||
globalScene.queueMessage(i18next.t("abilityTriggers:copyFaintedAllyAbility", { pokemonNameWithAffix: getPokemonNameWithAffix(knockedOut), abilityName: allAbilities[knockedOut.getAbility().id].name }));
|
||||
pokemon.setTempAbility(knockedOut.getAbility());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1993,6 +1993,21 @@ export class PostIntimidateStatStageChangeAbAttr extends AbAttr {
|
||||
* @see {@linkcode applyPostSummon()}
|
||||
*/
|
||||
export class PostSummonAbAttr extends AbAttr {
|
||||
/** Should the ability activate when gained in battle? This will almost always be true */
|
||||
private activateOnGain: boolean;
|
||||
|
||||
constructor(showAbility: boolean = true, activateOnGain: boolean = true) {
|
||||
super(showAbility);
|
||||
this.activateOnGain = activateOnGain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns Whether the ability should activate when gained in battle
|
||||
*/
|
||||
shouldActivateOnGain(): boolean {
|
||||
return this.activateOnGain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies ability post summon (after switching in)
|
||||
* @param pokemon {@linkcode Pokemon} with this ability
|
||||
@ -2330,7 +2345,7 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr {
|
||||
if (!simulated) {
|
||||
this.target = target!;
|
||||
this.targetAbilityName = allAbilities[target!.getAbility().id].name;
|
||||
pokemon.summonData.ability = target!.getAbility().id;
|
||||
pokemon.setTempAbility(target!.getAbility());
|
||||
setAbilityRevealed(target!);
|
||||
pokemon.updateInfo();
|
||||
}
|
||||
@ -2427,7 +2442,7 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr {
|
||||
*/
|
||||
export class PostSummonTransformAbAttr extends PostSummonAbAttr {
|
||||
constructor() {
|
||||
super(true);
|
||||
super(true, false);
|
||||
}
|
||||
|
||||
async applyPostSummon(pokemon: Pokemon, _passive: boolean, simulated: boolean, _args: any[]): Promise<boolean> {
|
||||
@ -2462,7 +2477,6 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr {
|
||||
}
|
||||
|
||||
pokemon.summonData.speciesForm = target.getSpeciesForm();
|
||||
pokemon.summonData.ability = target.getAbility().id;
|
||||
pokemon.summonData.gender = target.getGender();
|
||||
|
||||
// Copy all stats (except HP)
|
||||
@ -2492,6 +2506,8 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr {
|
||||
promises.push(pokemon.loadAssets(false).then(() => {
|
||||
pokemon.playAnim();
|
||||
pokemon.updateInfo();
|
||||
// If the new ability activates immediately, it needs to happen after all the transform animations
|
||||
pokemon.setTempAbility(target.getAbility());
|
||||
}));
|
||||
|
||||
await Promise.all(promises);
|
||||
@ -4852,53 +4868,72 @@ async function applyAbAttrsInternal<TAttr extends AbAttr>(
|
||||
showAbilityInstant: boolean = false,
|
||||
simulated: boolean = false,
|
||||
messages: string[] = [],
|
||||
gainedMidTurn: boolean = false
|
||||
) {
|
||||
for (const passive of [ false, true ]) {
|
||||
if (!pokemon?.canApplyAbility(passive) || (passive && pokemon.getPassiveAbility().id === pokemon.getAbility().id)) {
|
||||
if (!pokemon?.canApplyAbility(passive) || (passive && (pokemon.getPassiveAbility().id === pokemon.getAbility().id))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility();
|
||||
for (const attr of ability.getAttrs(attrType)) {
|
||||
const condition = attr.getCondition();
|
||||
if (condition && !condition(pokemon)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
globalScene.setPhaseQueueSplice();
|
||||
|
||||
let result = applyFunc(attr, passive);
|
||||
// TODO Remove this when promises get reworked
|
||||
if (result instanceof Promise) {
|
||||
result = await result;
|
||||
}
|
||||
if (result) {
|
||||
if (pokemon.summonData && !pokemon.summonData.abilitiesApplied.includes(ability.id)) {
|
||||
pokemon.summonData.abilitiesApplied.push(ability.id);
|
||||
}
|
||||
if (pokemon.battleData && !simulated && !pokemon.battleData.abilitiesApplied.includes(ability.id)) {
|
||||
pokemon.battleData.abilitiesApplied.push(ability.id);
|
||||
}
|
||||
if (attr.showAbility && !simulated) {
|
||||
if (showAbilityInstant) {
|
||||
globalScene.abilityBar.showAbility(pokemon, passive);
|
||||
} else {
|
||||
queueShowAbility(pokemon, passive);
|
||||
}
|
||||
}
|
||||
const message = attr.getTriggerMessage(pokemon, ability.name, args);
|
||||
if (message) {
|
||||
if (!simulated) {
|
||||
globalScene.queueMessage(message);
|
||||
}
|
||||
}
|
||||
messages.push(message!);
|
||||
}
|
||||
}
|
||||
applySingleAbAttrs(pokemon, passive, attrType, applyFunc, args, gainedMidTurn, simulated, showAbilityInstant, messages);
|
||||
globalScene.clearPhaseQueueSplice();
|
||||
}
|
||||
}
|
||||
|
||||
async function applySingleAbAttrs<TAttr extends AbAttr>(
|
||||
pokemon: Pokemon,
|
||||
passive: boolean,
|
||||
attrType: Constructor<TAttr>,
|
||||
applyFunc: AbAttrApplyFunc<TAttr>,
|
||||
args: any[],
|
||||
gainedMidTurn: boolean = false,
|
||||
simulated: boolean = false,
|
||||
showAbilityInstant: boolean = false,
|
||||
messages: string[] = []
|
||||
) {
|
||||
const ability = passive ? pokemon.getPassiveAbility() : pokemon.getAbility();
|
||||
if (gainedMidTurn && ability.getAttrs(attrType).some(attr => attr instanceof PostSummonAbAttr && !attr.shouldActivateOnGain())) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const attr of ability.getAttrs(attrType)) {
|
||||
const condition = attr.getCondition();
|
||||
if (condition && !condition(pokemon)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
globalScene.setPhaseQueueSplice();
|
||||
|
||||
let result = applyFunc(attr, passive);
|
||||
// TODO Remove this when promises get reworked
|
||||
if (result instanceof Promise) {
|
||||
result = await result;
|
||||
}
|
||||
if (result) {
|
||||
if (pokemon.summonData && !pokemon.summonData.abilitiesApplied.includes(ability.id)) {
|
||||
pokemon.summonData.abilitiesApplied.push(ability.id);
|
||||
}
|
||||
if (pokemon.battleData && !simulated && !pokemon.battleData.abilitiesApplied.includes(ability.id)) {
|
||||
pokemon.battleData.abilitiesApplied.push(ability.id);
|
||||
}
|
||||
if (attr.showAbility && !simulated) {
|
||||
if (showAbilityInstant) {
|
||||
globalScene.abilityBar.showAbility(pokemon, passive);
|
||||
} else {
|
||||
queueShowAbility(pokemon, passive);
|
||||
}
|
||||
}
|
||||
const message = attr.getTriggerMessage(pokemon, ability.name, args);
|
||||
if (message) {
|
||||
if (!simulated) {
|
||||
globalScene.queueMessage(message);
|
||||
}
|
||||
}
|
||||
messages.push(message!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ForceSwitchOutHelper {
|
||||
constructor(private switchType: SwitchType) {}
|
||||
|
||||
@ -5285,6 +5320,21 @@ export function applyPostItemLostAbAttrs(attrType: Constructor<PostItemLostAbAtt
|
||||
return applyAbAttrsInternal<PostItemLostAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostItemLost(pokemon, simulated, args), args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies abilities when they become active mid-turn (ability switch)
|
||||
*
|
||||
* Ignores passives as they don't change and shouldn't be reapplied when main abilities change
|
||||
*/
|
||||
export function applyOnGainAbAttrs(pokemon: Pokemon, passive: boolean = false, simulated: boolean = false, ...args: any[]): void {
|
||||
applySingleAbAttrs<PostSummonAbAttr>(pokemon, passive, PostSummonAbAttr, (attr, passive) => attr.applyPostSummon(pokemon, passive, simulated, args), args, true, simulated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears primal weather during the turn if {@linkcode pokemon}'s ability corresponds to one
|
||||
*/
|
||||
export function applyOnLoseClearWeatherAbAttrs(pokemon: Pokemon, passive: boolean = false, simulated: boolean = false, ...args: any[]): void {
|
||||
applySingleAbAttrs<PreLeaveFieldClearWeatherAbAttr>(pokemon, passive, PreLeaveFieldClearWeatherAbAttr, (attr, passive) => attr.applyPreLeaveField(pokemon, passive, simulated, [ ...args, true ]), args, true, simulated);
|
||||
}
|
||||
function queueShowAbility(pokemon: Pokemon, passive: boolean): void {
|
||||
globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.id, passive));
|
||||
globalScene.clearPhaseQueueSplice();
|
||||
|
@ -7521,11 +7521,11 @@ export class AbilityChangeAttr extends MoveEffectAttr {
|
||||
|
||||
const moveTarget = this.selfTarget ? user : target;
|
||||
|
||||
moveTarget.summonData.ability = this.ability;
|
||||
globalScene.triggerPokemonFormChange(moveTarget, SpeciesFormChangeRevertWeatherFormTrigger);
|
||||
|
||||
globalScene.queueMessage(i18next.t("moveTriggers:acquiredAbility", { pokemonName: getPokemonNameWithAffix((this.selfTarget ? user : target)), abilityName: allAbilities[this.ability].name }));
|
||||
|
||||
moveTarget.setTempAbility(allAbilities[this.ability]);
|
||||
globalScene.triggerPokemonFormChange(moveTarget, SpeciesFormChangeRevertWeatherFormTrigger);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -7548,13 +7548,13 @@ export class AbilityCopyAttr extends MoveEffectAttr {
|
||||
return false;
|
||||
}
|
||||
|
||||
user.summonData.ability = target.getAbility().id;
|
||||
|
||||
globalScene.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name }));
|
||||
|
||||
user.setTempAbility(target.getAbility());
|
||||
|
||||
if (this.copyToPartner && globalScene.currentBattle?.double && user.getAlly().hp) {
|
||||
user.getAlly().summonData.ability = target.getAbility().id;
|
||||
globalScene.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(user.getAlly()), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name }));
|
||||
user.getAlly().setTempAbility(target.getAbility());
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -7585,10 +7585,10 @@ export class AbilityGiveAttr extends MoveEffectAttr {
|
||||
return false;
|
||||
}
|
||||
|
||||
target.summonData.ability = user.getAbility().id;
|
||||
|
||||
globalScene.queueMessage(i18next.t("moveTriggers:acquiredAbility", { pokemonName: getPokemonNameWithAffix(target), abilityName: allAbilities[user.getAbility().id].name }));
|
||||
|
||||
target.setTempAbility(user.getAbility());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -7603,15 +7603,14 @@ export class SwitchAbilitiesAttr extends MoveEffectAttr {
|
||||
return false;
|
||||
}
|
||||
|
||||
const tempAbilityId = user.getAbility().id;
|
||||
user.summonData.ability = target.getAbility().id;
|
||||
target.summonData.ability = tempAbilityId;
|
||||
const tempAbility = user.getAbility();
|
||||
|
||||
globalScene.queueMessage(i18next.t("moveTriggers:swappedAbilitiesWithTarget", { pokemonName: getPokemonNameWithAffix(user) }));
|
||||
|
||||
user.setTempAbility(target.getAbility());
|
||||
target.setTempAbility(tempAbility);
|
||||
// Swaps Forecast/Flower Gift from Castform/Cherrim
|
||||
globalScene.arena.triggerWeatherBasedFormChangesToNormal();
|
||||
// Swaps Forecast/Flower Gift to Castform/Cherrim (edge case)
|
||||
globalScene.arena.triggerWeatherBasedFormChanges();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -7690,7 +7689,6 @@ export class TransformAttr extends MoveEffectAttr {
|
||||
|
||||
const promises: Promise<void>[] = [];
|
||||
user.summonData.speciesForm = target.getSpeciesForm();
|
||||
user.summonData.ability = target.getAbility().id;
|
||||
user.summonData.gender = target.getGender();
|
||||
|
||||
// Power Trick's effect will not preserved after using Transform
|
||||
@ -7723,6 +7721,8 @@ export class TransformAttr extends MoveEffectAttr {
|
||||
promises.push(user.loadAssets(false).then(() => {
|
||||
user.playAnim();
|
||||
user.updateInfo();
|
||||
// If the new ability activates immediately, it needs to happen after all the transform animations
|
||||
user.setTempAbility(target.getAbility());
|
||||
}));
|
||||
|
||||
await Promise.all(promises);
|
||||
|
@ -64,7 +64,7 @@ import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoo
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
|
||||
import type { Ability, AbAttr } from "#app/data/ability";
|
||||
import { StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr, PostSetStatusAbAttr, applyPostSetStatusAbAttrs, InfiltratorAbAttr, AlliedFieldDamageReductionAbAttr, PostDamageAbAttr, applyPostDamageAbAttrs, CommanderAbAttr, applyPostItemLostAbAttrs, PostItemLostAbAttr, PreLeaveFieldAbAttr, applyPreLeaveFieldAbAttrs } from "#app/data/ability";
|
||||
import { StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr, PostSetStatusAbAttr, applyPostSetStatusAbAttrs, InfiltratorAbAttr, AlliedFieldDamageReductionAbAttr, PostDamageAbAttr, applyPostDamageAbAttrs, CommanderAbAttr, applyPostItemLostAbAttrs, PostItemLostAbAttr, applyOnGainAbAttrs, PreLeaveFieldAbAttr, applyPreLeaveFieldAbAttrs, applyOnLoseClearWeatherAbAttrs } from "#app/data/ability";
|
||||
import type PokemonData from "#app/system/pokemon-data";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
@ -1481,6 +1481,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return abilityAttrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@linkcode Pokemon}'s ability and activates it if it normally activates on summon
|
||||
*
|
||||
* Also clears primal weather if it is from the ability being changed
|
||||
* @param ability New Ability
|
||||
*/
|
||||
public setTempAbility(ability: Ability, passive: boolean = false): void {
|
||||
applyOnLoseClearWeatherAbAttrs(this, passive);
|
||||
if (passive) {
|
||||
this.summonData.passiveAbility = ability.id;
|
||||
} else {
|
||||
this.summonData.ability = ability.id;
|
||||
}
|
||||
applyOnGainAbAttrs(this, passive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a pokemon has a passive either from:
|
||||
* - bought with starter candy
|
||||
|
@ -116,4 +116,15 @@ describe("Abilities - Imposter", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should activate its ability if it copies one that activates on summon", async () => {
|
||||
game.override.enemyAbility(Abilities.INTIMIDATE);
|
||||
|
||||
await game.classicMode.startBattle([ Species.DITTO ]);
|
||||
|
||||
game.move.select(Moves.TACKLE);
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
|
||||
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
|
||||
});
|
||||
});
|
||||
|
52
src/test/abilities/mummy.test.ts
Normal file
52
src/test/abilities/mummy.test.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Mummy", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.MUMMY)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.TACKLE);
|
||||
});
|
||||
|
||||
it("should set the enemy's ability to mummy when hit by a contact move", async () => {
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(Abilities.MUMMY);
|
||||
});
|
||||
|
||||
it("should not change the enemy's ability hit by a non-contact move", async () => {
|
||||
game.override.enemyMoveset(Moves.EARTHQUAKE);
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(Abilities.BALL_FETCH);
|
||||
});
|
||||
});
|
53
src/test/abilities/trace.test.ts
Normal file
53
src/test/abilities/trace.test.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { Stat } from "#app/enums/stat";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Trace", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.TRACE)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should copy the opponent's ability", async () => {
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(Abilities.BALL_FETCH);
|
||||
});
|
||||
|
||||
it("should activate a copied post-summon ability", async () => {
|
||||
game.override.enemyAbility(Abilities.INTIMIDATE);
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
|
||||
});
|
||||
});
|
65
src/test/abilities/wandering_spirit.test.ts
Normal file
65
src/test/abilities/wandering_spirit.test.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { Stat } from "#app/enums/stat";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Wandering Spirit", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.WANDERING_SPIRIT)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.TACKLE);
|
||||
});
|
||||
|
||||
it("should exchange abilities when hit with a contact move", async () => {
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(Abilities.BALL_FETCH);
|
||||
expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(Abilities.WANDERING_SPIRIT);
|
||||
});
|
||||
|
||||
it("should not exchange abilities when hit with a non-contact move", async () => {
|
||||
game.override.enemyMoveset(Moves.EARTHQUAKE);
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(Abilities.WANDERING_SPIRIT);
|
||||
expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(Abilities.BALL_FETCH);
|
||||
});
|
||||
|
||||
it("should activate post-summon abilities", async () => {
|
||||
game.override.enemyAbility(Abilities.INTIMIDATE);
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
|
||||
});
|
||||
});
|
67
src/test/battle/ability_swap.test.ts
Normal file
67
src/test/battle/ability_swap.test.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { allAbilities } from "#app/data/ability";
|
||||
import { Stat } from "#app/enums/stat";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Test Ability Swapping", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should activate post-summon abilities", async () => {
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
game.scene.getPlayerPokemon()?.setTempAbility(allAbilities[Abilities.INTIMIDATE]);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
|
||||
});
|
||||
|
||||
it("should remove primal weather when the setter's ability is removed", async () => {
|
||||
game.override.ability(Abilities.DESOLATE_LAND);
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
game.scene.getPlayerPokemon()?.setTempAbility(allAbilities[Abilities.BALL_FETCH]);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.arena.weather?.weatherType).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not activate passive abilities", async () => {
|
||||
game.override.passiveAbility(Abilities.INTREPID_SWORD);
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
game.scene.getPlayerPokemon()?.setTempAbility(allAbilities[Abilities.BALL_FETCH]);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(1); // would be 2 if passive activated again
|
||||
});
|
||||
});
|
70
src/test/moves/doodle.test.ts
Normal file
70
src/test/moves/doodle.test.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { Stat } from "#app/enums/stat";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Moves - Doodle", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH, Moves.DOODLE ])
|
||||
.ability(Abilities.ADAPTABILITY)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should copy the opponent's ability in singles", async () => {
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.DOODLE);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(Abilities.BALL_FETCH);
|
||||
});
|
||||
|
||||
it("should copy the opponent's ability to itself and its ally in doubles", async () => {
|
||||
game.override.battleType("double");
|
||||
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]);
|
||||
|
||||
game.move.select(Moves.DOODLE, 0, BattlerIndex.ENEMY);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getPlayerField()[0].getAbility().id).toBe(Abilities.BALL_FETCH);
|
||||
expect(game.scene.getPlayerField()[1].getAbility().id).toBe(Abilities.BALL_FETCH);
|
||||
});
|
||||
|
||||
it("should activate post-summon abilities", async () => {
|
||||
game.override.battleType("double")
|
||||
.enemyAbility(Abilities.INTIMIDATE);
|
||||
|
||||
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]);
|
||||
|
||||
game.move.select(Moves.DOODLE, 0, BattlerIndex.ENEMY);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
// Enemies should have been intimidated twice
|
||||
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-2);
|
||||
});
|
||||
});
|
53
src/test/moves/entrainment.test.ts
Normal file
53
src/test/moves/entrainment.test.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { Stat } from "#app/enums/stat";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Moves - Entrainment", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH, Moves.ENTRAINMENT ])
|
||||
.ability(Abilities.ADAPTABILITY)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("gives its ability to the target", async () => {
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.ENTRAINMENT);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(Abilities.ADAPTABILITY);
|
||||
});
|
||||
|
||||
it("should activate post-summon abilities", async () => {
|
||||
game.override.ability(Abilities.INTIMIDATE);
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.ENTRAINMENT);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
|
||||
});
|
||||
});
|
53
src/test/moves/role_play.test.ts
Normal file
53
src/test/moves/role_play.test.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { Stat } from "#app/enums/stat";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Moves - Role Play", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH, Moves.ROLE_PLAY ])
|
||||
.ability(Abilities.ADAPTABILITY)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should set the user's ability to the target's ability", async () => {
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.ROLE_PLAY);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(Abilities.BALL_FETCH);
|
||||
});
|
||||
|
||||
it("should activate post-summon abilities", async () => {
|
||||
game.override.enemyAbility(Abilities.INTIMIDATE);
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.ROLE_PLAY);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
|
||||
});
|
||||
});
|
42
src/test/moves/simple_beam.test.ts
Normal file
42
src/test/moves/simple_beam.test.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Moves - Simple Beam", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH, Moves.SIMPLE_BEAM ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("sets the target's ability to simple", async () => {
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SIMPLE_BEAM);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(Abilities.SIMPLE);
|
||||
});
|
||||
});
|
56
src/test/moves/skill_swap.test.ts
Normal file
56
src/test/moves/skill_swap.test.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { Stat } from "#app/enums/stat";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Moves - Skill Swap", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH, Moves.SKILL_SWAP ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should swap the two abilities", async () => {
|
||||
game.override.ability(Abilities.ADAPTABILITY);
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(Abilities.BALL_FETCH);
|
||||
expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(Abilities.ADAPTABILITY);
|
||||
});
|
||||
|
||||
it("should activate post-summon abilities", async () => {
|
||||
game.override.ability(Abilities.INTIMIDATE);
|
||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||
|
||||
game.move.select(Moves.SKILL_SWAP);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
// player atk should be -1 after opponent gains intimidate and it activates
|
||||
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
|
||||
});
|
||||
});
|
@ -116,4 +116,16 @@ describe("Moves - Transform", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should activate its ability if it copies one that activates on summon", async () => {
|
||||
game.override.enemyAbility(Abilities.INTIMIDATE)
|
||||
.ability(Abilities.BALL_FETCH);
|
||||
|
||||
await game.classicMode.startBattle([ Species.DITTO ]);
|
||||
game.move.select(Moves.TRANSFORM);
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user