Update tests to reflect move effect phase changes
Co-authored-by: innerthunder <brandonerickson98@gmail.com>
This commit is contained in:
parent
f85e008202
commit
f29486cd4e
|
@ -669,10 +669,10 @@ export default class Move implements Localizable {
|
|||
case MoveFlags.REFLECTABLE:
|
||||
// If the target is not semi-invulnerable and either has magic coat active or an unignored magic bounce ability
|
||||
if (
|
||||
target?.getTag(SemiInvulnerableTag) &&
|
||||
(target.getTag(BattlerTagType.MAGIC_COAT) ||
|
||||
target?.getTag(SemiInvulnerableTag) ||
|
||||
!(target?.getTag(BattlerTagType.MAGIC_COAT) ||
|
||||
(!this.doesFlagEffectApply({ flag: MoveFlags.IGNORE_ABILITIES, user, target }) &&
|
||||
target.hasAbilityWithAttr(ReflectStatusMoveAbAttr)))
|
||||
target?.hasAbilityWithAttr(ReflectStatusMoveAbAttr)))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
@ -2733,11 +2733,11 @@ export class StealEatBerryAttr extends EatBerryAttr {
|
|||
}
|
||||
/**
|
||||
* User steals a random berry from the target and then eats it.
|
||||
* @param {Pokemon} user Pokemon that used the move and will eat the stolen berry
|
||||
* @param {Pokemon} target Pokemon that will have its berry stolen
|
||||
* @param {Move} move Move being used
|
||||
* @param {any[]} args Unused
|
||||
* @returns {boolean} true if the function succeeds
|
||||
* @param user - Pokemon that used the move and will eat the stolen berry
|
||||
* @param target - Pokemon that will have its berry stolen
|
||||
* @param move - Move being used
|
||||
* @param args Unused
|
||||
* @returns true if the function succeeds
|
||||
*/
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const cancelled = new BooleanHolder(false);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
export enum MoveEffectTrigger {
|
||||
PRE_APPLY,
|
||||
POST_APPLY,
|
||||
HIT,
|
||||
/** Triggers one time after all target effects have applied */
|
||||
POST_TARGET
|
||||
}
|
||||
|
|
|
@ -151,8 +151,6 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
|
||||
let targets = this.getTargets();
|
||||
|
||||
let fieldBounce = false;
|
||||
|
||||
// For field targeted moves, we only look for the first target that may magic bounce
|
||||
|
||||
for (const [i, target] of targets.entries()) {
|
||||
|
@ -162,7 +160,6 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
if (fieldMove && hitCheck[0] === HitCheckResult.REFLECTED) {
|
||||
targets = [target];
|
||||
this.hitChecks = [hitCheck];
|
||||
fieldBounce = true;
|
||||
break;
|
||||
}
|
||||
if (hitCheck[0] === HitCheckResult.HIT) {
|
||||
|
@ -173,13 +170,8 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
this.hitChecks[i] = hitCheck;
|
||||
}
|
||||
|
||||
if (anySuccess || (fieldMove && !fieldBounce)) {
|
||||
if (anySuccess) {
|
||||
this.moveHistoryEntry.result = MoveResult.SUCCESS;
|
||||
// Unreflected field moves have no targets; they target the field
|
||||
if (fieldMove) {
|
||||
this.hitChecks = [];
|
||||
return [];
|
||||
}
|
||||
} else {
|
||||
user.turnData.hitCount = 1;
|
||||
user.turnData.hitsLeft = 1;
|
||||
|
@ -350,6 +342,9 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
|
||||
const targets = this.conductHitChecks(user, fieldMove);
|
||||
|
||||
this.firstHit = user.turnData.hitCount === user.turnData.hitsLeft;
|
||||
this.lastHit = user.turnData.hitsLeft === 1 || !targets.some(t => t.isActive(true));
|
||||
|
||||
// only play the animation if the move had at least one successful target
|
||||
|
||||
// Play the animation if the move was successful against any of its targets or it has a POST_TARGET effect (like self destruct)
|
||||
|
@ -375,6 +370,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
* Callback to be called after the move animation is played
|
||||
*/
|
||||
private postAnimCallback(user: Pokemon, targets: Pokemon[]) {
|
||||
console.log("============Inside post anim callback======");
|
||||
// Add to the move history entry
|
||||
if (this.firstHit) {
|
||||
user.pushMoveHistory(this.moveHistoryEntry);
|
||||
|
@ -387,7 +383,10 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
console.warn(e.message || "Unexpected error in move effect phase");
|
||||
this.end();
|
||||
}
|
||||
//
|
||||
// Apply queued phases
|
||||
if (this.queuedPhases.length) {
|
||||
globalScene.appendToPhase(this.queuedPhases, MoveEndPhase);
|
||||
}
|
||||
const moveType = user.getMoveType(this.move, true);
|
||||
if (this.move.category !== MoveCategory.STATUS && !user.stellarTypesBoosted.includes(moveType)) {
|
||||
user.stellarTypesBoosted.push(moveType);
|
||||
|
@ -398,11 +397,6 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
}
|
||||
|
||||
this.updateSubstitutes();
|
||||
|
||||
// Apply queued phases
|
||||
if (this.queuedPhases.length) {
|
||||
globalScene.appendToPhase(this.queuedPhases, MoveEndPhase);
|
||||
}
|
||||
this.end();
|
||||
}
|
||||
|
||||
|
@ -592,7 +586,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
* @param target - {@linkcode Pokemon} the target to check for protection
|
||||
* @param move - The {@linkcode Move} being used
|
||||
*/
|
||||
private protectedCheck(user: Pokemon, target: Pokemon, move: Move) {
|
||||
private protectedCheck(user: Pokemon, target: Pokemon) {
|
||||
/** The {@linkcode ArenaTagSide} to which the target belongs */
|
||||
const targetSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||
/** Has the invoked move been cancelled by conditional protection (e.g Quick Guard)? */
|
||||
|
@ -608,7 +602,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
hasConditionalProtectApplied,
|
||||
user,
|
||||
target,
|
||||
move.id,
|
||||
this.move.id,
|
||||
bypassIgnoreProtect,
|
||||
);
|
||||
}
|
||||
|
@ -648,18 +642,21 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
return [HitCheckResult.ERROR, 0];
|
||||
}
|
||||
|
||||
// Moves targeting the user or field bypass accuracy and effectiveness checks
|
||||
// Moves targeting the user bypass all checks
|
||||
if (move.moveTarget === MoveTarget.USER) {
|
||||
return [HitCheckResult.HIT, 1];
|
||||
}
|
||||
|
||||
const fieldTargeted = isFieldTargeted(move);
|
||||
|
||||
// If the target is not on the field, cancel the hit check
|
||||
if (!target.isActive(true)) {
|
||||
if (!target.isActive(true) && !fieldTargeted) {
|
||||
return [HitCheckResult.TARGET_NOT_ON_FIELD, 0];
|
||||
}
|
||||
|
||||
// If the target of the move is hidden by the effects of its commander ability, then this misses
|
||||
if (
|
||||
!fieldTargeted &&
|
||||
globalScene.currentBattle.double &&
|
||||
target.getAlly()?.getTag(BattlerTagType.COMMANDED)?.getSourcePokemon() === target
|
||||
) {
|
||||
|
@ -667,14 +664,15 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
}
|
||||
|
||||
/** Whether both accuracy and invulnerability checks can be skipped */
|
||||
const bypassAccAndInvuln = this.checkBypassAccAndInvuln(target);
|
||||
const bypassAccAndInvuln = fieldTargeted || this.checkBypassAccAndInvuln(target);
|
||||
const semiInvulnerableTag = target.getTag(SemiInvulnerableTag);
|
||||
|
||||
if (semiInvulnerableTag && !bypassAccAndInvuln && !this.checkBypassSemiInvuln(semiInvulnerableTag)) {
|
||||
return [HitCheckResult.MISS, 0];
|
||||
}
|
||||
|
||||
if (this.protectedCheck(user, target, move)) {
|
||||
if (!fieldTargeted && this.protectedCheck(user, target)) {
|
||||
console.log("====== Protected ========");
|
||||
return [HitCheckResult.PROTECTED, 0];
|
||||
}
|
||||
|
||||
|
@ -682,6 +680,12 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
return [HitCheckResult.REFLECTED, 0];
|
||||
}
|
||||
|
||||
// After the magic bounce check, field targeted moves are always successful
|
||||
if (fieldTargeted) {
|
||||
console.log("====== Field targeted moves overriding hit check ========");
|
||||
return [HitCheckResult.HIT, 1];
|
||||
}
|
||||
|
||||
const cancelNoEffectMessage = new BooleanHolder(false);
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,6 @@ import { PokemonType } from "#enums/pokemon-type";
|
|||
import { Abilities } from "#app/enums/abilities";
|
||||
import { Moves } from "#app/enums/moves";
|
||||
import { Species } from "#app/enums/species";
|
||||
import { HitResult } from "#app/field/pokemon";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
@ -38,13 +37,13 @@ describe("Abilities - Galvanize", () => {
|
|||
});
|
||||
|
||||
it("should change Normal-type attacks to Electric type and boost their power", async () => {
|
||||
await game.startBattle();
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
vi.spyOn(playerPokemon, "getMoveType");
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
vi.spyOn(enemyPokemon, "apply");
|
||||
const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness");
|
||||
|
||||
const move = allMoves[Moves.TACKLE];
|
||||
vi.spyOn(move, "calculateBattlePower");
|
||||
|
@ -54,21 +53,23 @@ describe("Abilities - Galvanize", () => {
|
|||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
||||
expect(playerPokemon.getMoveType).toHaveLastReturnedWith(PokemonType.ELECTRIC);
|
||||
expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.EFFECTIVE);
|
||||
expect(spy).toHaveReturnedWith(1);
|
||||
expect(move.calculateBattlePower).toHaveReturnedWith(48);
|
||||
expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp());
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it("should cause Normal-type attacks to activate Volt Absorb", async () => {
|
||||
game.override.enemyAbility(Abilities.VOLT_ABSORB);
|
||||
|
||||
await game.startBattle();
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
vi.spyOn(playerPokemon, "getMoveType");
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
vi.spyOn(enemyPokemon, "apply");
|
||||
const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness");
|
||||
|
||||
enemyPokemon.hp = Math.floor(enemyPokemon.getMaxHp() * 0.8);
|
||||
|
||||
|
@ -77,37 +78,37 @@ describe("Abilities - Galvanize", () => {
|
|||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
||||
expect(playerPokemon.getMoveType).toHaveLastReturnedWith(PokemonType.ELECTRIC);
|
||||
expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.NO_EFFECT);
|
||||
expect(spy).toHaveReturnedWith(0);
|
||||
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp());
|
||||
});
|
||||
|
||||
it("should not change the type of variable-type moves", async () => {
|
||||
game.override.enemySpecies(Species.MIGHTYENA);
|
||||
|
||||
await game.startBattle([Species.ESPEON]);
|
||||
await game.classicMode.startBattle([Species.ESPEON]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
vi.spyOn(playerPokemon, "getMoveType");
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
vi.spyOn(enemyPokemon, "apply");
|
||||
const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness");
|
||||
|
||||
game.move.select(Moves.REVELATION_DANCE);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
|
||||
expect(playerPokemon.getMoveType).not.toHaveLastReturnedWith(PokemonType.ELECTRIC);
|
||||
expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.NO_EFFECT);
|
||||
expect(spy).toHaveReturnedWith(0);
|
||||
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp());
|
||||
});
|
||||
|
||||
it("should affect all hits of a Normal-type multi-hit move", async () => {
|
||||
await game.startBattle();
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
vi.spyOn(playerPokemon, "getMoveType");
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
vi.spyOn(enemyPokemon, "apply");
|
||||
const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness");
|
||||
|
||||
game.move.select(Moves.FURY_SWIPES);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
|
@ -125,6 +126,6 @@ describe("Abilities - Galvanize", () => {
|
|||
expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp);
|
||||
}
|
||||
|
||||
expect(enemyPokemon.apply).not.toHaveReturnedWith(HitResult.NO_EFFECT);
|
||||
expect(spy).not.toHaveReturnedWith(0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@ import { MoveEndPhase } from "#app/phases/move-end-phase";
|
|||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { HitCheckResult } from "#enums/hit-check-result";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
|
||||
|
@ -28,6 +29,7 @@ describe("Abilities - No Guard", () => {
|
|||
.moveset(Moves.ZAP_CANNON)
|
||||
.ability(Abilities.NO_GUARD)
|
||||
.enemyLevel(200)
|
||||
.enemySpecies(Species.SNORLAX)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
});
|
||||
|
@ -48,7 +50,7 @@ describe("Abilities - No Guard", () => {
|
|||
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
|
||||
expect(moveEffectPhase.hitCheck).toHaveReturnedWith(true);
|
||||
expect(moveEffectPhase.hitCheck).toHaveReturnedWith([HitCheckResult.HIT, 1]);
|
||||
});
|
||||
|
||||
it("should guarantee double battle with any one LURE", async () => {
|
||||
|
|
|
@ -52,7 +52,7 @@ describe("Abilities - Shield Dust", () => {
|
|||
|
||||
// Shield Dust negates secondary effect
|
||||
const phase = game.scene.getCurrentPhase() as MoveEffectPhase;
|
||||
const move = phase.move.getMove();
|
||||
const move = phase.move;
|
||||
expect(move.id).toBe(Moves.AIR_SLASH);
|
||||
|
||||
const chance = new NumberHolder(move.chance);
|
||||
|
|
|
@ -25,7 +25,6 @@ describe("Abilities - Super Luck", () => {
|
|||
.moveset([Moves.TACKLE])
|
||||
.ability(Abilities.SUPER_LUCK)
|
||||
.battleStyle("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
|
|
|
@ -2,7 +2,6 @@ import { BattlerIndex } from "#app/battle";
|
|||
import { Abilities } from "#app/enums/abilities";
|
||||
import { Moves } from "#app/enums/moves";
|
||||
import { Species } from "#app/enums/species";
|
||||
import { HitResult } from "#app/field/pokemon";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
@ -87,13 +86,15 @@ describe("Abilities - Tera Shell", () => {
|
|||
await game.classicMode.startBattle([Species.CHARIZARD]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
vi.spyOn(playerPokemon, "apply");
|
||||
const spy = vi.spyOn(playerPokemon, "getMoveEffectiveness");
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(playerPokemon.apply).toHaveLastReturnedWith(HitResult.EFFECTIVE);
|
||||
expect(spy).toHaveLastReturnedWith(1);
|
||||
expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp() - 40);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it("should change the effectiveness of all strikes of a multi-strike move", async () => {
|
||||
|
@ -102,7 +103,7 @@ describe("Abilities - Tera Shell", () => {
|
|||
await game.classicMode.startBattle([Species.SNORLAX]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
vi.spyOn(playerPokemon, "apply");
|
||||
const spy = vi.spyOn(playerPokemon, "getMoveEffectiveness");
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
|
||||
|
@ -110,8 +111,9 @@ describe("Abilities - Tera Shell", () => {
|
|||
await game.move.forceHit();
|
||||
for (let i = 0; i < 2; i++) {
|
||||
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||
expect(playerPokemon.apply).toHaveLastReturnedWith(HitResult.NOT_VERY_EFFECTIVE);
|
||||
expect(spy).toHaveLastReturnedWith(0.5);
|
||||
}
|
||||
expect(playerPokemon.apply).toHaveReturnedTimes(2);
|
||||
expect(spy).toHaveReturnedTimes(2);
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -36,8 +36,7 @@ describe("Items - Dire Hit", () => {
|
|||
.enemyMoveset(Moves.SPLASH)
|
||||
.moveset([Moves.POUND])
|
||||
.startingHeldItems([{ name: "DIRE_HIT" }])
|
||||
.battleStyle("single")
|
||||
.disableCrits();
|
||||
.battleStyle("single");
|
||||
}, 20000);
|
||||
|
||||
it("should raise CRIT stage by 1", async () => {
|
||||
|
|
|
@ -28,7 +28,6 @@ describe("Items - Leek", () => {
|
|||
.enemyMoveset([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH])
|
||||
.startingHeldItems([{ name: "LEEK" }])
|
||||
.moveset([Moves.TACKLE])
|
||||
.disableCrits()
|
||||
.battleStyle("single");
|
||||
});
|
||||
|
||||
|
|
|
@ -27,8 +27,7 @@ describe("Items - Scope Lens", () => {
|
|||
.enemyMoveset(Moves.SPLASH)
|
||||
.moveset([Moves.POUND])
|
||||
.startingHeldItems([{ name: "SCOPE_LENS" }])
|
||||
.battleStyle("single")
|
||||
.disableCrits();
|
||||
.battleStyle("single");
|
||||
}, 20000);
|
||||
|
||||
it("should raise CRIT stage by 1", async () => {
|
||||
|
|
|
@ -57,12 +57,12 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
|
@ -77,12 +77,12 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
|
@ -97,7 +97,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
|
@ -107,7 +107,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||
await game.phaseInterceptor.runFrom(MovePhase).to(MoveEndPhase);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
|
@ -123,7 +123,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
|
@ -132,7 +132,7 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||
await game.phaseInterceptor.runFrom(MovePhase).to(MoveEndPhase);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
}, 20000);
|
||||
|
@ -147,12 +147,12 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
|
@ -191,22 +191,22 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
|
@ -245,22 +245,22 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionBolt.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.moveId).toBe(fusionFlare.id);
|
||||
expect((game.scene.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
}, 20000);
|
||||
|
|
|
@ -4,7 +4,6 @@ import { allMoves, TeraMoveCategoryAttr } from "#app/data/moves/move";
|
|||
import type Move from "#app/data/moves/move";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { Abilities } from "#app/enums/abilities";
|
||||
import { HitResult } from "#app/field/pokemon";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
|
@ -49,9 +48,9 @@ describe("Moves - Tera Blast", () => {
|
|||
|
||||
it("changes type to match user's tera type", async () => {
|
||||
game.override.enemySpecies(Species.FURRET);
|
||||
await game.startBattle();
|
||||
await game.classicMode.startBattle();
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
vi.spyOn(enemyPokemon, "apply");
|
||||
const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness");
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
playerPokemon.teraType = PokemonType.FIGHTING;
|
||||
|
@ -61,11 +60,11 @@ describe("Moves - Tera Blast", () => {
|
|||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||
|
||||
expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.SUPER_EFFECTIVE);
|
||||
expect(spy).toHaveReturnedWith(2);
|
||||
}, 20000);
|
||||
|
||||
it("increases power if user is Stellar tera type", async () => {
|
||||
await game.startBattle();
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
playerPokemon.teraType = PokemonType.STELLAR;
|
||||
|
@ -79,25 +78,25 @@ describe("Moves - Tera Blast", () => {
|
|||
}, 20000);
|
||||
|
||||
it("is super effective against terastallized targets if user is Stellar tera type", async () => {
|
||||
await game.startBattle();
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
playerPokemon.teraType = PokemonType.STELLAR;
|
||||
playerPokemon.isTerastallized = true;
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
vi.spyOn(enemyPokemon, "apply");
|
||||
const spy = vi.spyOn(enemyPokemon, "getMoveEffectiveness");
|
||||
enemyPokemon.isTerastallized = true;
|
||||
|
||||
game.move.select(Moves.TERA_BLAST);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||
|
||||
expect(enemyPokemon.apply).toHaveReturnedWith(HitResult.SUPER_EFFECTIVE);
|
||||
expect(spy).toHaveReturnedWith(2);
|
||||
});
|
||||
|
||||
it("uses the higher ATK for damage calculation", async () => {
|
||||
await game.startBattle();
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
playerPokemon.stats[Stat.ATK] = 100;
|
||||
|
@ -112,7 +111,7 @@ describe("Moves - Tera Blast", () => {
|
|||
});
|
||||
|
||||
it("uses the higher SPATK for damage calculation", async () => {
|
||||
await game.startBattle();
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
playerPokemon.stats[Stat.ATK] = 1;
|
||||
|
@ -127,7 +126,7 @@ describe("Moves - Tera Blast", () => {
|
|||
|
||||
it("should stay as a special move if ATK turns lower than SPATK mid-turn", async () => {
|
||||
game.override.enemyMoveset([Moves.CHARM]);
|
||||
await game.startBattle();
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
playerPokemon.stats[Stat.ATK] = 51;
|
||||
|
@ -145,7 +144,7 @@ describe("Moves - Tera Blast", () => {
|
|||
game.override
|
||||
.startingHeldItems([{ name: "SPECIES_STAT_BOOSTER", type: "THICK_CLUB" }])
|
||||
.starterSpecies(Species.CUBONE);
|
||||
await game.startBattle();
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
|
||||
|
@ -163,7 +162,7 @@ describe("Moves - Tera Blast", () => {
|
|||
|
||||
it("does not change its move category from stat changes due to abilities", async () => {
|
||||
game.override.ability(Abilities.HUGE_POWER);
|
||||
await game.startBattle();
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
playerPokemon.stats[Stat.ATK] = 50;
|
||||
|
@ -178,7 +177,7 @@ describe("Moves - Tera Blast", () => {
|
|||
});
|
||||
|
||||
it("causes stat drops if user is Stellar tera type", async () => {
|
||||
await game.startBattle();
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||
playerPokemon.teraType = PokemonType.STELLAR;
|
||||
|
|
|
@ -18,29 +18,29 @@ import { vi } from "vitest";
|
|||
*/
|
||||
export class MoveHelper extends GameManagerHelper {
|
||||
/**
|
||||
* Intercepts {@linkcode MoveEffectPhase} and mocks the
|
||||
* {@linkcode MoveEffectPhase.hitCheck | hitCheck}'s return value to `true`.
|
||||
* Used to force a move to hit.
|
||||
* Intercepts {@linkcode MoveEffectPhase} and mocks the phase's move's
|
||||
* accuracy to -1, guaranteeing a hit.
|
||||
*/
|
||||
public async forceHit(): Promise<void> {
|
||||
await this.game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
vi.spyOn(this.game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValue(true);
|
||||
const moveEffectPhase = this.game.scene.getCurrentPhase() as MoveEffectPhase;
|
||||
vi.spyOn(moveEffectPhase.move, "calculateBattleAccuracy").mockReturnValue(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercepts {@linkcode MoveEffectPhase} and mocks the
|
||||
* {@linkcode MoveEffectPhase.hitCheck | hitCheck}'s return value to `false`.
|
||||
* Used to force a move to miss.
|
||||
* Intercepts {@linkcode MoveEffectPhase} and mocks the phase's move's accuracy
|
||||
* to 0, guaranteeing a miss.
|
||||
* @param firstTargetOnly - Whether the move should force miss on the first target only, in the case of multi-target moves.
|
||||
*/
|
||||
public async forceMiss(firstTargetOnly = false): Promise<void> {
|
||||
await this.game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
const hitCheck = vi.spyOn(this.game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck");
|
||||
const moveEffectPhase = this.game.scene.getCurrentPhase() as MoveEffectPhase;
|
||||
const accuracy = vi.spyOn(moveEffectPhase.move, "calculateBattleAccuracy");
|
||||
|
||||
if (firstTargetOnly) {
|
||||
hitCheck.mockReturnValueOnce(false);
|
||||
accuracy.mockReturnValueOnce(0);
|
||||
} else {
|
||||
hitCheck.mockReturnValue(false);
|
||||
accuracy.mockReturnValue(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue