Fixed Wimp out tests & ME code

finally i think all the booleans are gone
i hope
This commit is contained in:
Bertie690 2025-05-10 10:35:51 -04:00
parent 3f02493f79
commit 06d250d9e7
13 changed files with 236 additions and 233 deletions

View File

@ -38,6 +38,7 @@ import type { BerryType } from "#enums/berry-type";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat";
import i18next from "i18next";
import { MoveUseType } from "#enums/move-use-type";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounters/absoluteAvarice";
@ -303,7 +304,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = MysteryEncounterBuilde
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.ENEMY],
move: new PokemonMove(Moves.STUFF_CHEEKS),
ignorePp: true,
useType: MoveUseType.IGNORE_PP,
});
await transitionMysteryEncounterIntroVisuals(true, true, 500);

View File

@ -49,6 +49,7 @@ import { CustomPokemonData } from "#app/data/custom-pokemon-data";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { EncounterAnim } from "#enums/encounter-anims";
import { Challenges } from "#enums/challenges";
import { MoveUseType } from "#enums/move-use-type";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounters/clowningAround";
@ -209,19 +210,19 @@ export const ClowningAroundEncounter: MysteryEncounter = MysteryEncounterBuilder
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.ENEMY_2],
move: new PokemonMove(Moves.ROLE_PLAY),
ignorePp: true,
useType: MoveUseType.IGNORE_PP,
},
{
sourceBattlerIndex: BattlerIndex.ENEMY_2,
targets: [BattlerIndex.PLAYER],
move: new PokemonMove(Moves.TAUNT),
ignorePp: true,
useType: MoveUseType.IGNORE_PP,
},
{
sourceBattlerIndex: BattlerIndex.ENEMY_2,
targets: [BattlerIndex.PLAYER_2],
move: new PokemonMove(Moves.TAUNT),
ignorePp: true,
useType: MoveUseType.IGNORE_PP,
},
);

View File

@ -41,6 +41,7 @@ import { PokeballType } from "#enums/pokeball";
import { Species } from "#enums/species";
import { Stat } from "#enums/stat";
import i18next from "i18next";
import { MoveUseType } from "#enums/move-use-type";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounters/dancingLessons";
@ -216,7 +217,7 @@ export const DancingLessonsEncounter: MysteryEncounter = MysteryEncounterBuilder
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
move: new PokemonMove(Moves.REVELATION_DANCE),
ignorePp: true,
useType: MoveUseType.IGNORE_PP,
});
await hideOricorioPokemon();

View File

@ -48,6 +48,7 @@ import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat";
import { Ability } from "#app/data/abilities/ability-class";
import { FIRE_RESISTANT_ABILITIES } from "#app/data/mystery-encounters/requirements/requirement-groups";
import { MoveUseType } from "#enums/move-use-type";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounters/fieryFallout";
@ -194,13 +195,13 @@ export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.w
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
move: new PokemonMove(Moves.FIRE_SPIN),
ignorePp: true,
useType: MoveUseType.IGNORE_PP,
},
{
sourceBattlerIndex: BattlerIndex.ENEMY_2,
targets: [BattlerIndex.PLAYER_2],
move: new PokemonMove(Moves.FIRE_SPIN),
ignorePp: true,
useType: MoveUseType.IGNORE_PP,
},
);
await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);

View File

@ -31,6 +31,7 @@ import { BerryType } from "#enums/berry-type";
import { Stat } from "#enums/stat";
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
import { randSeedInt } from "#app/utils/common";
import { MoveUseType } from "#enums/move-use-type";
/** i18n namespace for the encounter */
const namespace = "mysteryEncounters/slumberingSnorlax";
@ -133,14 +134,12 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuil
guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS],
fillRemaining: true,
});
encounter.startOfBattleEffects.push(
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
move: new PokemonMove(Moves.SNORE),
ignorePp: true,
},
);
encounter.startOfBattleEffects.push({
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
move: new PokemonMove(Moves.SNORE),
useType: MoveUseType.IGNORE_PP,
});
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);
},
)

View File

@ -29,6 +29,7 @@ import { CustomPokemonData } from "#app/data/custom-pokemon-data";
import { Stat } from "#enums/stat";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { MoveUseType } from "#enums/move-use-type";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounters/theStrongStuff";
@ -211,13 +212,13 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
move: new PokemonMove(Moves.GASTRO_ACID),
ignorePp: true,
useType: MoveUseType.IGNORE_PP,
},
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
move: new PokemonMove(Moves.STEALTH_ROCK),
ignorePp: true,
useType: MoveUseType.IGNORE_PP,
},
);

View File

@ -28,6 +28,7 @@ import { BattlerIndex } from "#app/battle";
import { PokemonMove } from "#app/field/pokemon";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { randSeedInt } from "#app/utils/common";
import { MoveUseType } from "#enums/move-use-type";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounters/trashToTreasure";
@ -207,13 +208,13 @@ export const TrashToTreasureEncounter: MysteryEncounter = MysteryEncounterBuilde
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
move: new PokemonMove(Moves.TOXIC),
ignorePp: true,
useType: MoveUseType.IGNORE_PP,
},
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.ENEMY],
move: new PokemonMove(Moves.STOCKPILE),
ignorePp: true,
useType: MoveUseType.IGNORE_PP,
},
);
await initBattleWithEnemyConfig(encounter.enemyPartyConfigs[0]);

View File

@ -38,6 +38,7 @@ import { BerryModifier } from "#app/modifier/modifier";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { MoveUseType } from "#enums/move-use-type";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounters/uncommonBreed";
@ -178,7 +179,7 @@ export const UncommonBreedEncounter: MysteryEncounter = MysteryEncounterBuilder.
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [target],
move: pokemonMove,
ignorePp: true,
useType: MoveUseType.IGNORE_PP,
});
}

View File

@ -28,14 +28,14 @@ import type { GameModes } from "#app/game-mode";
import type { EncounterAnim } from "#enums/encounter-anims";
import type { Challenges } from "#enums/challenges";
import { globalScene } from "#app/global-scene";
import type { MoveUseType } from "#enums/move-use-type";
export interface EncounterStartOfBattleEffect {
sourcePokemon?: Pokemon;
sourceBattlerIndex?: BattlerIndex;
targets: BattlerIndex[];
move: PokemonMove;
ignorePp: boolean;
followUp?: boolean;
useType: MoveUseType; // TODO: This should always be ignore PP...
}
const DEFAULT_MAX_ALLOWED_ENCOUNTERS = 2;
@ -253,7 +253,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
*/
selectedOption?: MysteryEncounterOption;
/**
* Will be set by option select handlers automatically, and can be used to refer to which option was chosen by later phases
* Array containing data pertaining to free moves used at the start of a battle mystery envounter.
*/
startOfBattleEffects: EncounterStartOfBattleEffect[] = [];
/**

View File

@ -1,5 +1,4 @@
import type Battle from "#app/battle";
import { BattlerIndex } from "#app/battle";
import { BattleType } from "#enums/battle-type";
import { biomeLinks, BiomePoolTier } from "#app/data/balance/biomes";
import type MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option";
@ -986,26 +985,9 @@ export function handleMysteryEncounterBattleStartEffects() {
) {
const effects = encounter.startOfBattleEffects;
effects.forEach(effect => {
let source: EnemyPokemon | Pokemon;
if (effect.sourcePokemon) {
source = effect.sourcePokemon;
} else if (!isNullOrUndefined(effect.sourceBattlerIndex)) {
if (effect.sourceBattlerIndex === BattlerIndex.ATTACKER) {
source = globalScene.getEnemyField()[0];
} else if (effect.sourceBattlerIndex === BattlerIndex.ENEMY) {
source = globalScene.getEnemyField()[0];
} else if (effect.sourceBattlerIndex === BattlerIndex.ENEMY_2) {
source = globalScene.getEnemyField()[1];
} else if (effect.sourceBattlerIndex === BattlerIndex.PLAYER) {
source = globalScene.getPlayerField()[0];
} else if (effect.sourceBattlerIndex === BattlerIndex.PLAYER_2) {
source = globalScene.getPlayerField()[1];
}
} else {
source = globalScene.getEnemyField()[0];
}
// @ts-ignore: source cannot be undefined
globalScene.pushPhase(new MovePhase(source, effect.targets, effect.move, effect.followUp, effect.ignorePp));
const source: EnemyPokemon | Pokemon =
effect.sourcePokemon ?? globalScene.getField()[effect.sourceBattlerIndex ?? 0];
globalScene.pushPhase(new MovePhase(source, effect.targets, effect.move, effect.useType));
});
// Pseudo turn end phase to reset flinch states, Endure, etc.

View File

@ -44,7 +44,6 @@ describe("Abilities - Wimp Out", () => {
function confirmSwitch(): void {
const [pokemon1, pokemon2] = game.scene.getPlayerParty();
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
expect(pokemon1.species.speciesId).not.toBe(Species.WIMPOD);
@ -56,17 +55,34 @@ describe("Abilities - Wimp Out", () => {
function confirmNoSwitch(): void {
const [pokemon1, pokemon2] = game.scene.getPlayerParty();
expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase");
expect(pokemon2.species.speciesId).not.toBe(Species.WIMPOD);
expect(pokemon1.species.speciesId).toBe(Species.WIMPOD);
expect(pokemon1.isFainted()).toBe(false);
expect(pokemon1.getHpRatio()).toBeLessThan(0.5);
expect(pokemon2.species.speciesId).not.toBe(Species.WIMPOD);
}
it("triggers regenerator passive single time when switching out with wimp out", async () => {
it("should switch user out when falling below 50% HP, cancelling any pending moves", async () => {
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
const wimpod = game.scene.getPlayerPokemon()!;
wimpod.hp *= 0.51;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("DamageAnimPhase", false);
game.phaseInterceptor.clearLogs();
await game.phaseInterceptor.to("TurnEndPhase");
confirmSwitch();
expect(wimpod.turnData.acted).toBe(false);
expect(game.phaseInterceptor.log).not.toContain("MoveEffectPhase");
});
it("should trigger regenerator passive when switching out", async () => {
game.override.passiveAbility(Abilities.REGENERATOR).startingLevel(5).enemyLevel(100);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
@ -80,7 +96,7 @@ describe("Abilities - Wimp Out", () => {
confirmSwitch();
});
it("It makes wild pokemon flee if triggered", async () => {
it("should cause wild pokemon to flee", async () => {
game.override.enemyAbility(Abilities.WIMP_OUT);
await game.classicMode.startBattle([Species.GOLISOPOD, Species.TYRUNT]);
@ -90,12 +106,11 @@ describe("Abilities - Wimp Out", () => {
game.move.select(Moves.FALSE_SWIPE);
await game.phaseInterceptor.to("BerryPhase");
const isVisible = enemyPokemon.visible;
const hasFled = enemyPokemon.switchOutStatus;
expect(!isVisible && hasFled).toBe(true);
expect(enemyPokemon.visible).toBe(false);
expect(enemyPokemon.switchOutStatus).toBe(true);
});
it("Does not trigger when HP already below half", async () => {
it("should not trigger when HP is already below half", async () => {
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
const wimpod = game.scene.getPlayerPokemon()!;
wimpod.hp = 5;
@ -107,7 +122,7 @@ describe("Abilities - Wimp Out", () => {
confirmNoSwitch();
});
it("Trapping moves do not prevent Wimp Out from activating.", async () => {
it("should bypass trapping moves and abilities", async () => {
game.override.enemyMoveset([Moves.SPIRIT_SHACKLE]).startingLevel(53).enemyLevel(45);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
@ -122,7 +137,7 @@ describe("Abilities - Wimp Out", () => {
confirmSwitch();
});
it("If this Ability activates due to being hit by U-turn or Volt Switch, the user of that move will not be switched out.", async () => {
it("should block switching from U-Turn on activation", async () => {
game.override.startingLevel(95).enemyMoveset([Moves.U_TURN]);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
@ -136,7 +151,7 @@ describe("Abilities - Wimp Out", () => {
confirmSwitch();
});
it("If this Ability does not activate due to being hit by U-turn or Volt Switch, the user of that move will be switched out.", async () => {
it("should not block switching from U-Turn on failed activation", async () => {
game.override.startingLevel(190).startingWave(8).enemyMoveset([Moves.U_TURN]);
await game.classicMode.startBattle([Species.GOLISOPOD, Species.TYRUNT]);
const RIVAL_NINJASK1 = game.scene.getEnemyPokemon()?.id;
@ -145,7 +160,7 @@ describe("Abilities - Wimp Out", () => {
expect(game.scene.getEnemyPokemon()?.id !== RIVAL_NINJASK1);
});
it("Dragon Tail and Circle Throw switch out Pokémon before the Ability activates.", async () => {
it("Dragon Tail and Circle Throw switch out Pokémon before the Ability activates", async () => {
game.override.startingLevel(69).enemyMoveset([Moves.DRAGON_TAIL]);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
@ -162,7 +177,7 @@ describe("Abilities - Wimp Out", () => {
expect(game.scene.getPlayerPokemon()!.species.speciesId).not.toBe(Species.WIMPOD);
});
it("triggers when recoil damage is taken", async () => {
it("should trigger from recoil damage", async () => {
game.override.moveset([Moves.HEAD_SMASH]).enemyMoveset([Moves.SPLASH]);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
@ -173,7 +188,7 @@ describe("Abilities - Wimp Out", () => {
confirmSwitch();
});
it("It does not activate when the Pokémon cuts its own HP", async () => {
it("should not activate when the Pokémon cuts its own HP", async () => {
game.override.moveset([Moves.SUBSTITUTE]).enemyMoveset([Moves.SPLASH]);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
@ -186,7 +201,19 @@ describe("Abilities - Wimp Out", () => {
confirmNoSwitch();
});
it("Does not trigger when neutralized", async () => {
it("should not trigger from Sheer Force-boosted moves", async () => {
game.override.enemyAbility(Abilities.SHEER_FORCE).enemyMoveset(Moves.SLUDGE_BOMB).startingLevel(95);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.move.select(Moves.ENDURE);
await game.phaseInterceptor.to("TurnEndPhase");
confirmNoSwitch();
});
it("should not trigger while neutralized", async () => {
game.override.enemyAbility(Abilities.NEUTRALIZING_GAS).startingLevel(5);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
@ -223,106 +250,140 @@ describe("Abilities - Wimp Out", () => {
},
);
it("Wimp Out will activate due to weather damage", async () => {
game.override.weather(WeatherType.HAIL).enemyMoveset([Moves.SPLASH]);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
// TODO: Condense into it.eaches
describe("Post Turn Damage Checks - ", () => {
beforeEach(() => {
game.override.enemyMoveset(Moves.SPLASH);
});
game.scene.getPlayerPokemon()!.hp *= 0.51;
it("Wimp Out will activate due to weather damage", async () => {
game.override.weather(WeatherType.HAIL);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
game.scene.getPlayerPokemon()!.hp *= 0.51;
confirmSwitch();
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
confirmSwitch();
});
it("Wimp Out will activate due to post turn status damage", async () => {
game.override.statusEffect(StatusEffect.POISON);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
it("Wimp Out will activate due to leech seed", async () => {
game.override.enemyMoveset([Moves.LEECH_SEED]);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.52;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
it("Wimp Out will activate due to curse damage", async () => {
game.override.enemySpecies(Species.DUSKNOIR).enemyMoveset([Moves.CURSE]);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.52;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
it("Wimp Out will activate due to salt cure damage", async () => {
game.override.enemySpecies(Species.NACLI).enemyMoveset([Moves.SALT_CURE]).enemyLevel(1);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.7;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
it("Wimp Out will activate due to damaging trap damage", async () => {
game.override.enemySpecies(Species.MAGIKARP).enemyMoveset([Moves.WHIRLPOOL]).enemyLevel(1);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.55;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
it("Wimp Out will activate due to aftermath", async () => {
game.override
.moveset([Moves.THUNDER_PUNCH])
.enemySpecies(Species.MAGIKARP)
.enemyAbility(Abilities.AFTERMATH)
.enemyMoveset([Moves.SPLASH])
.enemyLevel(1);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.move.select(Moves.THUNDER_PUNCH);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
confirmSwitch();
});
it("Wimp Out will activate due to bad dreams", async () => {
game.override.statusEffect(StatusEffect.SLEEP).enemyAbility(Abilities.BAD_DREAMS);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.52;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
it("Activates due to entry hazards", async () => {
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0, ArenaTagSide.ENEMY);
game.scene.arena.addTag(ArenaTagType.SPIKES, 1, Moves.SPIKES, 0, ArenaTagSide.ENEMY);
game.override.enemySpecies(Species.CENTISKORCH).enemyAbility(Abilities.WIMP_OUT).startingWave(4);
await game.classicMode.startBattle([Species.TYRUNT]);
expect(game.phaseInterceptor.log).not.toContain("MovePhase");
expect(game.phaseInterceptor.log).toContain("BattleEndPhase");
});
it("Wimp Out will activate due to Nightmare", async () => {
game.override.enemyMoveset([Moves.NIGHTMARE]).statusEffect(StatusEffect.SLEEP);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.65;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
});
it("Does not trigger when enemy has sheer force", async () => {
game.override.enemyAbility(Abilities.SHEER_FORCE).enemyMoveset(Moves.SLUDGE_BOMB).startingLevel(95);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.move.select(Moves.ENDURE);
await game.phaseInterceptor.to("TurnEndPhase");
confirmNoSwitch();
});
it("Wimp Out will activate due to post turn status damage", async () => {
game.override.statusEffect(StatusEffect.POISON).enemyMoveset([Moves.SPLASH]);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
it("Wimp Out will activate due to bad dreams", async () => {
game.override.statusEffect(StatusEffect.SLEEP).enemyAbility(Abilities.BAD_DREAMS);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.52;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
it("Wimp Out will activate due to leech seed", async () => {
game.override.enemyMoveset([Moves.LEECH_SEED]);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.52;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
it("Wimp Out will activate due to curse damage", async () => {
game.override.enemySpecies(Species.DUSKNOIR).enemyMoveset([Moves.CURSE]);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.52;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
it("Wimp Out will activate due to salt cure damage", async () => {
game.override.enemySpecies(Species.NACLI).enemyMoveset([Moves.SALT_CURE]).enemyLevel(1);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.7;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
it("Wimp Out will activate due to damaging trap damage", async () => {
game.override.enemySpecies(Species.MAGIKARP).enemyMoveset([Moves.WHIRLPOOL]).enemyLevel(1);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.55;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
it("Magic Guard passive should not allow indirect damage to trigger Wimp Out", async () => {
it("should not trigger on Magic Guard-prevented damage", async () => {
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0, ArenaTagSide.ENEMY);
game.scene.arena.addTag(ArenaTagType.SPIKES, 1, Moves.SPIKES, 0, ArenaTagSide.ENEMY);
game.override
@ -331,6 +392,7 @@ describe("Abilities - Wimp Out", () => {
.weather(WeatherType.HAIL)
.statusEffect(StatusEffect.POISON);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.move.select(Moves.SPLASH);
@ -341,6 +403,19 @@ describe("Abilities - Wimp Out", () => {
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.WIMPOD);
});
it("triggers status on the wimp out user before a new pokemon is switched in", async () => {
game.override.enemyMoveset(Moves.SLUDGE_BOMB).startingLevel(80);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
vi.spyOn(allMoves[Moves.SLUDGE_BOMB], "chance", "get").mockReturnValue(100);
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerParty()[1].status?.effect).toEqual(StatusEffect.POISON);
confirmSwitch();
});
it("Wimp Out activating should not cancel a double battle", async () => {
game.override.battleStyle("double").enemyAbility(Abilities.WIMP_OUT).enemyMoveset([Moves.SPLASH]).enemyLevel(1);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
@ -361,59 +436,7 @@ describe("Abilities - Wimp Out", () => {
expect(enemySecPokemon.hp).toEqual(enemySecPokemon.getMaxHp());
});
it("Wimp Out will activate due to aftermath", async () => {
game.override
.moveset([Moves.THUNDER_PUNCH])
.enemySpecies(Species.MAGIKARP)
.enemyAbility(Abilities.AFTERMATH)
.enemyMoveset([Moves.SPLASH])
.enemyLevel(1);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.move.select(Moves.THUNDER_PUNCH);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
confirmSwitch();
});
it("Activates due to entry hazards", async () => {
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0, ArenaTagSide.ENEMY);
game.scene.arena.addTag(ArenaTagType.SPIKES, 1, Moves.SPIKES, 0, ArenaTagSide.ENEMY);
game.override.enemySpecies(Species.CENTISKORCH).enemyAbility(Abilities.WIMP_OUT).startingWave(4);
await game.classicMode.startBattle([Species.TYRUNT]);
expect(game.phaseInterceptor.log).not.toContain("MovePhase");
expect(game.phaseInterceptor.log).toContain("BattleEndPhase");
});
it("Wimp Out will activate due to Nightmare", async () => {
game.override.enemyMoveset([Moves.NIGHTMARE]).statusEffect(StatusEffect.SLEEP);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.65;
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
confirmSwitch();
});
it("triggers status on the wimp out user before a new pokemon is switched in", async () => {
game.override.enemyMoveset(Moves.SLUDGE_BOMB).startingLevel(80);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
vi.spyOn(allMoves[Moves.SLUDGE_BOMB], "chance", "get").mockReturnValue(100);
game.move.select(Moves.SPLASH);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerParty()[1].status?.effect).toEqual(StatusEffect.POISON);
confirmSwitch();
});
it("triggers after last hit of multi hit move", async () => {
it("triggers after last hit of multi hit moves", async () => {
game.override.enemyMoveset(Moves.BULLET_SEED).enemyAbility(Abilities.SKILL_LINK);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
@ -444,6 +467,7 @@ describe("Abilities - Wimp Out", () => {
expect(enemyPokemon.turnData.hitCount).toBe(2);
confirmSwitch();
});
it("triggers after last hit of Parental Bond", async () => {
game.override.enemyMoveset(Moves.TACKLE).enemyAbility(Abilities.PARENTAL_BOND);
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);

View File

@ -18,6 +18,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite
const pokemonName = "PKM";
const sourceText = "SOURCE";
// TODO: Make this a giant it.each
describe("Status Effect Messages", () => {
describe("NONE", () => {
const statusEffect = StatusEffect.NONE;
@ -390,7 +391,7 @@ describe("Status Effects", () => {
game.move.select(Moves.SPLASH);
await game.toNextTurn();
expect(player.status?.effect).toBeUndefined();
expect(player.status).toBeFalsy();
expect(player.getLastXMoves(1)[0].result).toBe(MoveResult.SUCCESS);
});
});

View File

@ -1,5 +1,4 @@
import { allMoves } from "#app/data/moves/move";
import { CommandPhase } from "#app/phases/command-phase";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
@ -34,13 +33,9 @@ describe("Moves - Rollout", () => {
game.override.enemyMoveset(Moves.SPLASH);
});
it("should double it's dmg on sequential uses but reset after 5", async () => {
game.override.moveset([Moves.ROLLOUT]);
vi.spyOn(allMoves[Moves.ROLLOUT], "accuracy", "get").mockReturnValue(100); //always hit
const variance = 5;
const turns = 6;
const dmgHistory: number[] = [];
it("should double dmg on sequential uses but reset after 5", async () => {
game.override.moveset(Moves.ROLLOUT);
vi.spyOn(allMoves[Moves.ROLLOUT], "accuracy", "get").mockReturnValue(100); // always hit
await game.startBattle();
@ -49,14 +44,13 @@ describe("Moves - Rollout", () => {
const enemyPkm = game.scene.getEnemyParty()[0];
vi.spyOn(enemyPkm, "stats", "get").mockReturnValue([500000, 1, 1, 1, 1, 1]); // HP, ATK, DEF, SPATK, SPDEF, SPD
vi.spyOn(enemyPkm, "getHeldItems").mockReturnValue([]); //no berries
enemyPkm.hp = enemyPkm.getMaxHp();
let previousHp = enemyPkm.hp;
const dmgHistory: number[] = [];
for (let i = 0; i < turns; i++) {
for (let i = 0; i < 6; i++) {
game.move.select(Moves.ROLLOUT);
await game.phaseInterceptor.to(CommandPhase);
await game.toNextTurn();
dmgHistory.push(previousHp - enemyPkm.hp);
previousHp = enemyPkm.hp;
@ -64,16 +58,12 @@ describe("Moves - Rollout", () => {
const [turn1Dmg, turn2Dmg, turn3Dmg, turn4Dmg, turn5Dmg, turn6Dmg] = dmgHistory;
expect(turn2Dmg).toBeGreaterThanOrEqual(turn1Dmg * 2 - variance);
expect(turn2Dmg).toBeLessThanOrEqual(turn1Dmg * 2 + variance);
expect(turn3Dmg).toBeGreaterThanOrEqual(turn2Dmg * 2 - variance);
expect(turn3Dmg).toBeLessThanOrEqual(turn2Dmg * 2 + variance);
expect(turn4Dmg).toBeGreaterThanOrEqual(turn3Dmg * 2 - variance);
expect(turn4Dmg).toBeLessThanOrEqual(turn3Dmg * 2 + variance);
expect(turn5Dmg).toBeGreaterThanOrEqual(turn4Dmg * 2 - variance);
expect(turn5Dmg).toBeLessThanOrEqual(turn4Dmg * 2 + variance);
// reset
expect(turn6Dmg).toBeGreaterThanOrEqual(turn1Dmg - variance);
expect(turn6Dmg).toBeLessThanOrEqual(turn1Dmg + variance);
// 2 sig figs precision is more than enough
expect(turn2Dmg).toBeCloseTo(turn1Dmg * 2);
expect(turn3Dmg).toBeCloseTo(turn2Dmg * 2);
expect(turn4Dmg).toBeCloseTo(turn3Dmg * 2);
expect(turn5Dmg).toBeCloseTo(turn4Dmg * 2);
// reset on turn 6
expect(turn6Dmg).toBeCloseTo(turn1Dmg);
});
});