mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-05 07:52:17 +02:00
Revert leaveField tests for other PR
This commit is contained in:
parent
a42b7ed9f7
commit
ebe4878a2b
@ -1250,7 +1250,7 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the move type change attribute can be applied
|
* Determine if the move type change attribute can be applied
|
||||||
*
|
*
|
||||||
* Can be applied if:
|
* Can be applied if:
|
||||||
* - The ability's condition is met, e.g. pixilate only boosts normal moves,
|
* - The ability's condition is met, e.g. pixilate only boosts normal moves,
|
||||||
* - The move is not forbidden from having its type changed by an ability, e.g. {@linkcode Moves.MULTI_ATTACK}
|
* - The move is not forbidden from having its type changed by an ability, e.g. {@linkcode Moves.MULTI_ATTACK}
|
||||||
@ -1266,7 +1266,7 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr {
|
|||||||
*/
|
*/
|
||||||
override canApplyPreAttack(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _defender: Pokemon | null, move: Move, _args: [NumberHolder?, NumberHolder?, ...any]): boolean {
|
override canApplyPreAttack(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _defender: Pokemon | null, move: Move, _args: [NumberHolder?, NumberHolder?, ...any]): boolean {
|
||||||
return (!this.condition || this.condition(pokemon, _defender, move)) &&
|
return (!this.condition || this.condition(pokemon, _defender, move)) &&
|
||||||
!noAbilityTypeOverrideMoves.has(move.id) &&
|
!noAbilityTypeOverrideMoves.has(move.id) &&
|
||||||
(!pokemon.isTerastallized ||
|
(!pokemon.isTerastallized ||
|
||||||
(move.id !== Moves.TERA_BLAST &&
|
(move.id !== Moves.TERA_BLAST &&
|
||||||
(move.id !== Moves.TERA_STARSTORM || pokemon.getTeraType() !== PokemonType.STELLAR || !pokemon.hasSpecies(Species.TERAPAGOS))));
|
(move.id !== Moves.TERA_STARSTORM || pokemon.getTeraType() !== PokemonType.STELLAR || !pokemon.hasSpecies(Species.TERAPAGOS))));
|
||||||
|
@ -110,10 +110,14 @@ export function ForceSwitch<TBase extends SubMoveOrAbAttr>(Base: TBase) {
|
|||||||
this.tryFleeWildPokemon(switchOutTarget);
|
this.tryFleeWildPokemon(switchOutTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NB: `prependToPhase` is used here to ensure that the switch happens before the move ends
|
||||||
|
// and `arena.ignoreAbilities` is reset.
|
||||||
|
// This ensures ability ignore effects will persist for the duration of the switch (for hazards).
|
||||||
|
|
||||||
private trySwitchPlayerPokemon(switchOutTarget: PlayerPokemon): void {
|
private trySwitchPlayerPokemon(switchOutTarget: PlayerPokemon): void {
|
||||||
// If not forced to switch, add a SwitchPhase to allow picking the next switched in Pokemon.
|
// If not forced to switch, add a SwitchPhase to allow picking the next switched in Pokemon.
|
||||||
if (this.switchType !== SwitchType.FORCE_SWITCH) {
|
if (this.switchType !== SwitchType.FORCE_SWITCH) {
|
||||||
globalScene.appendToPhase(
|
globalScene.prependToPhase(
|
||||||
new SwitchPhase(this.switchType, switchOutTarget.getFieldIndex(), true, true),
|
new SwitchPhase(this.switchType, switchOutTarget.getFieldIndex(), true, true),
|
||||||
MoveEndPhase,
|
MoveEndPhase,
|
||||||
);
|
);
|
||||||
@ -124,7 +128,7 @@ export function ForceSwitch<TBase extends SubMoveOrAbAttr>(Base: TBase) {
|
|||||||
const reservePartyMembers = globalScene.getBackupPartyMemberIndices(true);
|
const reservePartyMembers = globalScene.getBackupPartyMemberIndices(true);
|
||||||
const switchInIndex = reservePartyMembers[switchOutTarget.randSeedInt(reservePartyMembers.length)];
|
const switchInIndex = reservePartyMembers[switchOutTarget.randSeedInt(reservePartyMembers.length)];
|
||||||
|
|
||||||
globalScene.appendToPhase(
|
globalScene.prependToPhase(
|
||||||
new SwitchSummonPhase(this.switchType, switchOutTarget.getFieldIndex(), switchInIndex, false, true),
|
new SwitchSummonPhase(this.switchType, switchOutTarget.getFieldIndex(), switchInIndex, false, true),
|
||||||
MoveEndPhase,
|
MoveEndPhase,
|
||||||
);
|
);
|
||||||
@ -144,7 +148,7 @@ export function ForceSwitch<TBase extends SubMoveOrAbAttr>(Base: TBase) {
|
|||||||
this.switchType === SwitchType.FORCE_SWITCH
|
this.switchType === SwitchType.FORCE_SWITCH
|
||||||
? reservePartyIndices[switchOutTarget.randSeedInt(reservePartyIndices.length)]
|
? reservePartyIndices[switchOutTarget.randSeedInt(reservePartyIndices.length)]
|
||||||
: (globalScene.currentBattle.trainer.getNextSummonIndex(switchOutTarget.trainerSlot) ?? 0);
|
: (globalScene.currentBattle.trainer.getNextSummonIndex(switchOutTarget.trainerSlot) ?? 0);
|
||||||
globalScene.appendToPhase(
|
globalScene.prependToPhase(
|
||||||
new SwitchSummonPhase(this.switchType, switchOutTarget.getFieldIndex(), summonIndex, false, false),
|
new SwitchSummonPhase(this.switchType, switchOutTarget.getFieldIndex(), summonIndex, false, false),
|
||||||
MoveEndPhase,
|
MoveEndPhase,
|
||||||
);
|
);
|
||||||
|
@ -1336,7 +1336,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* @see {@linkcode SubstituteTag}
|
* @see {@linkcode SubstituteTag}
|
||||||
* @see {@linkcode getFieldPositionOffset}
|
* @see {@linkcode getFieldPositionOffset}
|
||||||
*/
|
*/
|
||||||
getSubstituteOffset(): [number, number] {
|
getSubstituteOffset(): [x: number, y: number] {
|
||||||
return this.isPlayer() ? [-30, 10] : [30, -10];
|
return this.isPlayer() ? [-30, 10] : [30, -10];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,8 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
faintPokemon.getTag(BattlerTagType.GRUDGE)?.lapse(faintPokemon, BattlerTagLapseType.CUSTOM, this.source);
|
faintPokemon.getTag(BattlerTagType.GRUDGE)?.lapse(faintPokemon, BattlerTagLapseType.CUSTOM, this.source);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for reviver seed
|
faintPokemon.resetSummonData();
|
||||||
|
|
||||||
if (!this.preventInstantRevive) {
|
if (!this.preventInstantRevive) {
|
||||||
const instantReviveModifier = globalScene.applyModifier(
|
const instantReviveModifier = globalScene.applyModifier(
|
||||||
PokemonInstantReviveModifier,
|
PokemonInstantReviveModifier,
|
||||||
@ -70,7 +71,6 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
) as PokemonInstantReviveModifier;
|
) as PokemonInstantReviveModifier;
|
||||||
|
|
||||||
if (instantReviveModifier) {
|
if (instantReviveModifier) {
|
||||||
faintPokemon.resetSummonData();
|
|
||||||
faintPokemon.loseHeldItem(instantReviveModifier);
|
faintPokemon.loseHeldItem(instantReviveModifier);
|
||||||
globalScene.updateModifiers(this.player);
|
globalScene.updateModifiers(this.player);
|
||||||
return this.end();
|
return this.end();
|
||||||
@ -179,11 +179,11 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
} else {
|
} else {
|
||||||
globalScene.unshiftPhase(new VictoryPhase(this.battlerIndex));
|
globalScene.unshiftPhase(new VictoryPhase(this.battlerIndex));
|
||||||
if ([BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType)) {
|
if ([BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType)) {
|
||||||
const reservePartyIndices = globalScene.getBackupPartyMemberIndices(
|
const hasReservePartyMember = !!globalScene
|
||||||
false,
|
.getEnemyParty()
|
||||||
(pokemon as EnemyPokemon).trainerSlot,
|
.filter(p => p.isActive() && !p.isOnField() && p.trainerSlot === (pokemon as EnemyPokemon).trainerSlot)
|
||||||
);
|
.length;
|
||||||
if (reservePartyIndices.length) {
|
if (hasReservePartyMember) {
|
||||||
globalScene.pushPhase(new SwitchSummonPhase(SwitchType.SWITCH, this.fieldIndex, -1, false, false));
|
globalScene.pushPhase(new SwitchSummonPhase(SwitchType.SWITCH, this.fieldIndex, -1, false, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,7 +217,6 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
globalScene.addFaintedEnemyScore(pokemon as EnemyPokemon);
|
globalScene.addFaintedEnemyScore(pokemon as EnemyPokemon);
|
||||||
globalScene.currentBattle.addPostBattleLoot(pokemon as EnemyPokemon);
|
globalScene.currentBattle.addPostBattleLoot(pokemon as EnemyPokemon);
|
||||||
}
|
}
|
||||||
// TODO: Do we need to leave the field here & now as opposed to during `switchSummonPhase`?
|
|
||||||
pokemon.leaveField();
|
pokemon.leaveField();
|
||||||
this.end();
|
this.end();
|
||||||
},
|
},
|
||||||
|
@ -274,6 +274,8 @@ export class SummonPhase extends PartyMemberPokemonPhase {
|
|||||||
globalScene.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex()));
|
globalScene.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pokemon.resetTurnData();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!this.loaded ||
|
!this.loaded ||
|
||||||
[BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType) ||
|
[BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType) ||
|
||||||
|
@ -165,8 +165,13 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||||||
party[this.slotIndex] = this.lastPokemon;
|
party[this.slotIndex] = this.lastPokemon;
|
||||||
party[this.fieldIndex] = switchedInPokemon;
|
party[this.fieldIndex] = switchedInPokemon;
|
||||||
const showTextAndSummon = () => {
|
const showTextAndSummon = () => {
|
||||||
// TODO: Should this remove the info container?
|
// We don't reset temp effects here as we need to transfer them to tne new pokemon
|
||||||
this.lastPokemon.leaveField(![SwitchType.BATON_PASS, SwitchType.SHED_TAIL].includes(this.switchType), false);
|
// TODO: When should this remove the info container?
|
||||||
|
// Force switch moves did it prior
|
||||||
|
this.lastPokemon.leaveField(
|
||||||
|
![SwitchType.BATON_PASS, SwitchType.SHED_TAIL].includes(this.switchType),
|
||||||
|
this.doReturn,
|
||||||
|
);
|
||||||
globalScene.ui.showText(
|
globalScene.ui.showText(
|
||||||
this.player
|
this.player
|
||||||
? i18next.t("battle:playerGo", {
|
? i18next.t("battle:playerGo", {
|
||||||
@ -184,13 +189,11 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||||||
* If this switch is passing a Substitute, make the switched Pokemon matches the returned Pokemon's state as it left.
|
* If this switch is passing a Substitute, make the switched Pokemon matches the returned Pokemon's state as it left.
|
||||||
* Otherwise, clear any persisting tags on the returned Pokemon.
|
* Otherwise, clear any persisting tags on the returned Pokemon.
|
||||||
*/
|
*/
|
||||||
if (this.switchType === SwitchType.BATON_PASS || this.switchType === SwitchType.SHED_TAIL) {
|
const substitute = this.lastPokemon.getTag(SubstituteTag);
|
||||||
const substitute = this.lastPokemon.getTag(SubstituteTag);
|
if ((this.switchType === SwitchType.BATON_PASS || this.switchType === SwitchType.SHED_TAIL) && substitute) {
|
||||||
if (substitute) {
|
switchedInPokemon.x += this.lastPokemon.getSubstituteOffset()[0];
|
||||||
switchedInPokemon.x += this.lastPokemon.getSubstituteOffset()[0];
|
switchedInPokemon.y += this.lastPokemon.getSubstituteOffset()[1];
|
||||||
switchedInPokemon.y += this.lastPokemon.getSubstituteOffset()[1];
|
switchedInPokemon.setAlpha(0.5);
|
||||||
switchedInPokemon.setAlpha(0.5);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.summon();
|
this.summon();
|
||||||
};
|
};
|
||||||
@ -209,35 +212,35 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||||||
onEnd(): void {
|
onEnd(): void {
|
||||||
super.onEnd();
|
super.onEnd();
|
||||||
|
|
||||||
const pokemon = this.getPokemon();
|
const activePokemon = this.getPokemon();
|
||||||
|
|
||||||
// If not switching at start of battle, reset turn counts and temp data on the newly sent in Pokemon
|
// If not switching at start of battle, reset turn counts and temp data on the newly sent in Pokemon
|
||||||
// Needed as we increment turn counters in `TurnEndPhase`.
|
// Needed as we increment turn counters in `TurnEndPhase`.
|
||||||
if (this.switchType !== SwitchType.INITIAL_SWITCH) {
|
if (this.switchType !== SwitchType.INITIAL_SWITCH) {
|
||||||
// No need to reset turn/summon data for initial switch
|
// No need to reset turn/summon data for initial switch
|
||||||
// (since both get initialized to an empty object on object creation)
|
// (since both get initialized to an empty object on object creation)
|
||||||
this.lastPokemon.resetTurnData();
|
activePokemon.resetTurnData();
|
||||||
this.lastPokemon.resetSummonData();
|
activePokemon.resetSummonData();
|
||||||
pokemon.tempSummonData.turnCount--;
|
activePokemon.tempSummonData.turnCount--;
|
||||||
pokemon.tempSummonData.waveTurnCount--;
|
activePokemon.tempSummonData.waveTurnCount--;
|
||||||
pokemon.turnData.switchedInThisTurn = true;
|
activePokemon.turnData.switchedInThisTurn = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Baton Pass over any eligible effects or substitutes before resetting the last pokemon's temporary data.
|
// Baton Pass over any eligible effects or substitutes before resetting the last pokemon's temporary data.
|
||||||
if (this.switchType === SwitchType.BATON_PASS) {
|
if (this.switchType === SwitchType.BATON_PASS) {
|
||||||
pokemon.transferSummon(this.lastPokemon);
|
activePokemon.transferSummon(this.lastPokemon);
|
||||||
this.lastPokemon.resetTurnData();
|
this.lastPokemon.resetTurnData();
|
||||||
this.lastPokemon.resetSummonData();
|
this.lastPokemon.resetSummonData();
|
||||||
} else if (this.switchType === SwitchType.SHED_TAIL) {
|
} else if (this.switchType === SwitchType.SHED_TAIL) {
|
||||||
const subTag = this.lastPokemon.getTag(SubstituteTag);
|
const subTag = this.lastPokemon.getTag(SubstituteTag);
|
||||||
if (subTag) {
|
if (subTag) {
|
||||||
pokemon.summonData.tags.push(subTag);
|
activePokemon.summonData.tags.push(subTag);
|
||||||
}
|
}
|
||||||
this.lastPokemon.resetTurnData();
|
this.lastPokemon.resetTurnData();
|
||||||
this.lastPokemon.resetSummonData();
|
this.lastPokemon.resetSummonData();
|
||||||
}
|
}
|
||||||
|
|
||||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
|
globalScene.triggerPokemonFormChange(activePokemon, SpeciesFormChangeActiveTrigger, true);
|
||||||
// Reverts to weather-based forms when weather suppressors (Cloud Nine/Air Lock) are switched out
|
// Reverts to weather-based forms when weather suppressors (Cloud Nine/Air Lock) are switched out
|
||||||
globalScene.arena.triggerWeatherBasedFormChanges();
|
globalScene.arena.triggerWeatherBasedFormChanges();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { ArenaTagSide } from "#app/data/arena-tag";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import GameManager from "#test/testUtils/gameManager";
|
import GameManager from "#test/testUtils/gameManager";
|
||||||
@ -24,29 +26,60 @@ describe("Abilities - Mold Breaker", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
game.override
|
game.override
|
||||||
.moveset([Moves.SPLASH])
|
.moveset([Moves.ERUPTION, Moves.EARTHQUAKE, Moves.DRAGON_TAIL])
|
||||||
.ability(Abilities.MOLD_BREAKER)
|
.ability(Abilities.MOLD_BREAKER)
|
||||||
.battleStyle("single")
|
.battleStyle("single")
|
||||||
.disableCrits()
|
.disableCrits()
|
||||||
.enemySpecies(Species.MAGIKARP)
|
.enemySpecies(Species.MAGIKARP)
|
||||||
.enemyAbility(Abilities.BALL_FETCH)
|
.enemyPassiveAbility(Abilities.NO_GUARD)
|
||||||
.enemyMoveset(Moves.SPLASH);
|
.enemyMoveset(Moves.SPLASH);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should turn off the ignore abilities arena variable after the user's move", async () => {
|
it("should ignore ignorable abilities during the move's execution", async () => {
|
||||||
game.override
|
game.override.startingLevel(100).enemyLevel(2).enemyAbility(Abilities.STURDY);
|
||||||
.enemyMoveset(Moves.SPLASH)
|
|
||||||
.ability(Abilities.MOLD_BREAKER)
|
|
||||||
.moveset([Moves.ERUPTION])
|
|
||||||
.startingLevel(100)
|
|
||||||
.enemyLevel(2);
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.classicMode.startBattle([Species.MAGIKARP]);
|
||||||
const enemy = game.scene.getEnemyPokemon()!;
|
|
||||||
|
|
||||||
expect(enemy.isFainted()).toBe(false);
|
game.move.select(Moves.ERUPTION);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(game.scene.getEnemyPokemon()?.isFainted()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should turn off ignore abilities arena variable after the user's move concludes", async () => {
|
||||||
|
game.override.startingLevel(100).enemyLevel(2);
|
||||||
|
await game.classicMode.startBattle([Species.MAGIKARP]);
|
||||||
|
|
||||||
|
expect(globalScene.arena.ignoreAbilities).toBe(false);
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
await game.phaseInterceptor.to("MoveEndPhase", true);
|
|
||||||
|
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||||
|
expect(globalScene.arena.ignoreAbilities).toBe(true);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
expect(globalScene.arena.ignoreAbilities).toBe(false);
|
expect(globalScene.arena.ignoreAbilities).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should keep Levitate opponents grounded when using force switch moves", async () => {
|
||||||
|
game.override.enemyAbility(Abilities.LEVITATE).enemySpecies(Species.WEEZING).startingWave(8); // first rival battle; guaranteed 2 mon party
|
||||||
|
|
||||||
|
// Setup toxic spikes and stealth rock
|
||||||
|
game.scene.arena.addTag(ArenaTagType.TOXIC_SPIKES, -1, Moves.TOXIC_SPIKES, 1, ArenaTagSide.ENEMY);
|
||||||
|
game.scene.arena.addTag(ArenaTagType.SPIKES, -1, Moves.CEASELESS_EDGE, 1, ArenaTagSide.ENEMY);
|
||||||
|
await game.classicMode.startBattle([Species.MAGIKARP]);
|
||||||
|
|
||||||
|
const [weezing1, weezing2] = game.scene.getEnemyParty();
|
||||||
|
// Weezing's levitate prevented removal of Toxic Spikes, ignored Spikes damage
|
||||||
|
expect(game.scene.arena.getTagOnSide(ArenaTagType.TOXIC_SPIKES, ArenaTagSide.ENEMY)).toBeDefined();
|
||||||
|
expect(weezing1.getHpRatio()).toBe(1);
|
||||||
|
|
||||||
|
game.move.select(Moves.DRAGON_TAIL);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
// Levitate was ignored during the switch, causing Toxic Spikes to be removed and Spikes to deal damage
|
||||||
|
expect(weezing1.isOnField()).toBe(false);
|
||||||
|
expect(weezing2.isOnField()).toBe(true);
|
||||||
|
expect(weezing2.getHpRatio()).toBeCloseTo(0.75);
|
||||||
|
expect(game.scene.arena.getTagOnSide(ArenaTagType.TOXIC_SPIKES, ArenaTagSide.ENEMY)).toBeUndefined();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -349,7 +349,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
confirmNoSwitch();
|
confirmNoSwitch();
|
||||||
|
|
||||||
// Turn 2: get back enough HP that substitute doesn't put us under
|
// Turn 2: get back enough HP that substitute doesn't put us under
|
||||||
wimpod.hp = wimpod.getMaxHp() * 0.8;
|
wimpod.hp = wimpod.getMaxHp() * 0.78;
|
||||||
|
|
||||||
game.move.select(Moves.SUBSTITUTE);
|
game.move.select(Moves.SUBSTITUTE);
|
||||||
game.doSelectPartyPokemon(1);
|
game.doSelectPartyPokemon(1);
|
||||||
@ -373,7 +373,7 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
it("should disregard Shell Bell recovery while still activating it before switching", async () => {
|
it("should disregard Shell Bell recovery while still activating it before switching", async () => {
|
||||||
game.override
|
game.override
|
||||||
.moveset(Moves.DOUBLE_EDGE)
|
.moveset(Moves.DOUBLE_EDGE)
|
||||||
.enemyMoveset([Moves.SPLASH])
|
.enemyMoveset(Moves.SPLASH)
|
||||||
.startingHeldItems([{ name: "SHELL_BELL", count: 4 }]); // heals 50% of damage dealt, more than recoil takes away
|
.startingHeldItems([{ name: "SHELL_BELL", count: 4 }]); // heals 50% of damage dealt, more than recoil takes away
|
||||||
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
|
||||||
|
|
||||||
@ -382,10 +382,13 @@ describe("Abilities - Wimp Out", () => {
|
|||||||
|
|
||||||
game.move.select(Moves.DOUBLE_EDGE);
|
game.move.select(Moves.DOUBLE_EDGE);
|
||||||
game.doSelectPartyPokemon(1);
|
game.doSelectPartyPokemon(1);
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||||
|
|
||||||
// Wimp out activated before shell bell healing
|
// Wimp out check activated from recoil before shell bell procced, but did not deny the pokemon its recovery
|
||||||
|
expect(wimpod.turnData.damageTaken).toBeGreaterThan(0);
|
||||||
expect(wimpod.getHpRatio()).toBeGreaterThan(0.5);
|
expect(wimpod.getHpRatio()).toBeGreaterThan(0.5);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
confirmSwitch();
|
confirmSwitch();
|
||||||
expect(game.phaseInterceptor.log).toContain("PokemonHealPhase");
|
expect(game.phaseInterceptor.log).toContain("PokemonHealPhase");
|
||||||
});
|
});
|
||||||
|
@ -106,10 +106,9 @@ describe("Items - Reviver Seed", () => {
|
|||||||
|
|
||||||
// Self-damage tests
|
// Self-damage tests
|
||||||
it.each([
|
it.each([
|
||||||
{ moveType: "Relative Recoil", move: Moves.DOUBLE_EDGE },
|
{ moveType: "Recoil", move: Moves.DOUBLE_EDGE },
|
||||||
{ moveType: "HP% Recoil", move: Moves.CHLOROBLAST },
|
|
||||||
{ moveType: "Self-KO", move: Moves.EXPLOSION },
|
{ moveType: "Self-KO", move: Moves.EXPLOSION },
|
||||||
{ moveType: "Ghost-type Curse", move: Moves.CURSE },
|
{ moveType: "Self-Deduction", move: Moves.CURSE },
|
||||||
{ moveType: "Liquid Ooze", move: Moves.GIGA_DRAIN },
|
{ moveType: "Liquid Ooze", move: Moves.GIGA_DRAIN },
|
||||||
])("should not activate the holder's reviver seed from $moveType", async ({ move }) => {
|
])("should not activate the holder's reviver seed from $moveType", async ({ move }) => {
|
||||||
game.override
|
game.override
|
||||||
|
@ -32,34 +32,25 @@ describe("Moves - U-turn", () => {
|
|||||||
.disableCrits();
|
.disableCrits();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should switch the user out upon use", async () => {
|
it("triggers regenerator a single time when a regenerator user switches out with u-turn", async () => {
|
||||||
await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]);
|
// arrange
|
||||||
const [raichu, shuckle] = game.scene.getPlayerParty();
|
const playerHp = 1;
|
||||||
expect(raichu).toBeDefined();
|
|
||||||
expect(shuckle).toBeDefined();
|
|
||||||
|
|
||||||
expect(game.scene.getPlayerPokemon()!).toBe(raichu);
|
|
||||||
game.move.select(Moves.U_TURN);
|
|
||||||
game.doSelectPartyPokemon(1);
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
|
||||||
expect(game.scene.getPlayerPokemon()!).toBe(shuckle);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("triggers regenerator passive once upon switch", async () => {
|
|
||||||
game.override.ability(Abilities.REGENERATOR);
|
game.override.ability(Abilities.REGENERATOR);
|
||||||
await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]);
|
await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]);
|
||||||
game.scene.getPlayerPokemon()!.hp = 1;
|
game.scene.getPlayerPokemon()!.hp = playerHp;
|
||||||
|
|
||||||
|
// act
|
||||||
game.move.select(Moves.U_TURN);
|
game.move.select(Moves.U_TURN);
|
||||||
game.doSelectPartyPokemon(1);
|
game.doSelectPartyPokemon(1);
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
expect(game.scene.getPlayerParty()[1].hp).toBeGreaterThan(1);
|
// assert
|
||||||
|
expect(game.scene.getPlayerParty()[1].hp).toEqual(
|
||||||
|
Math.floor(game.scene.getPlayerParty()[1].getMaxHp() * 0.33 + playerHp),
|
||||||
|
);
|
||||||
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
||||||
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.SHUCKLE);
|
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.SHUCKLE);
|
||||||
});
|
}, 20000);
|
||||||
|
|
||||||
it("triggers rough skin on the u-turn user before a new pokemon is switched in", async () => {
|
it("triggers rough skin on the u-turn user before a new pokemon is switched in", async () => {
|
||||||
// arrange
|
// arrange
|
||||||
|
@ -1,240 +0,0 @@
|
|||||||
import { PokemonSummonData, PokemonTurnData } from "#app/field/pokemon";
|
|
||||||
import { Abilities } from "#enums/abilities";
|
|
||||||
import { BattleType } from "#enums/battle-type";
|
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
|
||||||
import { Moves } from "#enums/moves";
|
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import GameManager from "#test/testUtils/gameManager";
|
|
||||||
import Phaser from "phaser";
|
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
||||||
|
|
||||||
describe("Manual Switching -", () => {
|
|
||||||
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
|
|
||||||
.battleStyle("single")
|
|
||||||
.disableCrits()
|
|
||||||
.enemySpecies(Species.MAGIKARP)
|
|
||||||
.moveset(Moves.SPLASH)
|
|
||||||
.enemyMoveset(Moves.SPLASH)
|
|
||||||
.battleType(BattleType.TRAINER)
|
|
||||||
.enemyAbility(Abilities.BALL_FETCH);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Player", () => {
|
|
||||||
it("should only call leaveField once on the switched out pokemon", async () => {
|
|
||||||
await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]);
|
|
||||||
|
|
||||||
const [piloswine, mamoswine] = game.scene.getPlayerParty();
|
|
||||||
const piloLeaveSpy = vi.spyOn(piloswine, "leaveField");
|
|
||||||
const mamoLeaveSpy = vi.spyOn(mamoswine, "leaveField");
|
|
||||||
|
|
||||||
game.doSwitchPokemon(1);
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
expect(piloLeaveSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mamoLeaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should only reset summonData/turnData once per switch", async () => {
|
|
||||||
await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]);
|
|
||||||
|
|
||||||
const [piloswine, mamoswine] = game.scene.getPlayerParty();
|
|
||||||
const piloSummonSpy = vi.spyOn(piloswine, "resetSummonData");
|
|
||||||
const piloTurnSpy = vi.spyOn(piloswine, "resetTurnData");
|
|
||||||
const mamoSummonSpy = vi.spyOn(mamoswine, "resetSummonData");
|
|
||||||
const mamoTurnSpy = vi.spyOn(mamoswine, "resetTurnData");
|
|
||||||
|
|
||||||
game.doSwitchPokemon(1);
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
expect(piloSummonSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(piloTurnSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mamoSummonSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mamoTurnSpy).toHaveBeenCalledTimes(2); // once from switching, once at turn start
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not reset battleData/waveData upon switching", async () => {
|
|
||||||
await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]);
|
|
||||||
|
|
||||||
const [piloswine, mamoswine] = game.scene.getPlayerParty();
|
|
||||||
const piloWaveSpy = vi.spyOn(piloswine, "resetWaveData");
|
|
||||||
const piloBattleWaveSpy = vi.spyOn(piloswine, "resetBattleAndWaveData");
|
|
||||||
const mamoWaveSpy = vi.spyOn(mamoswine, "resetWaveData");
|
|
||||||
const mamoBattleWaveSpy = vi.spyOn(mamoswine, "resetBattleAndWaveData");
|
|
||||||
|
|
||||||
game.doSwitchPokemon(1);
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
expect(piloWaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
expect(piloBattleWaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
expect(mamoWaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
expect(mamoBattleWaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Enemy", () => {
|
|
||||||
it("should only call leaveField once on the switched out pokemon", async () => {
|
|
||||||
await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]);
|
|
||||||
|
|
||||||
const [enemy1, enemy2] = game.scene.getEnemyParty();
|
|
||||||
const enemy1LeaveSpy = vi.spyOn(enemy1, "leaveField");
|
|
||||||
const enemy2LeaveSpy = vi.spyOn(enemy2, "leaveField");
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
|
||||||
game.forceEnemyToSwitch();
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
expect(enemy1LeaveSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(enemy2LeaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should only reset summonData/turnData once per switch", async () => {
|
|
||||||
await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]);
|
|
||||||
|
|
||||||
const [enemy1, enemy2] = game.scene.getEnemyParty();
|
|
||||||
const enemy1SummonSpy = vi.spyOn(enemy1, "resetSummonData");
|
|
||||||
const enemy1TurnSpy = vi.spyOn(enemy1, "resetTurnData");
|
|
||||||
const enemy2SummonSpy = vi.spyOn(enemy2, "resetSummonData");
|
|
||||||
const enemy2TurnSpy = vi.spyOn(enemy2, "resetTurnData");
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
|
||||||
game.forceEnemyToSwitch();
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
expect(enemy1SummonSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(enemy1TurnSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(enemy2SummonSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(enemy2TurnSpy).toHaveBeenCalledTimes(2); // once from switching, once at turn start
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not reset battleData/waveData upon switching", async () => {
|
|
||||||
await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]);
|
|
||||||
|
|
||||||
const [enemy1, enemy2] = game.scene.getEnemyParty();
|
|
||||||
const enemy1WaveSpy = vi.spyOn(enemy1, "resetWaveData");
|
|
||||||
const enemy1BattleWaveSpy = vi.spyOn(enemy1, "resetBattleAndWaveData");
|
|
||||||
const enemy2WaveSpy = vi.spyOn(enemy2, "resetWaveData");
|
|
||||||
const enemy2BattleWaveSpy = vi.spyOn(enemy2, "resetBattleAndWaveData");
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
|
||||||
game.forceEnemyToSwitch();
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
expect(enemy1WaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
expect(enemy1BattleWaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
expect(enemy2WaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
expect(enemy2BattleWaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe.each<{ name: string; playerMove?: Moves; playerAbility?: Abilities; enemyMove?: Moves }>([
|
|
||||||
{ name: "Self Switch Attack Moves", playerMove: Moves.U_TURN },
|
|
||||||
{ name: "Target Switch Attack Moves", enemyMove: Moves.DRAGON_TAIL },
|
|
||||||
{ name: "Self Switch Status Moves", playerMove: Moves.TELEPORT },
|
|
||||||
{ name: "Target Switch Status Moves", enemyMove: Moves.WHIRLWIND },
|
|
||||||
{ name: "Self Switch Abilities", playerAbility: Abilities.EMERGENCY_EXIT, enemyMove: Moves.BRAVE_BIRD },
|
|
||||||
/* { name: "Fainting", playerMove: Moves.EXPLOSION }, */ // TODO: This calls it twice...
|
|
||||||
])(
|
|
||||||
"Mid-Battle Switch Outs - $name - ",
|
|
||||||
({ playerMove = Moves.SPLASH, playerAbility = Abilities.BALL_FETCH, enemyMove = Moves.SPLASH }) => {
|
|
||||||
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(playerMove)
|
|
||||||
.ability(playerAbility)
|
|
||||||
.battleStyle("single")
|
|
||||||
.disableCrits()
|
|
||||||
.enemyLevel(100)
|
|
||||||
.battleType(BattleType.TRAINER)
|
|
||||||
.passiveAbility(Abilities.STURDY)
|
|
||||||
.enemySpecies(Species.MAGIKARP)
|
|
||||||
.enemyMoveset(enemyMove)
|
|
||||||
.enemyAbility(Abilities.BALL_FETCH)
|
|
||||||
.enemyPassiveAbility(Abilities.NO_GUARD);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should only call leaveField once on the switched out pokemon", async () => {
|
|
||||||
await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]);
|
|
||||||
|
|
||||||
const [piloswine, mamoswine] = game.scene.getPlayerParty();
|
|
||||||
const piloLeaveSpy = vi.spyOn(piloswine, "leaveField");
|
|
||||||
const mamoLeaveSpy = vi.spyOn(mamoswine, "leaveField");
|
|
||||||
|
|
||||||
game.move.select(playerMove);
|
|
||||||
game.doSelectPartyPokemon(1);
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
expect(piloLeaveSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mamoLeaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should only reset summonData/turnData once per switch", async () => {
|
|
||||||
await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]);
|
|
||||||
|
|
||||||
const [piloswine, mamoswine] = game.scene.getPlayerParty();
|
|
||||||
piloswine.addTag(BattlerTagType.AQUA_RING, 999); // give piloswine a tag to ensure we know if summonData got reset
|
|
||||||
const piloSummonSpy = vi.spyOn(piloswine, "resetSummonData");
|
|
||||||
const piloTurnSpy = vi.spyOn(piloswine, "resetTurnData");
|
|
||||||
const mamoSummonSpy = vi.spyOn(mamoswine, "resetSummonData");
|
|
||||||
const mamoTurnSpy = vi.spyOn(mamoswine, "resetTurnData");
|
|
||||||
|
|
||||||
game.move.select(playerMove);
|
|
||||||
game.doSelectPartyPokemon(1);
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
expect(piloSummonSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(piloTurnSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mamoSummonSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mamoTurnSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(piloswine.summonData).toEqual(new PokemonSummonData());
|
|
||||||
expect(piloswine.turnData).toEqual(new PokemonTurnData());
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not reset battleData/waveData upon switching", async () => {
|
|
||||||
await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]);
|
|
||||||
|
|
||||||
const [piloswine, mamoswine] = game.scene.getPlayerParty();
|
|
||||||
const piloWaveSpy = vi.spyOn(piloswine, "resetWaveData");
|
|
||||||
const piloBattleWaveSpy = vi.spyOn(piloswine, "resetBattleAndWaveData");
|
|
||||||
const mamoWaveSpy = vi.spyOn(mamoswine, "resetWaveData");
|
|
||||||
const mamoBattleWaveSpy = vi.spyOn(mamoswine, "resetBattleAndWaveData");
|
|
||||||
|
|
||||||
game.move.select(playerMove);
|
|
||||||
game.doSelectPartyPokemon(1);
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
|
||||||
|
|
||||||
expect(piloWaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
expect(piloBattleWaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
expect(mamoWaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
expect(mamoBattleWaveSpy).toHaveBeenCalledTimes(0);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
Loading…
Reference in New Issue
Block a user