Compare commits

...

20 Commits

Author SHA1 Message Date
Amani H.
cf845c1237
Merge 37ff766d3e into 3b36ab17e4 2025-08-05 07:17:59 +00:00
xsn34kzx
37ff766d3e Merge beta 2025-08-05 03:17:45 -04:00
xsn34kzx
02c0347411 Add Unit Tests 2025-08-05 02:30:18 -04:00
Jimmybald1
3b36ab17e4
[Bug] Protect now tracks success chance properly (#5869)
* Protect rng now resets on new waves and fixed to look at all turns in the same wave.

* Added per-wave move history object to fix issues

@Jimmybald1 I added a commented out `console.log` in the protect code (L5797) for you to use for testing

* Added many tests

* Wave move history has to be looped in reverse

* Update src/data/moves/move.ts

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

* Update src/data/moves/move.ts

Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>

* comments

* Fixed forceEnemyMove references after merge

* Removed console log

Co-authored-by: Amani H. <109637146+xsn34kzx@users.noreply.github.com>

* Fixed test message

Co-authored-by: Bertie690 <136088738+Bertie690@users.noreply.github.com>

* Apply Biome

* Fix merge issues

* Fix Crafty Shield test

* Remove protect chance reset on wave change

* Fix merge issue

---------

Co-authored-by: Jimmybald1 <147992650+IBBCalc@users.noreply.github.com>
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
Co-authored-by: Amani H. <109637146+xsn34kzx@users.noreply.github.com>
Co-authored-by: Bertie690 <136088738+Bertie690@users.noreply.github.com>
2025-08-05 07:35:14 +02:00
NightKev
375587213e [Misc] Replace Moves.MOVE_NAME with MoveId.MOVE_NAME in comments 2025-08-04 21:24:58 -07:00
Bertie690
8da02bad50
[Test] Replace game.scene.getXPokemon()! with game.field.getXPokemon()
https://github.com/pagefaultgames/pokerogue/pull/6178

* Replaced all instances of `game.scene.getXXXPokemon()!`
inside tests with `game.field.getXXXPokemon()`

* Fixed tests

* Fixed oblivious test

* Fix Grudge test
2025-08-04 21:19:57 -07:00
xsn34kzx
b25dbe7732 Adjust TODOs 2025-08-04 13:02:23 -04:00
xsn34kzx
968b4d410f Minor Change 2025-08-04 12:57:40 -04:00
xsn34kzx
ac80209848 Fix Party Heal 2025-08-03 23:57:40 -04:00
xsn34kzx
2a7da4681f Fix Infinite Reward Reroll Bug 2025-08-03 22:47:47 -04:00
xsn34kzx
c2408fdce0 Merge beta 2025-08-02 23:20:39 -04:00
xsn34kzx
3df44ea899 Adjust Nuzlocke Achievement to Include Fresh Start 2025-08-02 23:18:25 -04:00
xsn34kzx
8673e10892 Change Challenge Order 2025-08-01 17:51:02 -04:00
xsn34kzx
2b28a9dcec Add "Nuzlocke" Achievement 2025-08-01 17:10:40 -04:00
xsn34kzx
b2e5983153 Transition to BooleanHolder 2025-08-01 15:05:20 -04:00
xsn34kzx
809a981893 Misc. Changes 2025-08-01 12:12:16 -04:00
xsn34kzx
5c2c7cf3d1 Separate Challenge Utility Functions 2025-08-01 00:49:44 -04:00
xsn34kzx
8976b0d3a2 Merge beta 2025-07-31 23:05:26 -04:00
xsn34kzx
80151b3338 Add Sacred Ash to revive Group 2025-07-31 22:24:38 -04:00
xsn34kzx
ef7437815a [Challenge] Add Nuzlocke-related Challenges
Co-authored-by: =?UTF-8?q?Matilde=20Sim=C3=B5es?= <matilde.simoes@tecnico.ulisboa.pt>
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
Co-authored-by: Sirzento <sirzento@gmx.de>
2025-07-31 21:31:23 -04:00
257 changed files with 2743 additions and 1804 deletions

View File

@ -1768,7 +1768,7 @@ export interface AddSecondStrikeAbAttrParams extends Omit<AugmentMoveInteraction
/**
* Class for abilities that add additional strikes to single-target moves.
* Used by {@linkcode Moves.PARENTAL_BOND | Parental Bond}.
* Used by {@linkcode MoveId.PARENTAL_BOND | Parental Bond}.
*/
export class AddSecondStrikeAbAttr extends PreAttackAbAttr {
/**

View File

@ -2055,7 +2055,7 @@ export class TruantTag extends AbilityBattlerTag {
const lastMove = pokemon.getLastXMoves()[0];
if (!lastMove || lastMove.move === MoveId.NONE) {
// Don't interrupt move if last move was `Moves.NONE` OR no prior move was found
// Don't interrupt move if last move was `MoveId.NONE` OR no prior move was found
return true;
}

View File

@ -1,29 +1,26 @@
import type { FixedBattleConfig } from "#app/battle";
import { getRandomTrainerFunc } from "#app/battle";
import { defaultStarterSpecies } from "#app/constants";
import { globalScene } from "#app/global-scene";
import { pokemonEvolutions } from "#balance/pokemon-evolutions";
import { speciesStarterCosts } from "#balance/starters";
import { pokemonFormChanges } from "#data/pokemon-forms";
import type { PokemonSpecies } from "#data/pokemon-species";
import { BattleType } from "#enums/battle-type";
import { ChallengeType } from "#enums/challenge-type";
import { Challenges } from "#enums/challenges";
import { TypeColor, TypeShadow } from "#enums/color";
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
import { ModifierTier } from "#enums/modifier-tier";
import type { MoveId } from "#enums/move-id";
import { MoveId } from "#enums/move-id";
import type { MoveSourceType } from "#enums/move-source-type";
import { Nature } from "#enums/nature";
import { PokemonType } from "#enums/pokemon-type";
import { SpeciesId } from "#enums/species-id";
import { TrainerType } from "#enums/trainer-type";
import { TrainerVariant } from "#enums/trainer-variant";
import type { Pokemon } from "#field/pokemon";
import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
import { Trainer } from "#field/trainer";
import type { ModifierTypeOption } from "#modifiers/modifier-type";
import { PokemonMove } from "#moves/pokemon-move";
import type { DexAttrProps, GameData } from "#system/game-data";
import { BooleanHolder, isBetween, type NumberHolder, randSeedItem } from "#utils/common";
import { type BooleanHolder, isBetween, type NumberHolder, randSeedItem } from "#utils/common";
import { deepCopy } from "#utils/data";
import { getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils";
import { toCamelCase, toSnakeCase } from "#utils/strings";
@ -341,6 +338,83 @@ export abstract class Challenge {
applyFlipStat(_pokemon: Pokemon, _baseStats: number[]) {
return false;
}
/**
* An apply function for PARTY_HEAL. Derived classes should alter this.
* @param _status - Whether party healing is enabled or not
* @returns Whether this function did anything
*/
applyPartyHeal(_status: BooleanHolder): boolean {
return false;
}
/**
* An apply function for SHOP. Derived classes should alter this.
* @param _status - Whether the shop is or is not available after a wave
* @returns Whether this function did anything
*/
applyShop(_status: BooleanHolder) {
return false;
}
/**
* An apply function for POKEMON_ADD_TO_PARTY. Derived classes should alter this.
* @param _pokemon - The pokemon being caught
* @param _status - Whether the pokemon can be added to the party or not
* @return Whether this function did anything
*/
applyPokemonAddToParty(_pokemon: EnemyPokemon, _status: BooleanHolder): boolean {
return false;
}
/**
* An apply function for POKEMON_FUSION. Derived classes should alter this.
* @param _pokemon - The pokemon being checked
* @param _status - Whether the selected pokemon is allowed to fuse or not
* @returns Whether this function did anything
*/
applyPokemonFusion(_pokemon: PlayerPokemon, _status: BooleanHolder): boolean {
return false;
}
/**
* An apply function for POKEMON_MOVE. Derived classes should alter this.
* @param _moveId - The move being checked
* @param _status - Whether the move can be used or not
* @returns Whether this function did anything
*/
applyPokemonMove(_moveId: MoveId, _status: BooleanHolder): boolean {
return false;
}
/**
* An apply function for SHOP_ITEM. Derived classes should alter this.
* @param _shopItem - The item being checked
* @param _status - Whether the item should be added to the shop or not
* @returns Whether this function did anything
*/
applyShopItem(_shopItem: ModifierTypeOption | null, _status: BooleanHolder): boolean {
return false;
}
/**
* An apply function for WAVE_REWARD. Derived classes should alter this.
* @param _reward - The reward being checked
* @param _status - Whether the reward should be added to the reward options or not
* @returns Whether this function did anything
*/
applyWaveReward(_reward: ModifierTypeOption | null, _status: BooleanHolder): boolean {
return false;
}
/**
* An apply function for PREVENT_REVIVE. Derived classes should alter this.
* @param _status - Whether fainting is a permanent status or not
* @returns Whether this function did anything
*/
applyPreventRevive(_status: BooleanHolder): boolean {
return false;
}
}
type ChallengeCondition = (data: GameData) => boolean;
@ -864,208 +938,108 @@ export class LowerStarterPointsChallenge extends Challenge {
}
/**
* Apply all challenges that modify starter choice.
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_CHOICE
* @param pokemon {@link PokemonSpecies} The pokemon to check the validity of.
* @param valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
* @param dexAttr {@link DexAttrProps} The dex attributes of the pokemon.
* @returns True if any challenge was successfully applied.
* Implements a No Support challenge
*/
export function applyChallenges(
challengeType: ChallengeType.STARTER_CHOICE,
pokemon: PokemonSpecies,
valid: BooleanHolder,
dexAttr: DexAttrProps,
): boolean;
/**
* Apply all challenges that modify available total starter points.
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_POINTS
* @param points {@link NumberHolder} The amount of points you have available.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(challengeType: ChallengeType.STARTER_POINTS, points: NumberHolder): boolean;
/**
* Apply all challenges that modify the cost of a starter.
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_COST
* @param species {@link SpeciesId} The pokemon to change the cost of.
* @param points {@link NumberHolder} The cost of the pokemon.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.STARTER_COST,
species: SpeciesId,
cost: NumberHolder,
): boolean;
/**
* Apply all challenges that modify a starter after selection.
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_MODIFY
* @param pokemon {@link Pokemon} The starter pokemon to modify.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(challengeType: ChallengeType.STARTER_MODIFY, pokemon: Pokemon): boolean;
/**
* Apply all challenges that what pokemon you can have in battle.
* @param challengeType {@link ChallengeType} ChallengeType.POKEMON_IN_BATTLE
* @param pokemon {@link Pokemon} The pokemon to check the validity of.
* @param valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.POKEMON_IN_BATTLE,
pokemon: Pokemon,
valid: BooleanHolder,
): boolean;
/**
* Apply all challenges that modify what fixed battles there are.
* @param challengeType {@link ChallengeType} ChallengeType.FIXED_BATTLES
* @param waveIndex {@link Number} The current wave index.
* @param battleConfig {@link FixedBattleConfig} The battle config to modify.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.FIXED_BATTLES,
waveIndex: number,
battleConfig: FixedBattleConfig,
): boolean;
/**
* Apply all challenges that modify type effectiveness.
* @param challengeType {@linkcode ChallengeType} ChallengeType.TYPE_EFFECTIVENESS
* @param effectiveness {@linkcode NumberHolder} The current effectiveness of the move.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(challengeType: ChallengeType.TYPE_EFFECTIVENESS, effectiveness: NumberHolder): boolean;
/**
* Apply all challenges that modify what level AI are.
* @param challengeType {@link ChallengeType} ChallengeType.AI_LEVEL
* @param level {@link NumberHolder} The generated level of the pokemon.
* @param levelCap {@link Number} The maximum level cap for the current wave.
* @param isTrainer {@link Boolean} Whether this is a trainer pokemon.
* @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.AI_LEVEL,
level: NumberHolder,
levelCap: number,
isTrainer: boolean,
isBoss: boolean,
): boolean;
/**
* Apply all challenges that modify how many move slots the AI has.
* @param challengeType {@link ChallengeType} ChallengeType.AI_MOVE_SLOTS
* @param pokemon {@link Pokemon} The pokemon being considered.
* @param moveSlots {@link NumberHolder} The amount of move slots.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.AI_MOVE_SLOTS,
pokemon: Pokemon,
moveSlots: NumberHolder,
): boolean;
/**
* Apply all challenges that modify whether a pokemon has its passive.
* @param challengeType {@link ChallengeType} ChallengeType.PASSIVE_ACCESS
* @param pokemon {@link Pokemon} The pokemon to modify.
* @param hasPassive {@link BooleanHolder} Whether it has its passive.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.PASSIVE_ACCESS,
pokemon: Pokemon,
hasPassive: BooleanHolder,
): boolean;
/**
* Apply all challenges that modify the game modes settings.
* @param challengeType {@link ChallengeType} ChallengeType.GAME_MODE_MODIFY
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(challengeType: ChallengeType.GAME_MODE_MODIFY): boolean;
/**
* Apply all challenges that modify what level a pokemon can access a move.
* @param challengeType {@link ChallengeType} ChallengeType.MOVE_ACCESS
* @param pokemon {@link Pokemon} What pokemon would learn the move.
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @param move {@link MoveId} The move in question.
* @param level {@link NumberHolder} The level threshold for access.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.MOVE_ACCESS,
pokemon: Pokemon,
moveSource: MoveSourceType,
move: MoveId,
level: NumberHolder,
): boolean;
/**
* Apply all challenges that modify what weight a pokemon gives to move generation
* @param challengeType {@link ChallengeType} ChallengeType.MOVE_WEIGHT
* @param pokemon {@link Pokemon} What pokemon would learn the move.
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @param move {@link MoveId} The move in question.
* @param weight {@link NumberHolder} The weight of the move.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.MOVE_WEIGHT,
pokemon: Pokemon,
moveSource: MoveSourceType,
move: MoveId,
weight: NumberHolder,
): boolean;
export class NoSupportChallenge extends Challenge {
constructor() {
super(Challenges.NO_SUPPORT, 3);
}
export function applyChallenges(challengeType: ChallengeType.FLIP_STAT, pokemon: Pokemon, baseStats: number[]): boolean;
export function applyChallenges(challengeType: ChallengeType, ...args: any[]): boolean {
let ret = false;
globalScene.gameMode.challenges.forEach(c => {
if (c.value !== 0) {
switch (challengeType) {
case ChallengeType.STARTER_CHOICE:
ret ||= c.applyStarterChoice(args[0], args[1], args[2]);
break;
case ChallengeType.STARTER_POINTS:
ret ||= c.applyStarterPoints(args[0]);
break;
case ChallengeType.STARTER_COST:
ret ||= c.applyStarterCost(args[0], args[1]);
break;
case ChallengeType.STARTER_MODIFY:
ret ||= c.applyStarterModify(args[0]);
break;
case ChallengeType.POKEMON_IN_BATTLE:
ret ||= c.applyPokemonInBattle(args[0], args[1]);
break;
case ChallengeType.FIXED_BATTLES:
ret ||= c.applyFixedBattle(args[0], args[1]);
break;
case ChallengeType.TYPE_EFFECTIVENESS:
ret ||= c.applyTypeEffectiveness(args[0]);
break;
case ChallengeType.AI_LEVEL:
ret ||= c.applyLevelChange(args[0], args[1], args[2], args[3]);
break;
case ChallengeType.AI_MOVE_SLOTS:
ret ||= c.applyMoveSlot(args[0], args[1]);
break;
case ChallengeType.PASSIVE_ACCESS:
ret ||= c.applyPassiveAccess(args[0], args[1]);
break;
case ChallengeType.GAME_MODE_MODIFY:
ret ||= c.applyGameModeModify();
break;
case ChallengeType.MOVE_ACCESS:
ret ||= c.applyMoveAccessLevel(args[0], args[1], args[2], args[3]);
break;
case ChallengeType.MOVE_WEIGHT:
ret ||= c.applyMoveWeight(args[0], args[1], args[2], args[3]);
break;
case ChallengeType.FLIP_STAT:
ret ||= c.applyFlipStat(args[0], args[1]);
break;
}
override applyPartyHeal(status: BooleanHolder): boolean {
if (status.value) {
status.value = this.value === 2;
return true;
}
});
return ret;
return false;
}
override applyShop(status: BooleanHolder): boolean {
if (status.value) {
status.value = this.value === 1;
return true;
}
return false;
}
static override loadChallenge(source: NoSupportChallenge | any): NoSupportChallenge {
const newChallenge = new NoSupportChallenge();
newChallenge.value = source.value;
newChallenge.severity = source.severity;
return newChallenge;
}
}
/**
* Implements a Limited Catch challenge
*/
export class LimitedCatchChallenge extends Challenge {
constructor() {
super(Challenges.LIMITED_CATCH, 1);
}
override applyPokemonAddToParty(pokemon: EnemyPokemon, status: BooleanHolder): boolean {
if (status.value) {
status.value = pokemon.metWave % 10 === 1;
return true;
}
return false;
}
static override loadChallenge(source: LimitedCatchChallenge | any): LimitedCatchChallenge {
const newChallenge = new LimitedCatchChallenge();
newChallenge.value = source.value;
newChallenge.severity = source.severity;
return newChallenge;
}
}
/**
* Implements a Permanent Faint challenge
*/
export class PermanentFaintChallenge extends Challenge {
constructor() {
super(Challenges.PERMANENT_FAINT, 1);
}
override applyPokemonFusion(pokemon: PlayerPokemon, status: BooleanHolder): boolean {
if (!status.value) {
status.value = pokemon.isFainted();
return true;
}
return false;
}
override applyShopItem(shopItem: ModifierTypeOption | null, status: BooleanHolder): boolean {
status.value = shopItem?.type.group !== "revive";
return true;
}
override applyWaveReward(reward: ModifierTypeOption | null, status: BooleanHolder): boolean {
return this.applyShopItem(reward, status);
}
override applyPokemonMove(moveId: MoveId, status: BooleanHolder) {
if (status.value) {
status.value = moveId !== MoveId.REVIVAL_BLESSING;
return true;
}
return false;
}
override applyPreventRevive(status: BooleanHolder): boolean {
if (!status.value) {
status.value = true;
return true;
}
return false;
}
static override loadChallenge(source: PermanentFaintChallenge | any): PermanentFaintChallenge {
const newChallenge = new PermanentFaintChallenge();
newChallenge.value = source.value;
newChallenge.severity = source.severity;
return newChallenge;
}
}
/**
@ -1089,6 +1063,12 @@ export function copyChallenge(source: Challenge | any): Challenge {
return InverseBattleChallenge.loadChallenge(source);
case Challenges.FLIP_STAT:
return FlipStatChallenge.loadChallenge(source);
case Challenges.LIMITED_CATCH:
return LimitedCatchChallenge.loadChallenge(source);
case Challenges.NO_SUPPORT:
return NoSupportChallenge.loadChallenge(source);
case Challenges.PERMANENT_FAINT:
return PermanentFaintChallenge.loadChallenge(source);
}
throw new Error("Unknown challenge copied");
}
@ -1097,87 +1077,13 @@ export const allChallenges: Challenge[] = [];
export function initChallenges() {
allChallenges.push(
new FreshStartChallenge(),
new PermanentFaintChallenge(),
new LimitedCatchChallenge(),
new NoSupportChallenge(),
new SingleGenerationChallenge(),
new SingleTypeChallenge(),
new FreshStartChallenge(),
new InverseBattleChallenge(),
new FlipStatChallenge(),
);
}
/**
* Apply all challenges to the given starter (and form) to check its validity.
* Differs from {@linkcode checkSpeciesValidForChallenge} which only checks form changes.
* @param species - The {@linkcode PokemonSpecies} to check the validity of.
* @param dexAttr - The {@linkcode DexAttrProps | dex attributes} of the species, including its form index.
* @param soft - If `true`, allow it if it could become valid through evolution or form change.
* @returns `true` if the species is considered valid.
*/
export function checkStarterValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) {
if (!soft) {
const isValidForChallenge = new BooleanHolder(true);
applyChallenges(ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props);
return isValidForChallenge.value;
}
// We check the validity of every evolution and form change, and require that at least one is valid
const speciesToCheck = [species.speciesId];
while (speciesToCheck.length) {
const checking = speciesToCheck.pop();
// Linter complains if we don't handle this
if (!checking) {
return false;
}
const checkingSpecies = getPokemonSpecies(checking);
if (checkSpeciesValidForChallenge(checkingSpecies, props, true)) {
return true;
}
if (checking && pokemonEvolutions.hasOwnProperty(checking)) {
pokemonEvolutions[checking].forEach(e => {
// Form check to deal with cases such as Basculin -> Basculegion
// TODO: does this miss anything if checking forms of a stage 2 Pokémon?
if (!e?.preFormKey || e.preFormKey === species.forms[props.formIndex].formKey) {
speciesToCheck.push(e.speciesId);
}
});
}
}
return false;
}
/**
* Apply all challenges to the given species (and form) to check its validity.
* Differs from {@linkcode checkStarterValidForChallenge} which also checks evolutions.
* @param species - The {@linkcode PokemonSpecies} to check the validity of.
* @param dexAttr - The {@linkcode DexAttrProps | dex attributes} of the species, including its form index.
* @param soft - If `true`, allow it if it could become valid through a form change.
* @returns `true` if the species is considered valid.
*/
function checkSpeciesValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) {
const isValidForChallenge = new BooleanHolder(true);
applyChallenges(ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props);
if (!soft || !pokemonFormChanges.hasOwnProperty(species.speciesId)) {
return isValidForChallenge.value;
}
// If the form in props is valid, return true before checking other form changes
if (soft && isValidForChallenge.value) {
return true;
}
const result = pokemonFormChanges[species.speciesId].some(f1 => {
// Exclude form changes that require the mon to be on the field to begin with
if (!("item" in f1.trigger)) {
return false;
}
return species.forms.some((f2, formIndex) => {
if (f1.formKey === f2.formKey) {
const formProps = { ...props, formIndex };
const isFormValidForChallenge = new BooleanHolder(true);
applyChallenges(ChallengeType.STARTER_CHOICE, species, isFormValidForChallenge, formProps);
return isFormValidForChallenge.value;
}
return false;
});
});
return result;
}

View File

@ -256,7 +256,7 @@ export const noAbilityTypeOverrideMoves: ReadonlySet<MoveId> = new Set([
MoveId.HIDDEN_POWER,
]);
/** Set of all moves that cannot be copied by {@linkcode Moves.SKETCH}. */
/** Set of all moves that cannot be copied by {@linkcode MoveId.SKETCH}. */
export const invalidSketchMoves: ReadonlySet<MoveId> = new Set([
MoveId.NONE,
MoveId.CHATTER,
@ -270,7 +270,7 @@ export const invalidSketchMoves: ReadonlySet<MoveId> = new Set([
MoveId.BREAKNECK_BLITZ__SPECIAL,
]);
/** Set of all moves that cannot be locked into by {@linkcode Moves.ENCORE}. */
/** Set of all moves that cannot be locked into by {@linkcode MoveId.ENCORE}. */
export const invalidEncoreMoves: ReadonlySet<MoveId> = new Set([
MoveId.MIMIC,
MoveId.MIRROR_MOVE,

View File

@ -22,7 +22,6 @@ import {
TypeBoostTag,
} from "#data/battler-tags";
import { getBerryEffectFunc } from "#data/berry";
import { applyChallenges } from "#data/challenge";
import { allAbilities, allMoves } from "#data/data-lists";
import { SpeciesFormChangeRevertWeatherFormTrigger } from "#data/form-change-triggers";
import { DelayedAttackTag } from "#data/positional-tags/positional-tag";
@ -93,6 +92,7 @@ import { BooleanHolder, type Constructor, isNullOrUndefined, NumberHolder, randS
import { getEnumValues } from "#utils/enums";
import { toTitleCase } from "#utils/strings";
import i18next from "i18next";
import { applyChallenges } from "#utils/challenge-utils";
/**
* A function used to conditionally determine execution of a given {@linkcode MoveAttr}.
@ -2587,7 +2587,7 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr {
}
/**
* Applies the effect of {@linkcode Moves.PSYCHO_SHIFT} to its target.
* Applies the effect of {@linkcode MoveId.PSYCHO_SHIFT} to its target.
* Psycho Shift takes the user's status effect and passes it onto the target.
* The user is then healed after the move has been successfully executed.
* @param user - The {@linkcode Pokemon} using the move
@ -2927,7 +2927,7 @@ export class HealStatusEffectAttr extends MoveEffectAttr {
/**
* Attribute to add the {@linkcode BattlerTagType.BYPASS_SLEEP | BYPASS_SLEEP Battler Tag} for 1 turn to the user before move use.
* Used by {@linkcode Moves.SNORE} and {@linkcode Moves.SLEEP_TALK}.
* Used by {@linkcode MoveId.SNORE} and {@linkcode MoveId.SLEEP_TALK}.
*/
// TODO: Should this use a battler tag?
// TODO: Give this `userSleptOrComatoseCondition` by default
@ -5912,20 +5912,21 @@ export class ProtectAttr extends AddBattlerTagAttr {
getCondition(): MoveConditionFunc {
return ((user, target, move): boolean => {
let timesUsed = 0;
const moveHistory = user.getLastXMoves();
let turnMove: TurnMove | undefined;
while (moveHistory.length) {
turnMove = moveHistory.shift();
if (!allMoves[turnMove?.move ?? MoveId.NONE].hasAttr("ProtectAttr") || turnMove?.result !== MoveResult.SUCCESS) {
for (const turnMove of user.getLastXMoves(-1).slice()) {
if (
// Quick & Wide guard increment the Protect counter without using it for fail chance
!(allMoves[turnMove.move].hasAttr("ProtectAttr") ||
[MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) ||
turnMove.result !== MoveResult.SUCCESS
) {
break;
}
timesUsed++;
timesUsed++
}
if (timesUsed) {
return !user.randBattleSeedInt(Math.pow(3, timesUsed));
}
return true;
return timesUsed === 0 || user.randBattleSeedInt(Math.pow(3, timesUsed)) === 0;
});
}
}
@ -7139,7 +7140,7 @@ export class CopyMoveAttr extends CallMoveAttr {
/**
* Attribute used for moves that cause the target to repeat their last used move.
*
* Used by {@linkcode Moves.INSTRUCT | Instruct}.
* Used by {@linkcode MoveId.INSTRUCT | Instruct}.
* @see [Instruct on Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Instruct_(move))
*/
export class RepeatMoveAttr extends MoveEffectAttr {
@ -7402,7 +7403,7 @@ const targetMoveCopiableCondition: MoveConditionFunc = (user, target, move) => {
/**
* Attribute to temporarily copy the last move in the target's moveset.
* Used by {@linkcode Moves.MIMIC}.
* Used by {@linkcode MoveId.MIMIC}.
*/
export class MovesetCopyMoveAttr extends OverrideMoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
@ -7966,7 +7967,7 @@ export class VariableTargetAttr extends MoveAttr {
/**
* Attribute to cause the target to move immediately after the user.
*
* Used by {@linkcode Moves.AFTER_YOU}.
* Used by {@linkcode MoveId.AFTER_YOU}.
*/
export class AfterYouAttr extends MoveEffectAttr {
/**

View File

@ -1,8 +1,10 @@
import { allMoves } from "#data/data-lists";
import { ChallengeType } from "#enums/challenge-type";
import type { MoveId } from "#enums/move-id";
import type { Pokemon } from "#field/pokemon";
import type { Move } from "#moves/move";
import { toDmgValue } from "#utils/common";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder, toDmgValue } from "#utils/common";
/**
* Wrapper class for the {@linkcode Move} class for Pokemon to interact with.
@ -45,16 +47,17 @@ export class PokemonMove {
* @returns Whether this {@linkcode PokemonMove} can be selected by this Pokemon.
*/
isUsable(pokemon: Pokemon, ignorePp = false, ignoreRestrictionTags = false): boolean {
const move = this.getMove();
// TODO: Add Sky Drop's 1 turn stall
if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)) {
return false;
const usability = new BooleanHolder(
!move.name.endsWith(" (N)") &&
(ignorePp || this.ppUsed < this.getMovePp() || move.pp === -1) &&
!(this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)),
);
if (pokemon.isPlayer()) {
applyChallenges(ChallengeType.POKEMON_MOVE, move.id, usability);
}
if (this.getMove().name.endsWith(" (N)")) {
return false;
}
return ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1;
return usability.value;
}
getMove(): Move {

View File

@ -13,6 +13,7 @@ import { CustomPokemonData } from "#data/pokemon-data";
import type { PokemonSpecies } from "#data/pokemon-species";
import { getStatusEffectCatchRateMultiplier } from "#data/status-effect";
import type { AbilityId } from "#enums/ability-id";
import { ChallengeType } from "#enums/challenge-type";
import { PlayerGender } from "#enums/player-gender";
import type { PokeballType } from "#enums/pokeball";
import type { PokemonType } from "#enums/pokemon-type";
@ -33,7 +34,8 @@ import { achvs } from "#system/achv";
import type { PartyOption } from "#ui/party-ui-handler";
import { PartyUiMode } from "#ui/party-ui-handler";
import { SummaryUiMode } from "#ui/summary-ui-handler";
import { isNullOrUndefined, randSeedInt } from "#utils/common";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder, isNullOrUndefined, randSeedInt } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import i18next from "i18next";
@ -706,6 +708,13 @@ export async function catchPokemon(
});
};
Promise.all([pokemon.hideInfo(), globalScene.gameData.setPokemonCaught(pokemon)]).then(() => {
const addStatus = new BooleanHolder(true);
applyChallenges(ChallengeType.POKEMON_ADD_TO_PARTY, pokemon, addStatus);
if (!addStatus.value) {
removePokemon();
end();
return;
}
if (globalScene.getPlayerParty().length === 6) {
const promptRelease = () => {
globalScene.ui.showText(

View File

@ -253,7 +253,6 @@ export class PokemonTempSummonData {
* Only currently used for positioning the battle cursor.
*/
turnCount = 1;
/**
* The number of turns this pokemon has spent in the active position since the start of the wave
* without switching out.

View File

@ -65,5 +65,45 @@ export enum ChallengeType {
/**
* Modifies what the pokemon stats for Flip Stat Mode.
*/
FLIP_STAT
FLIP_STAT,
/**
* Challenges which conditionally enable or disable automatic party healing during biome transitions
* @see {@linkcode Challenge.applyPartyHealAvailability}
*/
PARTY_HEAL,
/**
* Challenges which conditionally enable or disable the shop
* @see {@linkcode Challenge.applyShopAvailability}
*/
SHOP,
/**
* Challenges which validate whether a pokemon can be added to the player's party or not
* @see {@linkcode Challenge.applyCatchAvailability}
*/
POKEMON_ADD_TO_PARTY,
/**
* Challenges which validate whether a pokemon is allowed to fuse or not
* @see {@linkcode Challenge.applyFusionAvailability}
*/
POKEMON_FUSION,
/**
* Challenges which validate whether particular moves can or cannot be used
* @see {@linkcode Challenge.applyMoveAvailability}
*/
POKEMON_MOVE,
/**
* Challenges which validate whether particular items are or are not sold in the shop
* @see {@linkcode Challenge.applyShopItems}
*/
SHOP_ITEM,
/**
* Challenges which validate whether particular items will be given as a reward after a wave
* @see {@linkcode Challenge.applyWaveRewards}
*/
WAVE_REWARD,
/**
* Challenges which prevent recovery from fainting
* @see {@linkcode Challenge.applyPermanentFaint}
*/
PREVENT_REVIVE,
}

View File

@ -6,4 +6,7 @@ export enum Challenges {
FRESH_START,
INVERSE_BATTLE,
FLIP_STAT,
LIMITED_CATCH,
NO_SUPPORT,
PERMANENT_FAINT,
}

View File

@ -39,7 +39,6 @@ import {
TrappedTag,
TypeImmuneTag,
} from "#data/battler-tags";
import { applyChallenges } from "#data/challenge";
import { allAbilities, allMoves } from "#data/data-lists";
import { getLevelTotalExp } from "#data/exp";
import {
@ -148,6 +147,7 @@ import { EnemyBattleInfo } from "#ui/enemy-battle-info";
import type { PartyOption } from "#ui/party-ui-handler";
import { PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler";
import { PlayerBattleInfo } from "#ui/player-battle-info";
import { applyChallenges } from "#utils/challenge-utils";
import {
BooleanHolder,
type Constructor,
@ -725,7 +725,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
/**
* Load all assets needed for this Pokemon's use in battle
* @param ignoreOverride - Whether to ignore overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `true`
* @param ignoreOverride - Whether to ignore overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `true`
* @param useIllusion - Whether to consider this pokemon's active illusion; default `false`
* @returns A promise that resolves once all the corresponding assets have been loaded.
*/
@ -1032,7 +1032,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
/**
* Return this Pokemon's {@linkcode PokemonSpeciesForm | SpeciesForm}.
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false`
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false`
* and overrides `useIllusion`.
* @param useIllusion - Whether to consider this Pokemon's illusion if present; default `false`.
* @returns This Pokemon's {@linkcode PokemonSpeciesForm}.
@ -1088,7 +1088,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
/**
* Return the {@linkcode PokemonSpeciesForm | SpeciesForm} of this Pokemon's fusion counterpart.
* @param ignoreOverride - Whether to ignore species overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false`
* @param ignoreOverride - Whether to ignore species overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false`
* @param useIllusion - Whether to consider the species of this Pokemon's illusion; default `false`
* @returns The {@linkcode PokemonSpeciesForm} of this Pokemon's fusion counterpart.
*/
@ -1659,7 +1659,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
/**
* Return this Pokemon's {@linkcode Gender}.
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false`
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false`
* @param useIllusion - Whether to consider this pokemon's illusion if present; default `false`
* @returns the {@linkcode Gender} of this {@linkcode Pokemon}.
*/
@ -1675,7 +1675,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
/**
* Return this Pokemon's fusion's {@linkcode Gender}.
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false`
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false`
* @param useIllusion - Whether to consider this pokemon's illusion if present; default `false`
* @returns The {@linkcode Gender} of this {@linkcode Pokemon}'s fusion.
*/
@ -1817,7 +1817,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
/**
* Return all the {@linkcode PokemonMove}s that make up this Pokemon's moveset.
* Takes into account player/enemy moveset overrides (which will also override PP count).
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false`
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false`
* @returns An array of {@linkcode PokemonMove}, as described above.
*/
getMoveset(ignoreOverride = false): PokemonMove[] {
@ -1883,7 +1883,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* Evaluate and return this Pokemon's typing.
* @param includeTeraType - Whether to use this Pokemon's tera type if Terastallized; default `false`
* @param forDefend - Whether this Pokemon is currently receiving an attack; default `false`
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false`
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false`
* @param useIllusion - Whether to consider this Pokemon's illusion if present; default `false`
* @returns An array of {@linkcode PokemonType}s corresponding to this Pokemon's typing (real or percieved).
*/
@ -2006,7 +2006,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* @param type - The {@linkcode PokemonType} to check
* @param includeTeraType - Whether to use this Pokemon's tera type if Terastallized; default `true`
* @param forDefend - Whether this Pokemon is currently receiving an attack; default `false`
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false`
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false`
* @returns Whether this Pokemon is of the specified type.
*/
public isOfType(type: PokemonType, includeTeraType = true, forDefend = false, ignoreOverride = false): boolean {
@ -2019,7 +2019,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* Should rarely be called directly in favor of {@linkcode hasAbility} or {@linkcode hasAbilityWithAttr},
* both of which check both ability slots and account for suppression.
* @see {@linkcode hasAbility} and {@linkcode hasAbilityWithAttr} are the intended ways to check abilities in most cases
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false`
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false`
* @returns The non-passive {@linkcode Ability} of this Pokemon.
*/
public getAbility(ignoreOverride = false): Ability {
@ -2201,7 +2201,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* Accounts for all the various effects which can disable or modify abilities.
* @param ability - The {@linkcode Abilities | Ability} to check for
* @param canApply - Whether to check if the ability is currently active; default `true`
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false`
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false`
* @returns Whether this {@linkcode Pokemon} has the given ability
*/
public hasAbility(ability: AbilityId, canApply = true, ignoreOverride = false): boolean {
@ -2216,7 +2216,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* Accounts for all the various effects which can disable or modify abilities.
* @param attrType - The {@linkcode AbAttr | attribute} to check for
* @param canApply - Whether to check if the ability is currently active; default `true`
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode Moves.TRANSFORM | Transform}; default `false`
* @param ignoreOverride - Whether to ignore any overrides caused by {@linkcode MoveId.TRANSFORM | Transform}; default `false`
* @returns Whether this Pokemon has an ability with the given {@linkcode AbAttr}.
*/
public hasAbilityWithAttr(attrType: AbAttrString, canApply = true, ignoreOverride = false): boolean {
@ -4466,7 +4466,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
* Return the most recently executed {@linkcode TurnMove} this {@linkcode Pokemon} has used that is:
* - Not {@linkcode MoveId.NONE}
* - Non-virtual ({@linkcode MoveUseMode | useMode} < {@linkcode MoveUseMode.INDIRECT})
* @param ignoreStruggle - Whether to additionally ignore {@linkcode Moves.STRUGGLE}; default `false`
* @param ignoreStruggle - Whether to additionally ignore {@linkcode MoveId.STRUGGLE}; default `false`
* @param ignoreFollowUp - Whether to ignore moves with a use type of {@linkcode MoveUseMode.FOLLOW_UP}
* (e.g. ones called by Copycat/Mirror Move); default `true`.
* @returns The last move this Pokemon has used satisfying the aforementioned conditions,
@ -5094,6 +5094,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
*/
resetWaveData(): void {
this.waveData = new PokemonWaveData();
this.tempSummonData.waveTurnCount = 1;
}
resetTera(): void {

View File

@ -2,8 +2,7 @@ import { FixedBattleConfig } from "#app/battle";
import { CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene";
import Overrides from "#app/overrides";
import type { Challenge } from "#data/challenge";
import { allChallenges, applyChallenges, copyChallenge } from "#data/challenge";
import { allChallenges, type Challenge, copyChallenge } from "#data/challenge";
import { getDailyStartingBiome } from "#data/daily-run";
import { allSpecies } from "#data/data-lists";
import type { PokemonSpecies } from "#data/pokemon-species";
@ -14,7 +13,8 @@ import { GameModes } from "#enums/game-modes";
import { SpeciesId } from "#enums/species-id";
import type { Arena } from "#field/arena";
import { classicFixedBattles, type FixedBattleConfigs } from "#trainers/fixed-battle-configs";
import { isNullOrUndefined, randSeedInt, randSeedItem } from "#utils/common";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder, isNullOrUndefined, randSeedInt, randSeedItem } from "#utils/common";
import i18next from "i18next";
interface GameModeConfig {
@ -311,6 +311,16 @@ export class GameMode implements GameModeConfig {
return this.battleConfig[waveIndex];
}
/**
* Checks if the game mode has the shop enabled or not
* @returns Whether the shop is available or not
*/
getShopStatus(): boolean {
const status = new BooleanHolder(!this.hasNoShop);
applyChallenges(ChallengeType.SHOP, status);
return status.value;
}
getClearScoreBonus(): number {
switch (this.modeId) {
case GameModes.CLASSIC:

View File

@ -14,6 +14,7 @@ import { pokemonFormChanges, SpeciesFormChangeCondition } from "#data/pokemon-fo
import { getStatusEffectDescriptor } from "#data/status-effect";
import { BattlerTagType } from "#enums/battler-tag-type";
import { BerryType } from "#enums/berry-type";
import { ChallengeType } from "#enums/challenge-type";
import { FormChangeItem } from "#enums/form-change-item";
import { ModifierPoolType } from "#enums/modifier-pool-type";
import { ModifierTier } from "#enums/modifier-tier";
@ -116,7 +117,16 @@ import type { ModifierTypeFunc, WeightedModifierTypeWeightFunc } from "#types/mo
import type { PokemonMoveSelectFilter, PokemonSelectFilter } from "#ui/party-ui-handler";
import { PartyUiHandler } from "#ui/party-ui-handler";
import { getModifierTierTextTint } from "#ui/text";
import { formatMoney, isNullOrUndefined, NumberHolder, padInt, randSeedInt, randSeedItem } from "#utils/common";
import { applyChallenges } from "#utils/challenge-utils";
import {
BooleanHolder,
formatMoney,
isNullOrUndefined,
NumberHolder,
padInt,
randSeedInt,
randSeedItem,
} from "#utils/common";
import { getEnumKeys, getEnumValues } from "#utils/enums";
import { getModifierPoolForType, getModifierType } from "#utils/modifier-utils";
import i18next from "i18next";
@ -533,7 +543,9 @@ export class PokemonReviveModifierType extends PokemonHpRestoreModifierType {
);
this.selectFilter = (pokemon: PlayerPokemon) => {
if (pokemon.hp) {
const selectStatus = new BooleanHolder(pokemon.hp !== 0);
applyChallenges(ChallengeType.PREVENT_REVIVE, selectStatus);
if (selectStatus.value) {
return PartyUiHandler.NoEffectMessage;
}
return null;
@ -1011,6 +1023,7 @@ class AllPokemonFullReviveModifierType extends AllPokemonFullHpRestoreModifierTy
"modifierType:ModifierType.AllPokemonFullReviveModifierType",
(_type, _args) => new PokemonHpRestoreModifier(this, -1, 0, 100, false, true),
);
this.group = "revive";
}
}
@ -1262,7 +1275,9 @@ export class FusePokemonModifierType extends PokemonModifierType {
iconImage,
(_type, args) => new FusePokemonModifier(this, (args[0] as PlayerPokemon).id, (args[1] as PlayerPokemon).id),
(pokemon: PlayerPokemon) => {
if (pokemon.isFusion()) {
const selectStatus = new BooleanHolder(pokemon.isFusion());
applyChallenges(ChallengeType.POKEMON_FUSION, pokemon, selectStatus);
if (selectStatus.value) {
return PartyUiHandler.NoEffectMessage;
}
return null;
@ -2574,11 +2589,15 @@ function getModifierTypeOptionWithRetry(
): ModifierTypeOption {
allowLuckUpgrades = allowLuckUpgrades ?? true;
let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, tier, undefined, 0, allowLuckUpgrades);
const candidateValidity = new BooleanHolder(true);
applyChallenges(ChallengeType.WAVE_REWARD, candidate, candidateValidity);
let r = 0;
while (
existingOptions.length &&
++r < retryCount &&
existingOptions.filter(o => o.type.name === candidate?.type.name || o.type.group === candidate?.type.group).length
(existingOptions.length &&
++r < retryCount &&
existingOptions.filter(o => o.type.name === candidate?.type.name || o.type.group === candidate?.type.group)
.length) ||
!candidateValidity.value
) {
candidate = getNewModifierTypeOption(
party,
@ -2588,6 +2607,7 @@ function getModifierTypeOptionWithRetry(
0,
allowLuckUpgrades,
);
applyChallenges(ChallengeType.WAVE_REWARD, candidate, candidateValidity);
}
return candidate!;
}
@ -2648,7 +2668,15 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: number, baseC
[new ModifierTypeOption(modifierTypeInitObj.FULL_RESTORE(), 0, baseCost * 2.25)],
[new ModifierTypeOption(modifierTypeInitObj.SACRED_ASH(), 0, baseCost * 10)],
];
return options.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30)).flat();
return options
.slice(0, Math.ceil(Math.max(waveIndex + 10, 0) / 30))
.flat()
.filter(shopItem => {
const status = new BooleanHolder(true);
applyChallenges(ChallengeType.SHOP_ITEM, shopItem, status);
return status.value;
});
}
export function getEnemyBuffModifierForWave(

View File

@ -12,6 +12,7 @@ import {
} from "#data/pokeball";
import { getStatusEffectCatchRateMultiplier } from "#data/status-effect";
import { BattlerIndex } from "#enums/battler-index";
import { ChallengeType } from "#enums/challenge-type";
import type { PokeballType } from "#enums/pokeball";
import { StatusEffect } from "#enums/status-effect";
import { UiMode } from "#enums/ui-mode";
@ -23,6 +24,8 @@ import { achvs } from "#system/achv";
import type { PartyOption } from "#ui/party-ui-handler";
import { PartyUiMode } from "#ui/party-ui-handler";
import { SummaryUiMode } from "#ui/summary-ui-handler";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder } from "#utils/common";
import i18next from "i18next";
// TODO: Refactor and split up to allow for overriding capture chance
@ -287,6 +290,13 @@ export class AttemptCapturePhase extends PokemonPhase {
});
};
Promise.all([pokemon.hideInfo(), globalScene.gameData.setPokemonCaught(pokemon)]).then(() => {
const addStatus = new BooleanHolder(true);
applyChallenges(ChallengeType.POKEMON_ADD_TO_PARTY, pokemon, addStatus);
if (!addStatus.value) {
removePokemon();
end();
return;
}
if (globalScene.getPlayerParty().length === PLAYER_PARTY_MAX_SIZE) {
const promptRelease = () => {
globalScene.ui.showText(

View File

@ -58,12 +58,6 @@ export class BattleEndPhase extends BattlePhase {
globalScene.phaseManager.unshiftNew("GameOverPhase", true);
}
for (const pokemon of globalScene.getField()) {
if (pokemon) {
pokemon.tempSummonData.waveTurnCount = 1;
}
}
for (const pokemon of globalScene.getPokemonAllowedInBattle()) {
applyAbAttrs("PostBattleAbAttr", { pokemon, victory: this.isVictory });
}

View File

@ -9,6 +9,7 @@ import { ArenaTagType } from "#enums/arena-tag-type";
import { BattleType } from "#enums/battle-type";
import { BattlerTagType } from "#enums/battler-tag-type";
import { BiomeId } from "#enums/biome-id";
import { ChallengeType } from "#enums/challenge-type";
import { Command } from "#enums/command";
import { FieldPosition } from "#enums/field-position";
import { MoveId } from "#enums/move-id";
@ -21,6 +22,8 @@ import type { MoveTargetSet } from "#moves/move";
import { getMoveTargets } from "#moves/move-utils";
import { FieldPhase } from "#phases/field-phase";
import type { TurnMove } from "#types/turn-move";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder } from "#utils/common";
import i18next from "i18next";
export class CommandPhase extends FieldPhase {
@ -210,16 +213,27 @@ export class CommandPhase extends FieldPhase {
const move = user.getMoveset()[cursor];
globalScene.ui.setMode(UiMode.MESSAGE);
// Decides between a Disabled, Not Implemented, or No PP translation message
const errorMessage = user.isMoveRestricted(move.moveId, user)
? user.getRestrictingTag(move.moveId, user)!.selectionDeniedText(user, move.moveId)
: move.getName().endsWith(" (N)")
? "battle:moveNotImplemented"
: "battle:moveNoPP";
// Set the translation key for why the move cannot be selected
let cannotSelectKey: string;
const moveStatus = new BooleanHolder(true);
applyChallenges(ChallengeType.POKEMON_MOVE, move.moveId, moveStatus);
if (!moveStatus.value) {
cannotSelectKey = "battle:moveCannotUseChallenge";
} else if (move.getPpRatio() === 0) {
cannotSelectKey = "battle:moveNoPP";
} else if (move.getName().endsWith(" (N)")) {
cannotSelectKey = "battle:moveNotImplemented";
} else if (user.isMoveRestricted(move.moveId, user)) {
cannotSelectKey = user.getRestrictingTag(move.moveId, user)!.selectionDeniedText(user, move.moveId);
} else {
// TODO: Consider a message that signals a being unusable for an unknown reason
cannotSelectKey = "";
}
const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator
globalScene.ui.showText(
i18next.t(errorMessage, { moveName: moveName }),
i18next.t(cannotSelectKey, { moveName: moveName }),
null,
() => {
globalScene.ui.clearText();

View File

@ -649,7 +649,6 @@ export class MovePhase extends BattlePhase {
* Displays the move's usage text to the player as applicable for the move being used.
*/
public showMoveText(): void {
// No text for Moves.NONE, recharging/2-turn moves or interrupted moves
if (
this.move.moveId === MoveId.NONE ||
this.pokemon.getTag(BattlerTagType.RECHARGING) ||
@ -658,7 +657,6 @@ export class MovePhase extends BattlePhase {
return;
}
// Play message for magic coat reflection
// TODO: This should be done by the move...
globalScene.phaseManager.queueMessage(
i18next.t(isReflected(this.useMode) ? "battle:magicCoatActivated" : "battle:useMove", {

View File

@ -1,6 +1,8 @@
import { globalScene } from "#app/global-scene";
import { ChallengeType } from "#enums/challenge-type";
import { BattlePhase } from "#phases/battle-phase";
import { fixedInt } from "#utils/common";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder, fixedInt } from "#utils/common";
export class PartyHealPhase extends BattlePhase {
public readonly phaseName = "PartyHealPhase";
@ -20,13 +22,17 @@ export class PartyHealPhase extends BattlePhase {
globalScene.fadeOutBgm(1000, false);
}
globalScene.ui.fadeOut(1000).then(() => {
const preventRevive = new BooleanHolder(false);
applyChallenges(ChallengeType.PREVENT_REVIVE, preventRevive);
for (const pokemon of globalScene.getPlayerParty()) {
pokemon.hp = pokemon.getMaxHp();
pokemon.resetStatus(true, false, false, true);
for (const move of pokemon.moveset) {
move.ppUsed = 0;
if (!(pokemon.isFainted() && preventRevive.value)) {
pokemon.hp = pokemon.getMaxHp();
pokemon.resetStatus(true, false, false, true);
for (const move of pokemon.moveset) {
move.ppUsed = 0;
}
pokemon.updateInfo(true);
}
pokemon.updateInfo(true);
}
const healSong = globalScene.playSoundWithoutBgm("heal");
globalScene.time.delayedCall(fixedInt(healSong.totalDuration * 1000), () => {

View File

@ -1,11 +1,13 @@
import { globalScene } from "#app/global-scene";
import { biomeLinks, getBiomeName } from "#balance/biomes";
import { BiomeId } from "#enums/biome-id";
import { ChallengeType } from "#enums/challenge-type";
import { UiMode } from "#enums/ui-mode";
import { MapModifier, MoneyInterestModifier } from "#modifiers/modifier";
import { BattlePhase } from "#phases/battle-phase";
import type { OptionSelectItem } from "#ui/abstact-option-select-ui-handler";
import { randSeedInt } from "#utils/common";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder, randSeedInt } from "#utils/common";
export class SelectBiomePhase extends BattlePhase {
public readonly phaseName = "SelectBiomePhase";
@ -20,7 +22,11 @@ export class SelectBiomePhase extends BattlePhase {
const setNextBiome = (nextBiome: BiomeId) => {
if (nextWaveIndex % 10 === 1) {
globalScene.applyModifiers(MoneyInterestModifier, true);
globalScene.phaseManager.unshiftNew("PartyHealPhase", false);
const healStatus = new BooleanHolder(true);
applyChallenges(ChallengeType.PARTY_HEAL, healStatus);
if (healStatus.value) {
globalScene.phaseManager.unshiftNew("PartyHealPhase", false);
}
}
globalScene.phaseManager.unshiftNew("SwitchBiomePhase", nextBiome);
this.end();

View File

@ -1,7 +1,6 @@
import { globalScene } from "#app/global-scene";
import Overrides from "#app/overrides";
import { Phase } from "#app/phase";
import { applyChallenges } from "#data/challenge";
import { SpeciesFormChangeMoveLearnedTrigger } from "#data/form-change-triggers";
import { Gender } from "#data/gender";
import { ChallengeType } from "#enums/challenge-type";
@ -10,6 +9,7 @@ import { UiMode } from "#enums/ui-mode";
import { overrideHeldItems, overrideModifiers } from "#modifiers/modifier";
import { SaveSlotUiMode } from "#ui/save-slot-select-ui-handler";
import type { Starter } from "#ui/starter-select-ui-handler";
import { applyChallenges } from "#utils/challenge-utils";
import { isNullOrUndefined } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";

View File

@ -3,10 +3,13 @@ import { globalScene } from "#app/global-scene";
import { modifierTypes } from "#data/data-lists";
import { BattleType } from "#enums/battle-type";
import type { BattlerIndex } from "#enums/battler-index";
import { ChallengeType } from "#enums/challenge-type";
import { ClassicFixedBossWaves } from "#enums/fixed-boss-waves";
import type { CustomModifierSettings } from "#modifiers/modifier-type";
import { handleMysteryEncounterVictory } from "#mystery-encounters/encounter-phase-utils";
import { PokemonPhase } from "#phases/pokemon-phase";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder } from "#utils/common";
export class VictoryPhase extends PokemonPhase {
public readonly phaseName = "VictoryPhase";
@ -63,7 +66,9 @@ export class VictoryPhase extends PokemonPhase {
break;
}
}
if (globalScene.currentBattle.waveIndex % 10) {
const healStatus = new BooleanHolder(globalScene.currentBattle.waveIndex % 10 === 0);
applyChallenges(ChallengeType.PARTY_HEAL, healStatus);
if (!healStatus.value) {
globalScene.phaseManager.pushNew(
"SelectModifierPhase",
undefined,

View File

@ -5,6 +5,7 @@ import {
FlipStatChallenge,
FreshStartChallenge,
InverseBattleChallenge,
LimitedCatchChallenge,
SingleGenerationChallenge,
SingleTypeChallenge,
} from "#data/challenge";
@ -922,6 +923,19 @@ export const achvs = {
c.value > 0 &&
globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0),
).setSecret(),
// TODO: Decide on icon
NUZLOCKE: new ChallengeAchv(
"NUZLOCKE",
"",
"NUZLOCKE.description",
"leaf_stone",
100,
c =>
c instanceof LimitedCatchChallenge &&
c.value > 0 &&
globalScene.gameMode.challenges.some(c => c.id === Challenges.PERMANENT_FAINT && c.value > 0) &&
globalScene.gameMode.challenges.some(c => c.id === Challenges.FRESH_START && c.value > 0),
),
BREEDERS_IN_SPACE: new Achv("BREEDERS_IN_SPACE", "", "BREEDERS_IN_SPACE.description", "moon_stone", 50).setSecret(),
};

View File

@ -11,7 +11,6 @@ import { speciesEggMoves } from "#balance/egg-moves";
import { pokemonPrevolutions } from "#balance/pokemon-evolutions";
import { speciesStarterCosts } from "#balance/starters";
import { ArenaTrapTag } from "#data/arena-tag";
import { applyChallenges } from "#data/challenge";
import { allMoves, allSpecies } from "#data/data-lists";
import type { Egg } from "#data/egg";
import { pokemonFormChanges } from "#data/pokemon-forms";
@ -63,6 +62,7 @@ import { VoucherType, vouchers } from "#system/voucher";
import { trainerConfigs } from "#trainers/trainer-config";
import type { DexData, DexEntry } from "#types/dex-data";
import { RUN_HISTORY_LIMIT } from "#ui/run-history-ui-handler";
import { applyChallenges } from "#utils/challenge-utils";
import { executeIf, fixedInt, isLocal, NumberHolder, randInt, randSeedItem } from "#utils/common";
import { decrypt, encrypt } from "#utils/data";
import { getEnumKeys } from "#utils/enums";

View File

@ -209,10 +209,10 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
this.updateRerollCostText();
const typeOptions = args[1] as ModifierTypeOption[];
const removeHealShop = globalScene.gameMode.hasNoShop;
const hasShop = globalScene.gameMode.getShopStatus();
const baseShopCost = new NumberHolder(globalScene.getWaveMoneyAmount(1));
globalScene.applyModifier(HealShopCostModifier, true, baseShopCost);
const shopTypeOptions = !removeHealShop
const shopTypeOptions = hasShop
? getPlayerShopModifierTypeOptionsForWave(globalScene.currentBattle.waveIndex, baseShopCost.value)
: [];
const optionsYOffset =
@ -370,7 +370,7 @@ export class ModifierSelectUiHandler extends AwaitableUiHandler {
if (globalScene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) {
this.setRowCursor(0);
this.setCursor(2);
} else if (globalScene.shopCursorTarget === ShopCursorTarget.SHOP && globalScene.gameMode.hasNoShop) {
} else if (globalScene.shopCursorTarget === ShopCursorTarget.SHOP && !hasShop) {
this.setRowCursor(ShopCursorTarget.REWARDS);
this.setCursor(0);
} else {

View File

@ -1,7 +1,6 @@
import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import { pokemonEvolutions } from "#balance/pokemon-evolutions";
import { applyChallenges } from "#data/challenge";
import { allMoves } from "#data/data-lists";
import { SpeciesFormChangeItemTrigger } from "#data/form-change-triggers";
import { Gender, getGenderColor, getGenderSymbol } from "#data/gender";
@ -26,6 +25,7 @@ import { MoveInfoOverlay } from "#ui/move-info-overlay";
import { PokemonIconAnimHandler, PokemonIconAnimMode } from "#ui/pokemon-icon-anim-handler";
import { addBBCodeTextObject, addTextObject, getTextColor } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
import { applyChallenges } from "#utils/challenge-utils";
import { BooleanHolder, getLocalizedSpriteKey, randInt } from "#utils/common";
import { toTitleCase } from "#utils/strings";
import i18next from "i18next";

View File

@ -16,7 +16,6 @@ import {
POKERUS_STARTER_COUNT,
speciesStarterCosts,
} from "#balance/starters";
import { applyChallenges, checkStarterValidForChallenge } from "#data/challenge";
import { allAbilities, allMoves, allSpecies } from "#data/data-lists";
import { Egg, getEggTierForSpecies } from "#data/egg";
import { GrowthRate, getGrowthRateColor } from "#data/exp";
@ -59,6 +58,7 @@ import { StarterContainer } from "#ui/starter-container";
import { StatsContainer } from "#ui/stats-container";
import { addBBCodeTextObject, addTextObject } from "#ui/text";
import { addWindow } from "#ui/ui-theme";
import { applyChallenges, checkStarterValidForChallenge } from "#utils/challenge-utils";
import {
BooleanHolder,
fixedInt,

View File

@ -0,0 +1,409 @@
import type { FixedBattleConfig } from "#app/battle";
import { globalScene } from "#app/global-scene";
import { pokemonEvolutions } from "#balance/pokemon-evolutions";
import { pokemonFormChanges } from "#data/pokemon-forms";
import type { PokemonSpecies } from "#data/pokemon-species";
import { ChallengeType } from "#enums/challenge-type";
import type { MoveId } from "#enums/move-id";
import type { MoveSourceType } from "#enums/move-source-type";
import type { SpeciesId } from "#enums/species-id";
import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
import type { ModifierTypeOption } from "#modifiers/modifier-type";
import type { DexAttrProps } from "#system/game-data";
import { BooleanHolder, type NumberHolder } from "./common";
import { getPokemonSpecies } from "./pokemon-utils";
/**
* Apply all challenges that modify starter choice.
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_CHOICE
* @param pokemon {@link PokemonSpecies} The pokemon to check the validity of.
* @param valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
* @param dexAttr {@link DexAttrProps} The dex attributes of the pokemon.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.STARTER_CHOICE,
pokemon: PokemonSpecies,
valid: BooleanHolder,
dexAttr: DexAttrProps,
): boolean;
/**
* Apply all challenges that modify available total starter points.
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_POINTS
* @param points {@link NumberHolder} The amount of points you have available.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(challengeType: ChallengeType.STARTER_POINTS, points: NumberHolder): boolean;
/**
* Apply all challenges that modify the cost of a starter.
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_COST
* @param species {@link SpeciesId} The pokemon to change the cost of.
* @param points {@link NumberHolder} The cost of the pokemon.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.STARTER_COST,
species: SpeciesId,
cost: NumberHolder,
): boolean;
/**
* Apply all challenges that modify a starter after selection.
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_MODIFY
* @param pokemon {@link Pokemon} The starter pokemon to modify.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(challengeType: ChallengeType.STARTER_MODIFY, pokemon: Pokemon): boolean;
/**
* Apply all challenges that what pokemon you can have in battle.
* @param challengeType {@link ChallengeType} ChallengeType.POKEMON_IN_BATTLE
* @param pokemon {@link Pokemon} The pokemon to check the validity of.
* @param valid {@link BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.POKEMON_IN_BATTLE,
pokemon: Pokemon,
valid: BooleanHolder,
): boolean;
/**
* Apply all challenges that modify what fixed battles there are.
* @param challengeType {@link ChallengeType} ChallengeType.FIXED_BATTLES
* @param waveIndex {@link Number} The current wave index.
* @param battleConfig {@link FixedBattleConfig} The battle config to modify.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.FIXED_BATTLES,
waveIndex: number,
battleConfig: FixedBattleConfig,
): boolean;
/**
* Apply all challenges that modify type effectiveness.
* @param challengeType {@linkcode ChallengeType} ChallengeType.TYPE_EFFECTIVENESS
* @param effectiveness {@linkcode NumberHolder} The current effectiveness of the move.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(challengeType: ChallengeType.TYPE_EFFECTIVENESS, effectiveness: NumberHolder): boolean;
/**
* Apply all challenges that modify what level AI are.
* @param challengeType {@link ChallengeType} ChallengeType.AI_LEVEL
* @param level {@link NumberHolder} The generated level of the pokemon.
* @param levelCap {@link Number} The maximum level cap for the current wave.
* @param isTrainer {@link Boolean} Whether this is a trainer pokemon.
* @param isBoss {@link Boolean} Whether this is a non-trainer boss pokemon.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.AI_LEVEL,
level: NumberHolder,
levelCap: number,
isTrainer: boolean,
isBoss: boolean,
): boolean;
/**
* Apply all challenges that modify how many move slots the AI has.
* @param challengeType {@link ChallengeType} ChallengeType.AI_MOVE_SLOTS
* @param pokemon {@link Pokemon} The pokemon being considered.
* @param moveSlots {@link NumberHolder} The amount of move slots.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.AI_MOVE_SLOTS,
pokemon: Pokemon,
moveSlots: NumberHolder,
): boolean;
/**
* Apply all challenges that modify whether a pokemon has its passive.
* @param challengeType {@link ChallengeType} ChallengeType.PASSIVE_ACCESS
* @param pokemon {@link Pokemon} The pokemon to modify.
* @param hasPassive {@link BooleanHolder} Whether it has its passive.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.PASSIVE_ACCESS,
pokemon: Pokemon,
hasPassive: BooleanHolder,
): boolean;
/**
* Apply all challenges that modify the game modes settings.
* @param challengeType {@link ChallengeType} ChallengeType.GAME_MODE_MODIFY
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(challengeType: ChallengeType.GAME_MODE_MODIFY): boolean;
/**
* Apply all challenges that modify what level a pokemon can access a move.
* @param challengeType {@link ChallengeType} ChallengeType.MOVE_ACCESS
* @param pokemon {@link Pokemon} What pokemon would learn the move.
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @param move {@link MoveId} The move in question.
* @param level {@link NumberHolder} The level threshold for access.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.MOVE_ACCESS,
pokemon: Pokemon,
moveSource: MoveSourceType,
move: MoveId,
level: NumberHolder,
): boolean;
/**
* Apply all challenges that modify what weight a pokemon gives to move generation
* @param challengeType {@link ChallengeType} ChallengeType.MOVE_WEIGHT
* @param pokemon {@link Pokemon} What pokemon would learn the move.
* @param moveSource {@link MoveSourceType} What source the pokemon would get the move from.
* @param move {@link MoveId} The move in question.
* @param weight {@link NumberHolder} The weight of the move.
* @returns True if any challenge was successfully applied.
*/
export function applyChallenges(
challengeType: ChallengeType.MOVE_WEIGHT,
pokemon: Pokemon,
moveSource: MoveSourceType,
move: MoveId,
weight: NumberHolder,
): boolean;
export function applyChallenges(challengeType: ChallengeType.FLIP_STAT, pokemon: Pokemon, baseStats: number[]): boolean;
/**
* Apply all challenges that conditionally enable or disable automatic party healing during biome transitions
* @param challengeType - {@linkcode ChallengeType.PARTY_HEAL}
* @param status - Whether party healing is enabled or not
* @returns `true` if any challenge was successfully applied, `false` otherwise
*/
export function applyChallenges(challengeType: ChallengeType.PARTY_HEAL, status: BooleanHolder): boolean;
/**
* Apply all challenges that conditionally enable or disable the shop
* @param challengeType - {@linkcode ChallengeType.SHOP}
* @param status - Whether party healing is enabled or not
* @returns `true` if any challenge was successfully applied, `false` otherwise
*/
export function applyChallenges(challengeType: ChallengeType.SHOP, status: BooleanHolder): boolean;
/**
* Apply all challenges that validate whether a pokemon can be added to the player's party or not
* @param challengeType - {@linkcode ChallengeType.POKEMON_ADD_TO_PARTY}
* @param pokemon - The pokemon being caught
* @param status - Whether the pokemon can be added to the party or not
* @return `true` if any challenge was sucessfully applied, `false` otherwise
*/
export function applyChallenges(
challengeType: ChallengeType.POKEMON_ADD_TO_PARTY,
pokemon: EnemyPokemon,
status: BooleanHolder,
): boolean;
/**
* Apply all challenges that validate whether a pokemon is allowed to fuse or not
* @param challengeType - {@linkcode ChallengeType.POKEMON_FUSION}
* @param pokemon - The pokemon being checked
* @param status - Whether the selected pokemon is allowed to fuse or not
* @return `true` if any challenge was sucessfully applied, `false` otherwise
*/
export function applyChallenges(
challengeType: ChallengeType.POKEMON_FUSION,
pokemon: PlayerPokemon,
status: BooleanHolder,
): boolean;
/**
* Apply all challenges that validate whether particular moves can or cannot be used
* @param challengeType - {@linkcode ChallengeType.POKEMON_MOVE}
* @param moveId - The move being checked
* @param status - Whether the move can be used or not
* @return `true` if any challenge was sucessfully applied, `false` otherwise
*/
export function applyChallenges(
challengeType: ChallengeType.POKEMON_MOVE,
moveId: MoveId,
status: BooleanHolder,
): boolean;
/**
* Apply all challenges that validate whether particular items are or are not sold in the shop
* @param challengeType - {@linkcode ChallengeType.SHOP_ITEM}
* @param shopItem - The item being checked
* @param status - Whether the item should be added to the shop or not
* @return `true` if any challenge was sucessfully applied, `false` otherwise
*/
export function applyChallenges(
challengeType: ChallengeType.SHOP_ITEM,
shopItem: ModifierTypeOption | null,
status: BooleanHolder,
): boolean;
/**
* Apply all challenges that validate whether particular items will be given as a reward after a wave
* @param challengeType - {@linkcode ChallengeType.WAVE_REWARD}
* @param reward - The reward being checked
* @param status - Whether the reward should be added to the reward options or not
* @return `true` if any challenge was sucessfully applied, `false` otherwise
*/
export function applyChallenges(
challengeType: ChallengeType.WAVE_REWARD,
reward: ModifierTypeOption | null,
status: BooleanHolder,
): boolean;
/**
* Apply all challenges that prevent recovery from fainting
* @param challengeType - {@linkcode ChallengeType.PREVENT_REVIVE}
* @param status - Whether fainting is a permanent status or not
* @return `true` if any challenge was sucessfully applied, `false` otherwise
*/
export function applyChallenges(challengeType: ChallengeType.PREVENT_REVIVE, status: BooleanHolder): boolean;
export function applyChallenges(challengeType: ChallengeType, ...args: any[]): boolean {
let ret = false;
globalScene.gameMode.challenges.forEach(c => {
if (c.value !== 0) {
switch (challengeType) {
case ChallengeType.STARTER_CHOICE:
ret ||= c.applyStarterChoice(args[0], args[1], args[2]);
break;
case ChallengeType.STARTER_POINTS:
ret ||= c.applyStarterPoints(args[0]);
break;
case ChallengeType.STARTER_COST:
ret ||= c.applyStarterCost(args[0], args[1]);
break;
case ChallengeType.STARTER_MODIFY:
ret ||= c.applyStarterModify(args[0]);
break;
case ChallengeType.POKEMON_IN_BATTLE:
ret ||= c.applyPokemonInBattle(args[0], args[1]);
break;
case ChallengeType.FIXED_BATTLES:
ret ||= c.applyFixedBattle(args[0], args[1]);
break;
case ChallengeType.TYPE_EFFECTIVENESS:
ret ||= c.applyTypeEffectiveness(args[0]);
break;
case ChallengeType.AI_LEVEL:
ret ||= c.applyLevelChange(args[0], args[1], args[2], args[3]);
break;
case ChallengeType.AI_MOVE_SLOTS:
ret ||= c.applyMoveSlot(args[0], args[1]);
break;
case ChallengeType.PASSIVE_ACCESS:
ret ||= c.applyPassiveAccess(args[0], args[1]);
break;
case ChallengeType.GAME_MODE_MODIFY:
ret ||= c.applyGameModeModify();
break;
case ChallengeType.MOVE_ACCESS:
ret ||= c.applyMoveAccessLevel(args[0], args[1], args[2], args[3]);
break;
case ChallengeType.MOVE_WEIGHT:
ret ||= c.applyMoveWeight(args[0], args[1], args[2], args[3]);
break;
case ChallengeType.FLIP_STAT:
ret ||= c.applyFlipStat(args[0], args[1]);
break;
case ChallengeType.PARTY_HEAL:
ret ||= c.applyPartyHeal(args[0]);
break;
case ChallengeType.SHOP:
ret ||= c.applyShop(args[0]);
break;
case ChallengeType.POKEMON_ADD_TO_PARTY:
ret ||= c.applyPokemonAddToParty(args[0], args[1]);
break;
case ChallengeType.POKEMON_FUSION:
ret ||= c.applyPokemonFusion(args[0], args[1]);
break;
case ChallengeType.POKEMON_MOVE:
ret ||= c.applyPokemonMove(args[0], args[1]);
break;
case ChallengeType.SHOP_ITEM:
ret ||= c.applyShopItem(args[0], args[1]);
break;
case ChallengeType.WAVE_REWARD:
ret ||= c.applyWaveReward(args[0], args[1]);
break;
case ChallengeType.PREVENT_REVIVE:
ret ||= c.applyPreventRevive(args[0]);
break;
}
}
});
return ret;
}
/**
* Apply all challenges to the given starter (and form) to check its validity.
* Differs from {@linkcode checkSpeciesValidForChallenge} which only checks form changes.
* @param species - The {@linkcode PokemonSpecies} to check the validity of.
* @param dexAttr - The {@linkcode DexAttrProps | dex attributes} of the species, including its form index.
* @param soft - If `true`, allow it if it could become valid through evolution or form change.
* @returns `true` if the species is considered valid.
*/
export function checkStarterValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) {
if (!soft) {
const isValidForChallenge = new BooleanHolder(true);
applyChallenges(ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props);
return isValidForChallenge.value;
}
// We check the validity of every evolution and form change, and require that at least one is valid
const speciesToCheck = [species.speciesId];
while (speciesToCheck.length) {
const checking = speciesToCheck.pop();
// Linter complains if we don't handle this
if (!checking) {
return false;
}
const checkingSpecies = getPokemonSpecies(checking);
if (checkSpeciesValidForChallenge(checkingSpecies, props, true)) {
return true;
}
if (checking && pokemonEvolutions.hasOwnProperty(checking)) {
pokemonEvolutions[checking].forEach(e => {
// Form check to deal with cases such as Basculin -> Basculegion
// TODO: does this miss anything if checking forms of a stage 2 Pokémon?
if (!e?.preFormKey || e.preFormKey === species.forms[props.formIndex].formKey) {
speciesToCheck.push(e.speciesId);
}
});
}
}
return false;
}
/**
* Apply all challenges to the given species (and form) to check its validity.
* Differs from {@linkcode checkStarterValidForChallenge} which also checks evolutions.
* @param species - The {@linkcode PokemonSpecies} to check the validity of.
* @param dexAttr - The {@linkcode DexAttrProps | dex attributes} of the species, including its form index.
* @param soft - If `true`, allow it if it could become valid through a form change.
* @returns `true` if the species is considered valid.
*/
function checkSpeciesValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) {
const isValidForChallenge = new BooleanHolder(true);
applyChallenges(ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props);
if (!soft || !pokemonFormChanges.hasOwnProperty(species.speciesId)) {
return isValidForChallenge.value;
}
// If the form in props is valid, return true before checking other form changes
if (soft && isValidForChallenge.value) {
return true;
}
const result = pokemonFormChanges[species.speciesId].some(f1 => {
// Exclude form changes that require the mon to be on the field to begin with
if (!("item" in f1.trigger)) {
return false;
}
return species.forms.some((f2, formIndex) => {
if (f1.formKey === f2.formKey) {
const formProps = { ...props, formIndex };
const isFormValidForChallenge = new BooleanHolder(true);
applyChallenges(ChallengeType.STARTER_CHOICE, species, isFormValidForChallenge, formProps);
return isFormValidForChallenge.value;
}
return false;
});
});
return result;
}

View File

@ -38,7 +38,7 @@ describe("Abilities - Analytic", () => {
it("should increase damage if the user moves last", async () => {
await game.classicMode.startBattle([SpeciesId.ARCEUS]);
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
game.move.select(MoveId.TACKLE);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);

View File

@ -36,7 +36,7 @@ describe("Ability - Anger Point", () => {
it("should set the user's attack stage to +6 when hit by a critical hit", async () => {
game.override.enemyAbility(AbilityId.ANGER_POINT).moveset(MoveId.FALSE_SWIPE);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
// minimize the enemy's attack stage to ensure it is always set to +6
enemy.setStatStage(Stat.ATK, -6);
@ -53,7 +53,7 @@ describe("Ability - Anger Point", () => {
.enemyAbility(AbilityId.ANGER_POINT)
.ability(AbilityId.SKILL_LINK);
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
vi.spyOn(enemy, "getCriticalHitResult").mockReturnValueOnce(true);
const angerPointSpy = vi.spyOn(PostReceiveCritStatStageChangeAbAttr.prototype, "apply");
game.move.select(MoveId.BULLET_SEED);
@ -68,7 +68,7 @@ describe("Ability - Anger Point", () => {
.enemyHasPassiveAbility(true)
.moveset(MoveId.FALSE_SWIPE);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
vi.spyOn(enemy, "getCriticalHitResult").mockReturnValueOnce(true);
enemy.setStatStage(Stat.ATK, 6);
game.move.select(MoveId.FALSE_SWIPE);

View File

@ -36,7 +36,7 @@ describe("Abilities - Beast Boost", () => {
it("should prefer highest stat to boost its corresponding stat stage by 1 when winning a battle", async () => {
await game.classicMode.startBattle([SpeciesId.SLOWBRO]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
// Set the pokemon's highest stat to DEF, so it should be picked by Beast Boost
vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([10000, 100, 1000, 200, 100, 100]);
console.log(playerPokemon.stats);
@ -54,7 +54,7 @@ describe("Abilities - Beast Boost", () => {
await game.classicMode.startBattle([SpeciesId.SLOWBRO]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
// If the opponent uses Guard Split, the pokemon's second highest stat (SPATK) should be chosen
vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([10000, 100, 201, 200, 100, 100]);
@ -72,7 +72,7 @@ describe("Abilities - Beast Boost", () => {
// Order preference follows the order of EFFECTIVE_STAT
await game.classicMode.startBattle([SpeciesId.SLOWBRO]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
// Set up tie between SPATK, SPDEF, and SPD, where SPATK should win
vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([10000, 1, 1, 100, 100, 100]);

View File

@ -36,7 +36,7 @@ describe("Abilities - Competitive", () => {
it("lower atk and def by 1 via tickle, then increase spatk by 4 via competitive", async () => {
await game.classicMode.startBattle([SpeciesId.FLYGON]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to(TurnInitPhase);
@ -49,7 +49,7 @@ describe("Abilities - Competitive", () => {
game.override.enemyMoveset(MoveId.SPLASH);
await game.classicMode.startBattle([SpeciesId.FLYGON]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.CLOSE_COMBAT);
await game.phaseInterceptor.to(TurnInitPhase);
@ -62,7 +62,7 @@ describe("Abilities - Competitive", () => {
game.override.startingHeldItems([{ name: "WHITE_HERB" }]);
await game.classicMode.startBattle([SpeciesId.FLYGON]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to(TurnInitPhase);

View File

@ -33,7 +33,7 @@ describe("Abilities - Contrary", () => {
it("should invert stat changes when applied", async () => {
await game.classicMode.startBattle([SpeciesId.SLOWBRO]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
});
@ -43,7 +43,7 @@ describe("Abilities - Contrary", () => {
game.override.enemyPassiveAbility(AbilityId.CLEAR_BODY).moveset([MoveId.TAIL_WHIP]);
await game.classicMode.startBattle([SpeciesId.SLOWBRO]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
@ -57,7 +57,7 @@ describe("Abilities - Contrary", () => {
game.override.enemyPassiveAbility(AbilityId.CLEAR_BODY).enemyMoveset(MoveId.HOWL).moveset([MoveId.SPLASH]);
await game.classicMode.startBattle([SpeciesId.SLOWBRO]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);

View File

@ -43,7 +43,7 @@ describe("Abilities - Cud Chew", () => {
it("stores inside summonData at end of turn", async () => {
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
const farigiraf = game.scene.getPlayerPokemon()!;
const farigiraf = game.field.getPlayerPokemon();
farigiraf.hp = 1; // needed to allow sitrus procs
game.move.select(MoveId.SPLASH);
@ -71,7 +71,7 @@ describe("Abilities - Cud Chew", () => {
game.override.enemyMoveset([MoveId.SPLASH, MoveId.HEAL_PULSE]);
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
const farigiraf = game.scene.getPlayerPokemon()!;
const farigiraf = game.field.getPlayerPokemon();
// Dip below half to eat berry
farigiraf.hp = farigiraf.getMaxHp() / 2 - 1;
@ -120,7 +120,7 @@ describe("Abilities - Cud Chew", () => {
.enemyMoveset(MoveId.TEATIME);
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
const farigiraf = game.scene.getPlayerPokemon()!;
const farigiraf = game.field.getPlayerPokemon();
farigiraf.hp = 1; // needed to allow berry procs
game.move.select(MoveId.STUFF_CHEEKS);
@ -148,7 +148,7 @@ describe("Abilities - Cud Chew", () => {
it("should reset both arrays on switch", async () => {
await game.classicMode.startBattle([SpeciesId.FARIGIRAF, SpeciesId.GIRAFARIG]);
const farigiraf = game.scene.getPlayerPokemon()!;
const farigiraf = game.field.getPlayerPokemon();
farigiraf.hp = 1;
// eat berry turn 1, switch out turn 2
@ -177,7 +177,7 @@ describe("Abilities - Cud Chew", () => {
game.override.enemyAbility(AbilityId.NEUTRALIZING_GAS);
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
const farigiraf = game.scene.getPlayerPokemon()!;
const farigiraf = game.field.getPlayerPokemon();
farigiraf.hp = 1;
game.move.select(MoveId.SPLASH);
@ -199,7 +199,7 @@ describe("Abilities - Cud Chew", () => {
const apply = vi.spyOn(CudChewConsumeBerryAbAttr.prototype, "apply");
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
const farigiraf = game.scene.getPlayerPokemon()!;
const farigiraf = game.field.getPlayerPokemon();
farigiraf.hp = 1;
game.move.select(MoveId.SPLASH);
@ -225,7 +225,7 @@ describe("Abilities - Cud Chew", () => {
game.override.enemyAbility(AbilityId.UNNERVE);
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
const farigiraf = game.scene.getPlayerPokemon()!;
const farigiraf = game.field.getPlayerPokemon();
farigiraf.hp = 1;
game.move.select(MoveId.SPLASH);
@ -243,7 +243,7 @@ describe("Abilities - Cud Chew", () => {
game.override.enemyMoveset(MoveId.INCINERATE);
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
const farigiraf = game.scene.getPlayerPokemon()!;
const farigiraf = game.field.getPlayerPokemon();
farigiraf.hp = farigiraf.getMaxHp() / 4;
game.move.select(MoveId.SPLASH);
@ -262,7 +262,7 @@ describe("Abilities - Cud Chew", () => {
.startingHeldItems([]);
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
const farigiraf = game.scene.getPlayerPokemon()!;
const farigiraf = game.field.getPlayerPokemon();
game.move.select(MoveId.BUG_BITE);
await game.toNextTurn();
@ -278,7 +278,7 @@ describe("Abilities - Cud Chew", () => {
game.override.passiveAbility(AbilityId.RIPEN);
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
const farigiraf = game.scene.getPlayerPokemon()!;
const farigiraf = game.field.getPlayerPokemon();
farigiraf.hp = 1;
game.move.select(MoveId.SPLASH);
@ -294,7 +294,7 @@ describe("Abilities - Cud Chew", () => {
game.override.enemyLevel(1);
await game.classicMode.startBattle([SpeciesId.FARIGIRAF]);
const farigiraf = game.scene.getPlayerPokemon()!;
const farigiraf = game.field.getPlayerPokemon();
farigiraf.hp = 1;
game.move.select(MoveId.HYPER_VOICE);
@ -307,7 +307,7 @@ describe("Abilities - Cud Chew", () => {
// reload and the berry should still be there
await game.reload.reloadSession();
const farigirafReloaded = game.scene.getPlayerPokemon()!;
const farigirafReloaded = game.field.getPlayerPokemon();
expect(farigirafReloaded.summonData.berriesEatenLast).toEqual([BerryType.SITRUS]);
const wave1Hp = farigirafReloaded.hp;

View File

@ -125,7 +125,7 @@ describe("Abilities - Dancer", () => {
game.override.battleStyle("double").moveset(MoveId.SPLASH).enemyMoveset([MoveId.SWORDS_DANCE, MoveId.FAKE_OUT]);
await game.classicMode.startBattle([SpeciesId.ORICORIO]);
const oricorio = game.scene.getPlayerPokemon()!;
const oricorio = game.field.getPlayerPokemon();
expect(oricorio).toBeDefined();
// get faked out and copy swords dance

View File

@ -36,7 +36,7 @@ describe("Abilities - Defiant", () => {
it("lower atk and def by 1 via tickle, then increase atk by 4 via defiant", async () => {
await game.classicMode.startBattle([SpeciesId.FLYGON]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to(TurnInitPhase);
@ -48,7 +48,7 @@ describe("Abilities - Defiant", () => {
game.override.enemyMoveset(MoveId.SPLASH);
await game.classicMode.startBattle([SpeciesId.FLYGON]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.CLOSE_COMBAT);
await game.phaseInterceptor.to(TurnInitPhase);
@ -61,7 +61,7 @@ describe("Abilities - Defiant", () => {
game.override.startingHeldItems([{ name: "WHITE_HERB" }]);
await game.classicMode.startBattle([SpeciesId.FLYGON]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to(TurnInitPhase);

View File

@ -145,7 +145,7 @@ describe("Abilities - Desolate Land", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN);
vi.spyOn(game.scene.getPlayerPokemon()!, "randBattleSeedInt").mockReturnValue(0);
vi.spyOn(game.field.getPlayerPokemon(), "randBattleSeedInt").mockReturnValue(0);
vi.spyOn(globalScene, "randBattleSeedInt").mockReturnValue(0);
const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase;

View File

@ -37,7 +37,7 @@ describe("Abilities - Disguise", () => {
it("takes no damage from attacking move and transforms to Busted form, takes 1/8 max HP damage from the disguise breaking", async () => {
await game.classicMode.startBattle();
const mimikyu = game.scene.getEnemyPokemon()!;
const mimikyu = game.field.getEnemyPokemon();
const maxHp = mimikyu.getMaxHp();
const disguiseDamage = toDmgValue(maxHp / 8);
@ -54,7 +54,7 @@ describe("Abilities - Disguise", () => {
it("doesn't break disguise when attacked with ineffective move", async () => {
await game.classicMode.startBattle();
const mimikyu = game.scene.getEnemyPokemon()!;
const mimikyu = game.field.getEnemyPokemon();
expect(mimikyu.formIndex).toBe(disguisedForm);
@ -69,7 +69,7 @@ describe("Abilities - Disguise", () => {
game.override.moveset([MoveId.SURGING_STRIKES]).enemyLevel(5);
await game.classicMode.startBattle();
const mimikyu = game.scene.getEnemyPokemon()!;
const mimikyu = game.field.getEnemyPokemon();
const maxHp = mimikyu.getMaxHp();
const disguiseDamage = toDmgValue(maxHp / 8);
@ -91,7 +91,7 @@ describe("Abilities - Disguise", () => {
it("takes effects from status moves and damage from status effects", async () => {
await game.classicMode.startBattle();
const mimikyu = game.scene.getEnemyPokemon()!;
const mimikyu = game.field.getEnemyPokemon();
expect(mimikyu.hp).toBe(mimikyu.getMaxHp());
game.move.select(MoveId.TOXIC_THREAD);
@ -109,7 +109,7 @@ describe("Abilities - Disguise", () => {
await game.classicMode.startBattle([SpeciesId.MIMIKYU, SpeciesId.FURRET]);
const mimikyu = game.scene.getPlayerPokemon()!;
const mimikyu = game.field.getPlayerPokemon();
const maxHp = mimikyu.getMaxHp();
const disguiseDamage = toDmgValue(maxHp / 8);
@ -154,7 +154,7 @@ describe("Abilities - Disguise", () => {
await game.classicMode.startBattle();
const mimikyu = game.scene.getPlayerPokemon()!;
const mimikyu = game.field.getPlayerPokemon();
expect(mimikyu.formIndex).toBe(bustedForm);
@ -175,7 +175,7 @@ describe("Abilities - Disguise", () => {
await game.classicMode.startBattle([SpeciesId.MIMIKYU, SpeciesId.FURRET]);
const mimikyu1 = game.scene.getPlayerPokemon()!;
const mimikyu1 = game.field.getPlayerPokemon();
expect(mimikyu1.formIndex).toBe(bustedForm);
@ -190,7 +190,7 @@ describe("Abilities - Disguise", () => {
game.override.enemyMoveset([MoveId.ENDURE]);
await game.classicMode.startBattle();
const mimikyu = game.scene.getEnemyPokemon()!;
const mimikyu = game.field.getEnemyPokemon();
mimikyu.hp = 1;
game.move.select(MoveId.SHADOW_SNEAK);
@ -205,7 +205,7 @@ describe("Abilities - Disguise", () => {
await game.classicMode.startBattle();
const mimikyu = game.scene.getEnemyPokemon()!;
const mimikyu = game.field.getEnemyPokemon();
const maxHp = mimikyu.getMaxHp();
const disguiseDamage = toDmgValue(maxHp / 8);
@ -225,6 +225,6 @@ describe("Abilities - Disguise", () => {
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.toNextTurn();
expect(game.scene.getEnemyPokemon()!.formIndex).toBe(disguisedForm);
expect(game.field.getEnemyPokemon().formIndex).toBe(disguisedForm);
});
});

View File

@ -35,7 +35,7 @@ describe("Abilities - Dry Skin", () => {
it("during sunlight, lose 1/8 of maximum health at the end of each turn", async () => {
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
// first turn
game.move.select(MoveId.SUNNY_DAY);
@ -52,7 +52,7 @@ describe("Abilities - Dry Skin", () => {
it("during rain, gain 1/8 of maximum health at the end of each turn", async () => {
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
enemy.hp = 1;
@ -72,7 +72,7 @@ describe("Abilities - Dry Skin", () => {
game.override.moveset([MoveId.FLAMETHROWER]);
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
const initialHP = 1000;
enemy.hp = initialHP;
@ -95,7 +95,7 @@ describe("Abilities - Dry Skin", () => {
it("opposing water attacks heal 1/4 of maximum health and deal no damage", async () => {
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
enemy.hp = 1;
@ -109,7 +109,7 @@ describe("Abilities - Dry Skin", () => {
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
enemy.hp = 1;
@ -123,7 +123,7 @@ describe("Abilities - Dry Skin", () => {
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
enemy.hp = 1;
@ -145,7 +145,7 @@ describe("Abilities - Dry Skin", () => {
it("opposing water moves still heal regardless of accuracy check", async () => {
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
game.move.select(MoveId.WATER_GUN);
enemy.hp = enemy.hp - 1;

View File

@ -37,7 +37,7 @@ describe("Abilities - Early Bird", () => {
it("reduces Rest's sleep time to 1 turn", async () => {
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
const player = game.scene.getPlayerPokemon()!;
const player = game.field.getPlayerPokemon();
game.move.select(MoveId.BELLY_DRUM);
await game.toNextTurn();
@ -62,7 +62,7 @@ describe("Abilities - Early Bird", () => {
it("reduces 3-turn sleep to 1 turn", async () => {
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
const player = game.scene.getPlayerPokemon()!;
const player = game.field.getPlayerPokemon();
player.status = new Status(StatusEffect.SLEEP, 0, 4);
game.move.select(MoveId.SPLASH);
@ -81,7 +81,7 @@ describe("Abilities - Early Bird", () => {
it("reduces 1-turn sleep to 0 turns", async () => {
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
const player = game.scene.getPlayerPokemon()!;
const player = game.field.getPlayerPokemon();
player.status = new Status(StatusEffect.SLEEP, 0, 2);
game.move.select(MoveId.SPLASH);

View File

@ -39,7 +39,7 @@ describe("Abilities - Flash Fire", () => {
game.override.enemyMoveset([MoveId.EMBER]).moveset(MoveId.SPLASH);
await game.classicMode.startBattle([SpeciesId.BLISSEY]);
const blissey = game.scene.getPlayerPokemon()!;
const blissey = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase);
@ -50,7 +50,7 @@ describe("Abilities - Flash Fire", () => {
game.override.enemyMoveset([MoveId.EMBER]).moveset([MoveId.PROTECT]);
await game.classicMode.startBattle([SpeciesId.BLISSEY]);
const blissey = game.scene.getPlayerPokemon()!;
const blissey = game.field.getPlayerPokemon();
game.move.select(MoveId.PROTECT);
await game.phaseInterceptor.to(TurnEndPhase);
@ -61,7 +61,7 @@ describe("Abilities - Flash Fire", () => {
game.override.enemyMoveset([MoveId.WILL_O_WISP]).moveset(MoveId.SPLASH);
await game.classicMode.startBattle([SpeciesId.BLISSEY]);
const blissey = game.scene.getPlayerPokemon()!;
const blissey = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
await game.move.forceHit();
@ -76,7 +76,7 @@ describe("Abilities - Flash Fire", () => {
game.override.enemyMoveset([MoveId.EMBER]).moveset(MoveId.SPLASH).statusEffect(StatusEffect.FREEZE);
await game.classicMode.startBattle([SpeciesId.BLISSEY]);
const blissey = game.scene.getPlayerPokemon()!;
const blissey = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
@ -95,8 +95,8 @@ describe("Abilities - Flash Fire", () => {
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to(TurnEndPhase);
const chansey = game.scene.getPlayerPokemon()!;
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(SpeciesId.CHANSEY);
const chansey = game.field.getPlayerPokemon();
expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.CHANSEY);
expect(chansey!.getTag(BattlerTagType.FIRE_BOOST)).toBeUndefined();
});
@ -107,7 +107,7 @@ describe("Abilities - Flash Fire", () => {
.enemyAbility(AbilityId.FLASH_FIRE)
.ability(AbilityId.NONE);
await game.classicMode.startBattle([SpeciesId.BLISSEY]);
const blissey = game.scene.getPlayerPokemon()!;
const blissey = game.field.getPlayerPokemon();
const initialHP = 1000;
blissey.hp = initialHP;
@ -137,7 +137,7 @@ describe("Abilities - Flash Fire", () => {
.enemySpecies(SpeciesId.BLISSEY);
await game.classicMode.startBattle([SpeciesId.RATTATA]);
const blissey = game.scene.getEnemyPokemon()!;
const blissey = game.field.getEnemyPokemon();
const initialHP = 1000;
blissey.hp = initialHP;

View File

@ -27,7 +27,7 @@ describe("Abilities - Flower Gift", () => {
game.move.select(MoveId.SPLASH);
expect(game.scene.getPlayerPokemon()?.formIndex).toBe(OVERCAST_FORM);
expect(game.field.getPlayerPokemon().formIndex).toBe(OVERCAST_FORM);
};
/**
@ -169,7 +169,7 @@ describe("Abilities - Flower Gift", () => {
game.override.weather(WeatherType.HARSH_SUN);
await game.classicMode.startBattle([SpeciesId.CHERRIM]);
const cherrim = game.scene.getPlayerPokemon()!;
const cherrim = game.field.getPlayerPokemon();
expect(cherrim.formIndex).toBe(SUNSHINE_FORM);
game.move.select(MoveId.SPLASH);
@ -188,7 +188,7 @@ describe("Abilities - Flower Gift", () => {
await game.classicMode.startBattle([SpeciesId.CHERRIM, SpeciesId.MAGIKARP]);
const cherrim = game.scene.getPlayerPokemon()!;
const cherrim = game.field.getPlayerPokemon();
expect(cherrim.formIndex).toBe(SUNSHINE_FORM);
@ -215,7 +215,7 @@ describe("Abilities - Flower Gift", () => {
game.override.weather(WeatherType.SUNNY);
await game.classicMode.startBattle([SpeciesId.CASTFORM, SpeciesId.MAGIKARP]);
const cherrim = game.scene.getPlayerPokemon()!;
const cherrim = game.field.getPlayerPokemon();
expect(cherrim.formIndex).toBe(SUNSHINE_FORM);

View File

@ -46,7 +46,7 @@ describe("Abilities - Flower Veil", () => {
.moveset([MoveId.REST, MoveId.SPLASH])
.startingHeldItems([{ name: "FLAME_ORB" }]);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const user = game.scene.getPlayerPokemon()!;
const user = game.field.getPlayerPokemon();
game.move.select(MoveId.REST);
await game.move.selectEnemyMove(MoveId.TACKLE);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
@ -74,7 +74,7 @@ describe("Abilities - Flower Veil", () => {
await game.move.selectEnemyMove(MoveId.YAWN, BattlerIndex.PLAYER_2);
await game.phaseInterceptor.to("BerryPhase");
const user = game.scene.getPlayerPokemon()!;
const user = game.field.getPlayerPokemon();
expect(user.getTag(BattlerTagType.DROWSY)).toBeFalsy();
expect(ally.getTag(BattlerTagType.DROWSY)).toBeFalsy();
});
@ -87,7 +87,7 @@ describe("Abilities - Flower Veil", () => {
game.move.select(MoveId.SPLASH);
await game.move.selectEnemyMove(MoveId.THUNDER_WAVE);
await game.toNextTurn();
expect(game.scene.getPlayerPokemon()!.status).toBeUndefined();
expect(game.field.getPlayerPokemon().status).toBeUndefined();
});
it("should not prevent status conditions for a non-grass user and its non-grass allies", async () => {
@ -155,7 +155,7 @@ describe("Abilities - Flower Veil", () => {
it("should prevent the drops while retaining the boosts from spicy extract", async () => {
game.override.enemyMoveset([MoveId.SPICY_EXTRACT]).moveset([MoveId.SPLASH]);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const user = game.scene.getPlayerPokemon()!;
const user = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
expect(user.getStatStage(Stat.ATK)).toBe(2);

View File

@ -32,7 +32,7 @@ describe("Abilities - Forecast", () => {
game.move.select(MoveId.SPLASH);
expect(game.scene.getPlayerPokemon()?.formIndex).toBe(NORMAL_FORM);
expect(game.field.getPlayerPokemon().formIndex).toBe(NORMAL_FORM);
};
beforeAll(() => {
@ -186,14 +186,14 @@ describe("Abilities - Forecast", () => {
game.move.select(MoveId.RAIN_DANCE);
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getPlayerPokemon()?.formIndex).toBe(RAINY_FORM);
expect(game.scene.getEnemyPokemon()?.formIndex).not.toBe(RAINY_FORM);
expect(game.field.getPlayerPokemon().formIndex).toBe(RAINY_FORM);
expect(game.field.getEnemyPokemon().formIndex).not.toBe(RAINY_FORM);
});
it("reverts to Normal Form when Forecast is suppressed, changes form to match the weather when it regains it", async () => {
game.override.enemyMoveset([MoveId.GASTRO_ACID]).weather(WeatherType.RAIN);
await game.classicMode.startBattle([SpeciesId.CASTFORM, SpeciesId.PIKACHU]);
const castform = game.scene.getPlayerPokemon()!;
const castform = game.field.getPlayerPokemon();
expect(castform.formIndex).toBe(RAINY_FORM);
@ -233,7 +233,7 @@ describe("Abilities - Forecast", () => {
game.doSwitchPokemon(1);
await game.phaseInterceptor.to(PostSummonPhase);
const castform = game.scene.getPlayerPokemon()!;
const castform = game.field.getPlayerPokemon();
// Damage phase should come first
await game.phaseInterceptor.to(DamageAnimPhase);
@ -248,7 +248,7 @@ describe("Abilities - Forecast", () => {
game.override.weather(WeatherType.RAIN);
await game.classicMode.startBattle([SpeciesId.CASTFORM, SpeciesId.MAGIKARP]);
const castform = game.scene.getPlayerPokemon()!;
const castform = game.field.getPlayerPokemon();
expect(castform.formIndex).toBe(RAINY_FORM);
@ -263,14 +263,14 @@ describe("Abilities - Forecast", () => {
it("should trigger player's form change when summoned at the same time as an enemy with a weather changing ability", async () => {
game.override.enemyAbility(AbilityId.DROUGHT);
await game.classicMode.startBattle([SpeciesId.CASTFORM, SpeciesId.MAGIKARP]);
const castform = game.scene.getPlayerPokemon()!;
const castform = game.field.getPlayerPokemon();
expect(castform.formIndex).toBe(SUNNY_FORM);
});
it("should trigger enemy's form change when summoned at the same time as a player with a weather changing ability", async () => {
game.override.ability(AbilityId.DROUGHT).enemySpecies(SpeciesId.CASTFORM).enemyAbility(AbilityId.FORECAST);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const castform = game.scene.getEnemyPokemon()!;
const castform = game.field.getEnemyPokemon();
expect(castform.formIndex).toBe(SUNNY_FORM);
});
});

View File

@ -43,7 +43,7 @@ describe("Abilities - Good As Gold", () => {
game.override.enemyMoveset([MoveId.GROWL]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const player = game.scene.getPlayerPokemon()!;
const player = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);

View File

@ -40,7 +40,7 @@ describe("Abilities - Gorilla Tactics", () => {
it("boosts the Pokémon's Attack by 50%, but limits the Pokémon to using only one move", async () => {
await game.classicMode.startBattle([SpeciesId.GALAR_DARMANITAN]);
const darmanitan = game.scene.getPlayerPokemon()!;
const darmanitan = game.field.getPlayerPokemon();
const initialAtkStat = darmanitan.getStat(Stat.ATK);
game.move.select(MoveId.SPLASH);
@ -86,7 +86,7 @@ describe("Abilities - Gorilla Tactics", () => {
vi.spyOn(RandomMoveAttr.prototype, "getMoveOverride").mockReturnValue(MoveId.TACKLE);
await game.classicMode.startBattle([SpeciesId.GALAR_DARMANITAN]);
const darmanitan = game.scene.getPlayerPokemon()!;
const darmanitan = game.field.getPlayerPokemon();
game.move.select(MoveId.METRONOME);
await game.phaseInterceptor.to("TurnEndPhase");

View File

@ -52,7 +52,7 @@ describe("Abilities - Gulp Missile", () => {
it("changes to Gulping Form if HP is over half when Surf or Dive is used", async () => {
await game.classicMode.startBattle([SpeciesId.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
const cramorant = game.field.getPlayerPokemon();
game.move.select(MoveId.DIVE);
await game.toNextTurn();
@ -66,7 +66,7 @@ describe("Abilities - Gulp Missile", () => {
it("changes to Gorging Form if HP is under half when Surf or Dive is used", async () => {
await game.classicMode.startBattle([SpeciesId.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
const cramorant = game.field.getPlayerPokemon();
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.49);
expect(cramorant.getHpRatio()).toBe(0.49);
@ -80,7 +80,7 @@ describe("Abilities - Gulp Missile", () => {
it("changes to base form when switched out after Surf or Dive is used", async () => {
await game.classicMode.startBattle([SpeciesId.CRAMORANT, SpeciesId.MAGIKARP]);
const cramorant = game.scene.getPlayerPokemon()!;
const cramorant = game.field.getPlayerPokemon();
game.move.select(MoveId.SURF);
await game.toNextTurn();
@ -95,7 +95,7 @@ describe("Abilities - Gulp Missile", () => {
it("changes form during Dive's charge turn", async () => {
await game.classicMode.startBattle([SpeciesId.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
const cramorant = game.field.getPlayerPokemon();
game.move.select(MoveId.DIVE);
await game.phaseInterceptor.to("MoveEndPhase");
@ -108,7 +108,7 @@ describe("Abilities - Gulp Missile", () => {
game.override.enemyMoveset(MoveId.TACKLE);
await game.classicMode.startBattle([SpeciesId.CRAMORANT]);
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
vi.spyOn(enemy, "damageAndUpdate");
game.move.select(MoveId.SURF);
@ -121,7 +121,7 @@ describe("Abilities - Gulp Missile", () => {
game.override.enemyMoveset(MoveId.TAIL_WHIP);
await game.classicMode.startBattle([SpeciesId.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
const cramorant = game.field.getPlayerPokemon();
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.55);
game.move.select(MoveId.SURF);
@ -140,8 +140,8 @@ describe("Abilities - Gulp Missile", () => {
game.override.enemyMoveset(MoveId.TACKLE);
await game.classicMode.startBattle([SpeciesId.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const cramorant = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
vi.spyOn(enemy, "damageAndUpdate");
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.55);
@ -164,8 +164,8 @@ describe("Abilities - Gulp Missile", () => {
game.override.enemyMoveset(MoveId.TACKLE);
await game.classicMode.startBattle([SpeciesId.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const cramorant = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
vi.spyOn(enemy, "damageAndUpdate");
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.45);
@ -188,7 +188,7 @@ describe("Abilities - Gulp Missile", () => {
game.override.enemyMoveset(MoveId.SURF);
await game.classicMode.startBattle([SpeciesId.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
const cramorant = game.field.getPlayerPokemon();
game.move.select(MoveId.DIVE);
await game.phaseInterceptor.to("BerryPhase", false);
@ -201,8 +201,8 @@ describe("Abilities - Gulp Missile", () => {
game.override.enemyMoveset(MoveId.TACKLE).enemyAbility(AbilityId.MAGIC_GUARD);
await game.classicMode.startBattle([SpeciesId.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const cramorant = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.55);
@ -225,7 +225,7 @@ describe("Abilities - Gulp Missile", () => {
game.override.enemyMoveset(MoveId.THUNDERBOLT);
await game.classicMode.startBattle([SpeciesId.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
const cramorant = game.field.getPlayerPokemon();
game.move.select(MoveId.SURF);
await game.phaseInterceptor.to("FaintPhase");
@ -233,7 +233,7 @@ describe("Abilities - Gulp Missile", () => {
expect(cramorant.hp).toBe(0);
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeUndefined();
expect(cramorant.formIndex).toBe(NORMAL_FORM);
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.DEF)).toBe(-1);
expect(game.field.getEnemyPokemon().getStatStage(Stat.DEF)).toBe(-1);
});
it("doesn't trigger if user is behind a substitute", async () => {
@ -244,21 +244,21 @@ describe("Abilities - Gulp Missile", () => {
await game.move.selectEnemyMove(MoveId.SPLASH);
await game.toNextTurn();
expect(game.scene.getPlayerPokemon()!.formIndex).toBe(GULPING_FORM);
expect(game.field.getPlayerPokemon().formIndex).toBe(GULPING_FORM);
game.move.select(MoveId.SUBSTITUTE);
await game.move.selectEnemyMove(MoveId.POWER_TRIP);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.toNextTurn();
expect(game.scene.getPlayerPokemon()!.formIndex).toBe(GULPING_FORM);
expect(game.field.getPlayerPokemon().formIndex).toBe(GULPING_FORM);
});
it("cannot be suppressed", async () => {
game.override.enemyMoveset(MoveId.GASTRO_ACID);
await game.classicMode.startBattle([SpeciesId.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
const cramorant = game.field.getPlayerPokemon();
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.55);
game.move.select(MoveId.SURF);
@ -278,7 +278,7 @@ describe("Abilities - Gulp Missile", () => {
game.override.enemyMoveset(MoveId.SKILL_SWAP);
await game.classicMode.startBattle([SpeciesId.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;
const cramorant = game.field.getPlayerPokemon();
vi.spyOn(cramorant, "getHpRatio").mockReturnValue(0.55);
game.move.select(MoveId.SURF);
@ -301,6 +301,6 @@ describe("Abilities - Gulp Missile", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("TurnStartPhase");
expect(game.scene.getEnemyPokemon()?.hasAbility(AbilityId.GULP_MISSILE)).toBe(false);
expect(game.field.getEnemyPokemon().hasAbility(AbilityId.GULP_MISSILE)).toBe(false);
});
});

View File

@ -19,7 +19,7 @@ describe("Abilities - Harvest", () => {
let game: GameManager;
const getPlayerBerries = () =>
game.scene.getModifiers(BerryModifier, true).filter(b => b.pokemonId === game.scene.getPlayerPokemon()?.id);
game.scene.getModifiers(BerryModifier, true).filter(b => b.pokemonId === game.field.getPlayerPokemon().id);
/** Check whether the player's Modifiers contains the specified berries and nothing else. */
function expectBerriesContaining(...berries: ModifierOverride[]): void {
@ -64,11 +64,11 @@ describe("Abilities - Harvest", () => {
await game.move.selectEnemyMove(MoveId.NUZZLE);
await game.phaseInterceptor.to("BerryPhase");
expect(getPlayerBerries()).toHaveLength(0);
expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toHaveLength(1);
expect(game.field.getPlayerPokemon().battleData.berriesEaten).toHaveLength(1);
await game.phaseInterceptor.to("TurnEndPhase");
expectBerriesContaining({ name: "BERRY", type: BerryType.LUM, count: 1 });
expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]);
expect(game.field.getPlayerPokemon().battleData.berriesEaten).toEqual([]);
});
it("tracks berries eaten while disabled/not present", async () => {
@ -82,7 +82,7 @@ describe("Abilities - Harvest", () => {
.enemyAbility(AbilityId.NEUTRALIZING_GAS);
await game.classicMode.startBattle([SpeciesId.MILOTIC]);
const milotic = game.scene.getPlayerPokemon()!;
const milotic = game.field.getPlayerPokemon();
expect(milotic).toBeDefined();
// Chug a few berries without harvest (should get tracked)
@ -122,7 +122,7 @@ describe("Abilities - Harvest", () => {
.ability(AbilityId.BALL_FETCH); // don't actually need harvest for this test
await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const regieleki = game.scene.getPlayerPokemon()!;
const regieleki = game.field.getPlayerPokemon();
regieleki.hp = 1;
game.move.select(MoveId.SPLASH);
@ -150,7 +150,7 @@ describe("Abilities - Harvest", () => {
.enemyAbility(AbilityId.COMPOUND_EYES);
await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const regieleki = game.scene.getPlayerPokemon()!;
const regieleki = game.field.getPlayerPokemon();
regieleki.hp = regieleki.getMaxHp() / 4 + 1;
game.move.select(MoveId.SPLASH);
@ -161,7 +161,7 @@ describe("Abilities - Harvest", () => {
// ate 1 berry and recovered it
expect(regieleki.battleData.berriesEaten).toEqual([]);
expect(getPlayerBerries()).toEqual([expect.objectContaining({ berryType: BerryType.PETAYA, stackCount: 1 })]);
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.SPATK)).toBe(1);
expect(game.field.getPlayerPokemon().getStatStage(Stat.SPATK)).toBe(1);
// heal up so harvest doesn't proc and kill enemy
game.move.select(MoveId.EARTHQUAKE);
@ -170,13 +170,13 @@ describe("Abilities - Harvest", () => {
await game.toNextWave();
expectBerriesContaining({ name: "BERRY", count: 1, type: BerryType.PETAYA });
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.SPATK)).toBe(1);
expect(game.field.getPlayerPokemon().getStatStage(Stat.SPATK)).toBe(1);
await game.reload.reloadSession();
expect(regieleki.battleData.berriesEaten).toEqual([]);
expectBerriesContaining({ name: "BERRY", count: 1, type: BerryType.PETAYA });
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.SPATK)).toBe(1);
expect(game.field.getPlayerPokemon().getStatStage(Stat.SPATK)).toBe(1);
});
it("cannot restore capped berries", async () => {
@ -187,7 +187,7 @@ describe("Abilities - Harvest", () => {
game.override.startingHeldItems(initBerries);
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
const feebas = game.scene.getPlayerPokemon()!;
const feebas = game.field.getPlayerPokemon();
feebas.battleData.berriesEaten = [BerryType.LUM, BerryType.STARF];
game.move.select(MoveId.SPLASH);
@ -215,7 +215,7 @@ describe("Abilities - Harvest", () => {
game.override.startingHeldItems(initBerries);
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
const player = game.scene.getPlayerPokemon()!;
const player = game.field.getPlayerPokemon();
player.battleData.berriesEaten = [BerryType.LUM, BerryType.STARF];
game.move.select(MoveId.SPLASH);
@ -234,7 +234,7 @@ describe("Abilities - Harvest", () => {
await game.move.selectEnemyMove(MoveId.INCINERATE);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]);
expect(game.field.getPlayerPokemon().battleData.berriesEaten).toEqual([]);
});
it("cannot restore knocked off berries", async () => {
@ -245,7 +245,7 @@ describe("Abilities - Harvest", () => {
await game.move.selectEnemyMove(MoveId.KNOCK_OFF);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]);
expect(game.field.getPlayerPokemon().battleData.berriesEaten).toEqual([]);
});
it("can restore berries eaten by Teatime", async () => {
@ -257,7 +257,7 @@ describe("Abilities - Harvest", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]);
expect(game.field.getPlayerPokemon().battleData.berriesEaten).toEqual([]);
expectBerriesContaining(...initBerries);
});
@ -271,8 +271,8 @@ describe("Abilities - Harvest", () => {
await game.phaseInterceptor.to("BerryPhase");
// pluck triggers harvest for neither side
expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]);
expect(game.scene.getEnemyPokemon()?.battleData.berriesEaten).toEqual([]);
expect(game.field.getPlayerPokemon().battleData.berriesEaten).toEqual([]);
expect(game.field.getEnemyPokemon().battleData.berriesEaten).toEqual([]);
expect(getPlayerBerries()).toEqual([]);
});
@ -293,7 +293,7 @@ describe("Abilities - Harvest", () => {
await game.phaseInterceptor.to("TurnEndPhase", false);
// won't trigger harvest since we didn't lose the berry (it just doesn't ever add it to the array)
expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toEqual([]);
expect(game.field.getPlayerPokemon().battleData.berriesEaten).toEqual([]);
expectBerriesContaining(...initBerries);
});
@ -303,7 +303,7 @@ describe("Abilities - Harvest", () => {
await game.classicMode.startBattle([SpeciesId.MEOWSCARADA]);
// pre damage
const player = game.scene.getPlayerPokemon()!;
const player = game.field.getPlayerPokemon();
player.hp = 1;
// steal a sitrus and immediately consume it
@ -326,7 +326,7 @@ describe("Abilities - Harvest", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toBe([]);
expect(game.field.getPlayerPokemon().battleData.berriesEaten).toBe([]);
expect(getPlayerBerries()).toEqual([]);
});
@ -339,7 +339,7 @@ describe("Abilities - Harvest", () => {
game.move.select(MoveId.NATURAL_GIFT);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerPokemon()?.battleData.berriesEaten).toHaveLength(0);
expect(game.field.getPlayerPokemon().battleData.berriesEaten).toHaveLength(0);
expectBerriesContaining(...initBerries);
});
});

View File

@ -46,7 +46,7 @@ describe("Abilities - Healer", () => {
game.override.moveset([MoveId.SPLASH, MoveId.LUNAR_DANCE]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MAGIKARP]);
const user = game.scene.getPlayerPokemon()!;
const user = game.field.getPlayerPokemon();
// Only want one magikarp to have the ability
vi.spyOn(user, "getAbility").mockReturnValue(allAbilities[AbilityId.HEALER]);
game.move.select(MoveId.SPLASH);

View File

@ -40,7 +40,7 @@ describe("Abilities - Heatproof", () => {
it("reduces Fire type damage by half", async () => {
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
const initialHP = 1000;
enemy.hp = initialHP;
@ -63,7 +63,7 @@ describe("Abilities - Heatproof", () => {
game.override.enemyStatusEffect(StatusEffect.BURN).enemySpecies(SpeciesId.ABRA);
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
game.move.select(MoveId.SPLASH);
await game.toNextTurn();

View File

@ -62,7 +62,7 @@ describe("Abilities - Honey Gather", () => {
game.scene.money = 1000;
// something weird is going on with the test framework, so this is required to prevent a crash
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
vi.spyOn(enemy, "scene", "get").mockReturnValue(game.scene);
// Expects next wave so run must succeed
vi.spyOn(Overrides, "RUN_SUCCESS_OVERRIDE", "get").mockReturnValue(true);

View File

@ -35,7 +35,7 @@ describe("Abilities - Hustle", () => {
it("increases the user's Attack stat by 50%", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const pikachu = game.scene.getPlayerPokemon()!;
const pikachu = game.field.getPlayerPokemon();
const atk = pikachu.stats[Stat.ATK];
vi.spyOn(pikachu, "getEffectiveStat");
@ -49,7 +49,7 @@ describe("Abilities - Hustle", () => {
it("lowers the accuracy of the user's physical moves by 20%", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const pikachu = game.scene.getPlayerPokemon()!;
const pikachu = game.field.getPlayerPokemon();
vi.spyOn(pikachu, "getAccuracyMultiplier");
@ -61,7 +61,7 @@ describe("Abilities - Hustle", () => {
it("does not affect non-physical moves", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const pikachu = game.scene.getPlayerPokemon()!;
const pikachu = game.field.getPlayerPokemon();
const spatk = pikachu.stats[Stat.SPATK];
vi.spyOn(pikachu, "getEffectiveStat");
@ -78,8 +78,8 @@ describe("Abilities - Hustle", () => {
game.override.startingLevel(100).enemyLevel(30);
await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const pikachu = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const pikachu = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
vi.spyOn(pikachu, "getAccuracyMultiplier");
vi.spyOn(allMoves[MoveId.FISSURE], "calculateBattleAccuracy");

View File

@ -36,7 +36,7 @@ describe("Abilities - Hyper Cutter", () => {
it("only prevents ATK drops", async () => {
await game.classicMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
game.move.select(MoveId.OCTOLOCK);
await game.toNextTurn();

View File

@ -44,7 +44,7 @@ describe("Abilities - Ice Face", () => {
await game.phaseInterceptor.to(MoveEndPhase);
const eiscue = game.scene.getEnemyPokemon()!;
const eiscue = game.field.getEnemyPokemon();
expect(eiscue.isFullHp()).toBe(true);
expect(eiscue.formIndex).toBe(noiceForm);
@ -57,7 +57,7 @@ describe("Abilities - Ice Face", () => {
game.move.select(MoveId.SURGING_STRIKES);
const eiscue = game.scene.getEnemyPokemon()!;
const eiscue = game.field.getEnemyPokemon();
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined();
// First hit
@ -85,7 +85,7 @@ describe("Abilities - Ice Face", () => {
await game.phaseInterceptor.to(MoveEndPhase);
const eiscue = game.scene.getEnemyPokemon()!;
const eiscue = game.field.getEnemyPokemon();
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined);
expect(eiscue.formIndex).toBe(icefaceForm);
@ -99,7 +99,7 @@ describe("Abilities - Ice Face", () => {
await game.phaseInterceptor.to(MoveEndPhase);
const eiscue = game.scene.getEnemyPokemon()!;
const eiscue = game.field.getEnemyPokemon();
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined);
expect(eiscue.formIndex).toBe(icefaceForm);
@ -114,7 +114,7 @@ describe("Abilities - Ice Face", () => {
await game.phaseInterceptor.to(MoveEndPhase);
const eiscue = game.scene.getEnemyPokemon()!;
const eiscue = game.field.getEnemyPokemon();
expect(eiscue.isFullHp()).toBe(true);
expect(eiscue.formIndex).toBe(noiceForm);
@ -134,7 +134,7 @@ describe("Abilities - Ice Face", () => {
game.move.select(MoveId.SNOWSCAPE);
await game.phaseInterceptor.to(TurnEndPhase);
let eiscue = game.scene.getPlayerPokemon()!;
let eiscue = game.field.getPlayerPokemon();
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined();
expect(eiscue.formIndex).toBe(noiceForm);
@ -146,7 +146,7 @@ describe("Abilities - Ice Face", () => {
game.doSwitchPokemon(1);
await game.phaseInterceptor.to(QuietFormChangePhase);
eiscue = game.scene.getPlayerPokemon()!;
eiscue = game.field.getPlayerPokemon();
expect(eiscue.formIndex).toBe(icefaceForm);
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined);
@ -158,7 +158,7 @@ describe("Abilities - Ice Face", () => {
await game.classicMode.startBattle([SpeciesId.EISCUE]);
game.move.select(MoveId.HAIL);
const eiscue = game.scene.getPlayerPokemon()!;
const eiscue = game.field.getPlayerPokemon();
await game.phaseInterceptor.to(QuietFormChangePhase);
@ -179,7 +179,7 @@ describe("Abilities - Ice Face", () => {
game.move.select(MoveId.ICE_BEAM);
await game.phaseInterceptor.to(TurnEndPhase);
let eiscue = game.scene.getPlayerPokemon()!;
let eiscue = game.field.getPlayerPokemon();
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined();
expect(eiscue.formIndex).toBe(noiceForm);
@ -206,7 +206,7 @@ describe("Abilities - Ice Face", () => {
await game.classicMode.startBattle([SpeciesId.EISCUE]);
const eiscue = game.scene.getPlayerPokemon()!;
const eiscue = game.field.getPlayerPokemon();
expect(eiscue.formIndex).toBe(noiceForm);
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined();
@ -229,7 +229,7 @@ describe("Abilities - Ice Face", () => {
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.toNextTurn();
expect(game.scene.getEnemyPokemon()!.formIndex).toBe(icefaceForm);
expect(game.field.getEnemyPokemon().formIndex).toBe(icefaceForm);
});
it("cannot be suppressed", async () => {
@ -241,7 +241,7 @@ describe("Abilities - Ice Face", () => {
await game.phaseInterceptor.to(TurnEndPhase);
const eiscue = game.scene.getEnemyPokemon()!;
const eiscue = game.field.getEnemyPokemon();
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined);
expect(eiscue.formIndex).toBe(icefaceForm);
@ -257,7 +257,7 @@ describe("Abilities - Ice Face", () => {
await game.phaseInterceptor.to(TurnEndPhase);
const eiscue = game.scene.getEnemyPokemon()!;
const eiscue = game.field.getEnemyPokemon();
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined();
expect(eiscue.formIndex).toBe(icefaceForm);
@ -269,10 +269,10 @@ describe("Abilities - Ice Face", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const eiscue = game.scene.getEnemyPokemon()!;
const eiscue = game.field.getEnemyPokemon();
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined();
expect(eiscue.formIndex).toBe(icefaceForm);
expect(game.scene.getPlayerPokemon()!.hasAbility(AbilityId.TRACE)).toBe(true);
expect(game.field.getPlayerPokemon().hasAbility(AbilityId.TRACE)).toBe(true);
});
});

View File

@ -33,7 +33,7 @@ describe("Abilities - Illuminate", () => {
await game.classicMode.startBattle();
const player = game.scene.getPlayerPokemon()!;
const player = game.field.getPlayerPokemon();
expect(player.getStatStage(Stat.ACC)).toBe(0);

View File

@ -35,8 +35,8 @@ describe("Abilities - Illusion", () => {
it("creates illusion at the start", async () => {
await game.classicMode.startBattle([SpeciesId.ZOROARK, SpeciesId.FEEBAS]);
const zoroark = game.scene.getPlayerPokemon()!;
const zorua = game.scene.getEnemyPokemon()!;
const zoroark = game.field.getPlayerPokemon();
const zorua = game.field.getEnemyPokemon();
expect(!!zoroark.summonData.illusion).equals(true);
expect(!!zorua.summonData.illusion).equals(true);
@ -48,7 +48,7 @@ describe("Abilities - Illusion", () => {
await game.phaseInterceptor.to("TurnEndPhase");
const zorua = game.scene.getEnemyPokemon()!;
const zorua = game.field.getEnemyPokemon();
expect(!!zorua.summonData.illusion).equals(false);
expect(zorua.name).equals("Zorua");
@ -60,7 +60,7 @@ describe("Abilities - Illusion", () => {
await game.phaseInterceptor.to("TurnEndPhase");
const zorua = game.scene.getEnemyPokemon()!;
const zorua = game.field.getEnemyPokemon();
expect(!!zorua.summonData.illusion).equals(false);
});
@ -69,7 +69,7 @@ describe("Abilities - Illusion", () => {
game.override.enemyAbility(AbilityId.NEUTRALIZING_GAS);
await game.classicMode.startBattle([SpeciesId.KOFFING]);
const zorua = game.scene.getEnemyPokemon()!;
const zorua = game.field.getEnemyPokemon();
expect(!!zorua.summonData.illusion).equals(false);
});
@ -85,15 +85,15 @@ describe("Abilities - Illusion", () => {
game.doSwitchPokemon(1);
await game.toNextTurn();
expect(game.scene.getPlayerPokemon()!.summonData.illusion).toBeFalsy();
expect(game.field.getPlayerPokemon().summonData.illusion).toBeFalsy();
});
it("causes enemy AI to consider the illusion's type instead of the actual type when considering move effectiveness", async () => {
game.override.enemyMoveset([MoveId.FLAMETHROWER, MoveId.PSYCHIC, MoveId.TACKLE]);
await game.classicMode.startBattle([SpeciesId.ZOROARK, SpeciesId.FEEBAS]);
const enemy = game.scene.getEnemyPokemon()!;
const zoroark = game.scene.getPlayerPokemon()!;
const enemy = game.field.getEnemyPokemon();
const zoroark = game.field.getPlayerPokemon();
const flameThrower = enemy.getMoveset()[0]!.getMove();
const psychic = enemy.getMoveset()[1]!.getMove();
@ -125,7 +125,7 @@ describe("Abilities - Illusion", () => {
await game.move.forceEnemyMove(MoveId.WILL_O_WISP);
await game.toEndOfTurn();
const zoroark = game.scene.getPlayerPokemon()!;
const zoroark = game.field.getPlayerPokemon();
expect(!!zoroark.summonData.illusion).equals(true);
});
@ -143,7 +143,7 @@ describe("Abilities - Illusion", () => {
await game.phaseInterceptor.to("TurnEndPhase");
const zoroark = game.scene.getPlayerPokemon()!;
const zoroark = game.field.getPlayerPokemon();
expect(zoroark.summonData.illusion?.name).equals("Axew");
expect(zoroark.getNameToRender(true)).equals("axew nickname");
@ -155,7 +155,7 @@ describe("Abilities - Illusion", () => {
it("breaks when suppressed", async () => {
game.override.moveset(MoveId.GASTRO_ACID);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const zorua = game.scene.getEnemyPokemon()!;
const zorua = game.field.getEnemyPokemon();
expect(!!zorua.summonData?.illusion).toBe(true);

View File

@ -58,8 +58,8 @@ describe("Abilities - Infiltrator", () => {
])("should bypass the target's $effectName", async ({ tagType, move }) => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const player = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
const preScreenDmg = enemy.getAttackDamage({ source: player, move: allMoves[move] }).damage;
@ -74,8 +74,8 @@ describe("Abilities - Infiltrator", () => {
it("should bypass the target's Safeguard", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const player = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
game.scene.arena.addTag(ArenaTagType.SAFEGUARD, 1, MoveId.NONE, enemy.id, ArenaTagSide.ENEMY, true);
@ -90,8 +90,8 @@ describe("Abilities - Infiltrator", () => {
it.todo("should bypass the target's Mist", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const player = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
game.scene.arena.addTag(ArenaTagType.MIST, 1, MoveId.NONE, enemy.id, ArenaTagSide.ENEMY, true);
@ -105,8 +105,8 @@ describe("Abilities - Infiltrator", () => {
it("should bypass the target's Substitute", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const player = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
enemy.addTag(BattlerTagType.SUBSTITUTE, 1, MoveId.NONE, enemy.id);

View File

@ -32,8 +32,8 @@ describe("Abilities - Intrepid Sword", () => {
it("should raise ATK stat stage by 1 on entry", async () => {
await game.classicMode.runToSummon([SpeciesId.ZACIAN]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
await game.phaseInterceptor.to(CommandPhase, false);

View File

@ -42,7 +42,7 @@ describe("Abilities - Magic Bounce", () => {
game.move.use(MoveId.GROWL);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(-1);
expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(-1);
});
it("should not bounce moves while the target is in the semi-invulnerable state", async () => {
@ -53,7 +53,7 @@ describe("Abilities - Magic Bounce", () => {
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(0);
expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(0);
});
it("should individually bounce back multi-target moves", async () => {
@ -70,12 +70,12 @@ describe("Abilities - Magic Bounce", () => {
it("should still bounce back a move that would otherwise fail", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
game.scene.getEnemyPokemon()?.setStatStage(Stat.ATK, -6);
game.field.getEnemyPokemon().setStatStage(Stat.ATK, -6);
game.move.use(MoveId.GROWL);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(-1);
expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(-1);
});
it("should not bounce back a move that was just bounced", async () => {
@ -85,7 +85,7 @@ describe("Abilities - Magic Bounce", () => {
game.move.select(MoveId.GROWL);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(-1);
expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(-1);
});
it("should receive the stat change after reflecting a move back to a mirror armor user", async () => {
@ -95,7 +95,7 @@ describe("Abilities - Magic Bounce", () => {
game.move.select(MoveId.GROWL);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(-1);
expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1);
});
it("should not bounce back a move from a mold breaker user", async () => {
@ -105,7 +105,7 @@ describe("Abilities - Magic Bounce", () => {
game.move.use(MoveId.GROWL);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(-1);
expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1);
});
it("should bounce back a spread status move against both pokemon", async () => {
@ -156,7 +156,7 @@ describe("Abilities - Magic Bounce", () => {
game.move.select(MoveId.CURSE);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()!.getTag(BattlerTagType.CURSED)).toBeDefined();
expect(game.field.getEnemyPokemon().getTag(BattlerTagType.CURSED)).toBeDefined();
});
// TODO: enable when Magic Bounce is fixed to properly reset the hit count
@ -164,8 +164,8 @@ describe("Abilities - Magic Bounce", () => {
game.override.moveset([MoveId.SPLASH, MoveId.GROWL, MoveId.ENCORE]).enemyMoveset([MoveId.TACKLE, MoveId.GROWL]);
// game.override.ability(AbilityId.MOLD_BREAKER);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
// Give the player MOLD_BREAKER for this turn to bypass Magic Bounce.
const playerAbilitySpy = game.field.mockAbility(playerPokemon, AbilityId.MOLD_BREAKER);
@ -194,8 +194,8 @@ describe("Abilities - Magic Bounce", () => {
.enemyAbility(AbilityId.MAGIC_BOUNCE);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
// turn 1
game.move.select(MoveId.GROWL);
@ -237,7 +237,7 @@ describe("Abilities - Magic Bounce", () => {
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const stomping_tantrum = allMoves[MoveId.STOMPING_TANTRUM];
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
vi.spyOn(stomping_tantrum, "calculateBattlePower");
// Spore gets reflected back onto us
@ -260,35 +260,35 @@ describe("Abilities - Magic Bounce", () => {
// Turn 1 - thunder wave immunity test
game.move.select(MoveId.THUNDER_WAVE);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.status).toBeUndefined();
expect(game.field.getPlayerPokemon().status).toBeUndefined();
// Turn 2 - soundproof immunity test
game.move.select(MoveId.GROWL);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(0);
expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(0);
});
it("should bounce back a move before the accuracy check", async () => {
game.override.moveset([MoveId.SPORE]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const attacker = game.scene.getPlayerPokemon()!;
const attacker = game.field.getPlayerPokemon();
vi.spyOn(attacker, "getAccuracyMultiplier").mockReturnValue(0.0);
game.move.select(MoveId.SPORE);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.status?.effect).toBe(StatusEffect.SLEEP);
expect(game.field.getPlayerPokemon().status?.effect).toBe(StatusEffect.SLEEP);
});
it("should take the accuracy of the magic bounce user into account", async () => {
game.override.moveset([MoveId.SPORE]);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const opponent = game.scene.getEnemyPokemon()!;
const opponent = game.field.getEnemyPokemon();
vi.spyOn(opponent, "getAccuracyMultiplier").mockReturnValue(0);
game.move.select(MoveId.SPORE);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.status).toBeUndefined();
expect(game.field.getPlayerPokemon().status).toBeUndefined();
});
it("should always apply the leftmost available target's magic bounce when bouncing moves like sticky webs in doubles", async () => {
@ -332,14 +332,14 @@ describe("Abilities - Magic Bounce", () => {
await game.move.selectEnemyMove(MoveId.FLY);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.TOXIC);
expect(game.scene.getPlayerPokemon()!.status).toBeUndefined();
expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.TOXIC);
expect(game.field.getPlayerPokemon().status).toBeUndefined();
game.override.ability(AbilityId.NO_GUARD);
game.move.select(MoveId.CHARM);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(-2);
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(0);
expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-2);
expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(0);
});
});

View File

@ -47,44 +47,44 @@ describe("Abilities - Mimicry", () => {
});
it("Pokemon should revert back to its original, root type once terrain ends", async () => {
game.override
.moveset([MoveId.SPLASH, MoveId.TRANSFORM])
.enemyAbility(AbilityId.MIMICRY)
.enemyMoveset([MoveId.SPLASH, MoveId.PSYCHIC_TERRAIN]);
game.override.enemyAbility(AbilityId.MIMICRY);
await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const playerPokemon = game.scene.getPlayerPokemon();
game.move.select(MoveId.TRANSFORM);
await game.move.selectEnemyMove(MoveId.PSYCHIC_TERRAIN);
const playerPokemon = game.field.getPlayerPokemon();
game.move.use(MoveId.SKILL_SWAP);
await game.move.forceEnemyMove(MoveId.PSYCHIC_TERRAIN);
await game.toNextTurn();
expect(playerPokemon?.getTypes().includes(PokemonType.PSYCHIC)).toBe(true);
expect(playerPokemon.getTypes()).toEqual([PokemonType.PSYCHIC]);
if (game.scene.arena.terrain) {
game.scene.arena.terrain.turnsLeft = 1;
}
game.move.select(MoveId.SPLASH);
await game.move.selectEnemyMove(MoveId.SPLASH);
game.move.use(MoveId.SPLASH);
await game.move.forceEnemyMove(MoveId.SPLASH);
await game.toNextTurn();
expect(playerPokemon?.getTypes().includes(PokemonType.ELECTRIC)).toBe(true);
expect(playerPokemon.getTypes()).toEqual([PokemonType.ELECTRIC]);
});
it("If the Pokemon is under the effect of a type-adding move and an equivalent terrain activates, the move's effect disappears", async () => {
game.override.enemyMoveset([MoveId.FORESTS_CURSE, MoveId.GRASSY_TERRAIN]);
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
const playerPokemon = game.scene.getPlayerPokemon();
const playerPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
await game.move.selectEnemyMove(MoveId.FORESTS_CURSE);
await game.toNextTurn();
expect(playerPokemon?.summonData.addedType).toBe(PokemonType.GRASS);
expect(playerPokemon.summonData.addedType).toBe(PokemonType.GRASS);
game.move.select(MoveId.SPLASH);
await game.move.selectEnemyMove(MoveId.GRASSY_TERRAIN);
await game.phaseInterceptor.to("TurnEndPhase");
expect(playerPokemon?.summonData.addedType).toBeNull();
expect(playerPokemon?.getTypes().includes(PokemonType.GRASS)).toBe(true);
expect(playerPokemon.summonData.addedType).toBeNull();
expect(playerPokemon.getTypes()).toEqual([PokemonType.GRASS]);
});
});

View File

@ -40,8 +40,8 @@ describe("Ability - Mirror Armor", () => {
game.override.ability(AbilityId.MIRROR_ARMOR).enemyAbility(AbilityId.INTIMIDATE);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const userPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const userPokemon = game.field.getPlayerPokemon();
// Enemy has intimidate, enemy should lose -1 atk
game.move.select(MoveId.SPLASH);
@ -56,8 +56,8 @@ describe("Ability - Mirror Armor", () => {
game.override.enemyAbility(AbilityId.MIRROR_ARMOR).ability(AbilityId.INTIMIDATE);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const userPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const userPokemon = game.field.getPlayerPokemon();
// Enemy has intimidate, enemy should lose -1 atk
game.move.select(MoveId.SPLASH);
@ -112,8 +112,8 @@ describe("Ability - Mirror Armor", () => {
game.override.ability(AbilityId.MIRROR_ARMOR).enemyAbility(AbilityId.INTIMIDATE);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const userPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const userPokemon = game.field.getPlayerPokemon();
// Enemy has intimidate and uses tickle, enemy receives -2 atk and -1 defense
game.move.select(MoveId.SPLASH);
@ -153,8 +153,8 @@ describe("Ability - Mirror Armor", () => {
game.override.enemyAbility(AbilityId.MIRROR_ARMOR).ability(AbilityId.INTIMIDATE);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const userPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const userPokemon = game.field.getPlayerPokemon();
// Enemy has intimidate and uses tickle, enemy receives -2 atk and -1 defense
game.move.select(MoveId.TICKLE);
@ -171,8 +171,8 @@ describe("Ability - Mirror Armor", () => {
game.override.enemyAbility(AbilityId.WHITE_SMOKE).ability(AbilityId.MIRROR_ARMOR);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const userPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const userPokemon = game.field.getPlayerPokemon();
// Enemy has intimidate and uses tickle, enemy has white smoke, no one loses stats
game.move.select(MoveId.SPLASH);
@ -189,8 +189,8 @@ describe("Ability - Mirror Armor", () => {
game.override.ability(AbilityId.WHITE_SMOKE).enemyAbility(AbilityId.MIRROR_ARMOR);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const userPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const userPokemon = game.field.getPlayerPokemon();
// Enemy has intimidate and uses tickle, enemy has white smoke, no one loses stats
game.move.select(MoveId.TICKLE);
@ -207,8 +207,8 @@ describe("Ability - Mirror Armor", () => {
game.override.ability(AbilityId.MIRROR_ARMOR);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const userPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const userPokemon = game.field.getPlayerPokemon();
// Enemy uses octolock, player loses stats at end of turn
game.move.select(MoveId.SPLASH);
@ -225,8 +225,8 @@ describe("Ability - Mirror Armor", () => {
game.override.enemyAbility(AbilityId.MIRROR_ARMOR);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const userPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const userPokemon = game.field.getPlayerPokemon();
// Player uses octolock, enemy loses stats at end of turn
game.move.select(MoveId.OCTOLOCK);
@ -243,8 +243,8 @@ describe("Ability - Mirror Armor", () => {
game.override.enemyAbility(AbilityId.MIRROR_ARMOR).ability(AbilityId.MIRROR_ARMOR).ability(AbilityId.INTIMIDATE);
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const userPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const userPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
await game.move.selectEnemyMove(MoveId.SPLASH, BattlerIndex.PLAYER);
@ -258,8 +258,8 @@ describe("Ability - Mirror Armor", () => {
game.override.ability(AbilityId.MIRROR_ARMOR);
await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER, SpeciesId.SQUIRTLE]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const userPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const userPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
await game.move.selectEnemyMove(MoveId.STICKY_WEB, BattlerIndex.PLAYER);

View File

@ -35,7 +35,7 @@ describe("Abilities - Moody", () => {
it("should increase one stat stage by 2 and decrease a different stat stage by 1", async () => {
await game.classicMode.startBattle();
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
await game.toNextTurn();
@ -52,7 +52,7 @@ describe("Abilities - Moody", () => {
it("should only increase one stat stage by 2 if all stat stages are at -6", async () => {
await game.classicMode.startBattle();
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
// Set all stat stages to -6
vi.spyOn(playerPokemon.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(-6));
@ -70,7 +70,7 @@ describe("Abilities - Moody", () => {
it("should only decrease one stat stage by 1 stage if all stat stages are at 6", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
// Set all stat stages to 6
vi.spyOn(playerPokemon.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(6));

View File

@ -41,7 +41,7 @@ describe("Abilities - Moxie", () => {
const moveToUse = MoveId.AERIAL_ACE;
await game.classicMode.startBattle([SpeciesId.MIGHTYENA, SpeciesId.MIGHTYENA]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0);

View File

@ -37,7 +37,7 @@ describe("Abilities - Mummy", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(AbilityId.MUMMY);
expect(game.field.getEnemyPokemon().getAbility().id).toBe(AbilityId.MUMMY);
});
it("should not change the enemy's ability hit by a non-contact move", async () => {
@ -47,6 +47,6 @@ describe("Abilities - Mummy", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(AbilityId.BALL_FETCH);
expect(game.field.getEnemyPokemon().getAbility().id).toBe(AbilityId.BALL_FETCH);
});
});

View File

@ -42,12 +42,12 @@ describe("Abilities - Mycelium Might", () => {
* https://www.smogon.com/forums/threads/scarlet-violet-battle-mechanics-research.3709545/page-24
*/
it("will move last in its priority bracket and ignore protective abilities", async () => {
it("should move last in its priority bracket and ignore protective abilities", async () => {
await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const enemyPokemon = game.scene.getEnemyPokemon();
const playerIndex = game.scene.getPlayerPokemon()?.getBattlerIndex();
const enemyIndex = enemyPokemon?.getBattlerIndex();
const enemyPokemon = game.field.getEnemyPokemon();
const playerIndex = game.field.getPlayerPokemon().getBattlerIndex();
const enemyIndex = enemyPokemon.getBattlerIndex();
game.move.select(MoveId.BABY_DOLL_EYES);
@ -62,16 +62,16 @@ describe("Abilities - Mycelium Might", () => {
await game.phaseInterceptor.to(TurnEndPhase);
// Despite the opponent's ability (Clear Body), its ATK stat stage is still reduced.
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
});
it("will still go first if a status move that is in a higher priority bracket than the opponent's move is used", async () => {
it("should still go first if a status move that is in a higher priority bracket than the opponent's move is used", async () => {
game.override.enemyMoveset(MoveId.TACKLE);
await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const enemyPokemon = game.scene.getEnemyPokemon();
const playerIndex = game.scene.getPlayerPokemon()?.getBattlerIndex();
const enemyIndex = enemyPokemon?.getBattlerIndex();
const enemyPokemon = game.field.getEnemyPokemon();
const playerIndex = game.field.getPlayerPokemon().getBattlerIndex();
const enemyIndex = enemyPokemon.getBattlerIndex();
game.move.select(MoveId.BABY_DOLL_EYES);
@ -85,14 +85,14 @@ describe("Abilities - Mycelium Might", () => {
expect(commandOrder).toEqual([playerIndex, enemyIndex]);
await game.phaseInterceptor.to(TurnEndPhase);
// Despite the opponent's ability (Clear Body), its ATK stat stage is still reduced.
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
});
it("will not affect non-status moves", async () => {
it("should not affect non-status moves", async () => {
await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex();
const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex();
const playerIndex = game.field.getPlayerPokemon().getBattlerIndex();
const enemyIndex = game.field.getEnemyPokemon().getBattlerIndex();
game.move.select(MoveId.QUICK_ATTACK);

View File

@ -46,7 +46,7 @@ describe("Abilities - Neutralizing Gas", () => {
await game.phaseInterceptor.to("TurnEndPhase");
// Intimidate is suppressed, so the attack stat should not be lowered
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(0);
expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(0);
});
it("should allow the user's passive to activate", async () => {
@ -56,7 +56,7 @@ describe("Abilities - Neutralizing Gas", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(1);
expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(1);
});
it.todo("should activate before other abilities", async () => {
@ -68,7 +68,7 @@ describe("Abilities - Neutralizing Gas", () => {
await game.phaseInterceptor.to("TurnEndPhase");
// Intimidate is suppressed even when the user's speed is lower
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(0);
expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(0);
});
it("should activate other abilities when removed", async () => {
@ -79,15 +79,15 @@ describe("Abilities - Neutralizing Gas", () => {
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
const enemyPokemon = game.scene.getEnemyPokemon();
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(0);
expect(enemyPokemon?.getStatStage(Stat.DEF)).toBe(0);
const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0);
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
// Enemy removes user's ability, so both abilities are activated
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(1);
expect(enemyPokemon?.getStatStage(Stat.DEF)).toBe(1);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(1);
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(1);
});
it("should not activate the user's other ability when removed", async () => {
@ -95,13 +95,13 @@ describe("Abilities - Neutralizing Gas", () => {
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
// Neutralising gas user's passive is still active
const enemyPokemon = game.scene.getEnemyPokemon();
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1);
const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
// Intimidate did not reactivate after neutralizing gas was removed
expect(enemyPokemon?.getStatStage(Stat.ATK)).toBe(-1);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
});
it("should only deactivate when all setters are off the field", async () => {
@ -164,7 +164,7 @@ describe("Abilities - Neutralizing Gas", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined();
vi.spyOn(game.scene.getPlayerPokemon()!, "randBattleSeedInt").mockReturnValue(0);
vi.spyOn(game.field.getPlayerPokemon(), "randBattleSeedInt").mockReturnValue(0);
vi.spyOn(globalScene, "randBattleSeedInt").mockReturnValue(0);
const commandPhase = game.scene.phaseManager.getCurrentPhase() as CommandPhase;
@ -178,7 +178,7 @@ describe("Abilities - Neutralizing Gas", () => {
game.override.battleStyle("single").ability(AbilityId.NEUTRALIZING_GAS).enemyAbility(AbilityId.DELTA_STREAM);
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
const weatherChangeAttr = enemy.getAbilityAttrs("PostSummonWeatherChangeAbAttr", false)[0];
const weatherChangeSpy = vi.spyOn(weatherChangeAttr, "apply");
@ -186,7 +186,7 @@ describe("Abilities - Neutralizing Gas", () => {
game.move.select(MoveId.SPLASH);
await game.killPokemon(enemy);
await game.killPokemon(game.scene.getPlayerPokemon()!);
await game.killPokemon(game.field.getPlayerPokemon());
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined();
expect(weatherChangeSpy).not.toHaveBeenCalled();

View File

@ -58,10 +58,10 @@ describe.each([
it(`should change Normal-type attacks to ${tyName} type and boost their power`, async () => {
await game.classicMode.startBattle();
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const typeSpy = vi.spyOn(playerPokemon, "getMoveType");
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const enemySpy = vi.spyOn(enemyPokemon, "getMoveEffectiveness");
const powerSpy = vi.spyOn(allMoves[MoveId.TACKLE], "calculateBattlePower");
@ -103,10 +103,10 @@ describe.each([
await game.classicMode.startBattle();
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const tySpy = vi.spyOn(playerPokemon, "getMoveType");
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const enemyEffectivenessSpy = vi.spyOn(enemyPokemon, "getMoveEffectiveness");
enemyPokemon.hp = Math.floor(enemyPokemon.getMaxHp() * 0.8);
@ -137,7 +137,7 @@ describe.each([
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const tySpy = vi.spyOn(playerPokemon, "getMoveType");
game.move.select(move);
@ -149,10 +149,10 @@ describe.each([
it("should affect all hits of a Normal-type multi-hit move", async () => {
await game.classicMode.startBattle();
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const tySpy = vi.spyOn(playerPokemon, "getMoveType");
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
game.move.select(MoveId.FURY_SWIPES);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
@ -183,7 +183,7 @@ describe.each([
expect(boost, "power boost should be defined").toBeDefined();
const powerSpy = vi.spyOn(testMoveInstance, "calculateBattlePower");
const typeSpy = vi.spyOn(game.scene.getPlayerPokemon()!, "getMoveType");
const typeSpy = vi.spyOn(game.field.getPlayerPokemon(), "getMoveType");
game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.to("BerryPhase", false);
expect(typeSpy, "type was not changed").toHaveLastReturnedWith(ty);

View File

@ -39,14 +39,14 @@ describe("Abilities - Oblivious", () => {
.moveset(MoveId.SKILL_SWAP)
.enemyMoveset(MoveId.SPLASH);
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
const enemy = game.scene.getEnemyPokemon();
enemy?.addTag(BattlerTagType.TAUNT);
expect(enemy?.getTag(BattlerTagType.TAUNT)).toBeTruthy();
const enemy = game.field.getEnemyPokemon();
enemy.addTag(BattlerTagType.TAUNT);
expect(enemy.getTag(BattlerTagType.TAUNT)).toBeDefined();
game.move.select(MoveId.SKILL_SWAP);
await game.phaseInterceptor.to("BerryPhase");
expect(enemy?.getTag(BattlerTagType.TAUNT)).toBeFalsy();
expect(enemy.getTag(BattlerTagType.TAUNT)).toBeUndefined();
});
it("should remove infatuation when gained", async () => {
@ -56,14 +56,15 @@ describe("Abilities - Oblivious", () => {
.moveset(MoveId.SKILL_SWAP)
.enemyMoveset(MoveId.SPLASH);
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
const enemy = game.scene.getEnemyPokemon();
vi.spyOn(enemy!, "isOppositeGender").mockReturnValue(true);
enemy?.addTag(BattlerTagType.INFATUATED, 5, MoveId.JUDGMENT, game.scene.getPlayerPokemon()?.id); // sourceID needs to be defined
expect(enemy?.getTag(BattlerTagType.INFATUATED)).toBeTruthy();
const enemy = game.field.getEnemyPokemon();
vi.spyOn(enemy, "isOppositeGender").mockReturnValue(true);
enemy.addTag(BattlerTagType.INFATUATED, 5, MoveId.JUDGMENT, game.field.getPlayerPokemon().id); // sourceID needs to be defined
expect(enemy.getTag(BattlerTagType.INFATUATED)).toBeTruthy();
game.move.select(MoveId.SKILL_SWAP);
await game.phaseInterceptor.to("BerryPhase");
expect(enemy?.getTag(BattlerTagType.INFATUATED)).toBeFalsy();
expect(enemy.getTag(BattlerTagType.INFATUATED)).toBeFalsy();
});
});

View File

@ -42,8 +42,8 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
let enemyStartingHp = enemyPokemon.hp;
@ -66,7 +66,7 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.POWER_UP_PUNCH);
@ -81,7 +81,7 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
game.move.select(MoveId.BABY_DOLL_EYES);
@ -95,7 +95,7 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.DOUBLE_HIT);
await game.move.forceHit();
@ -110,7 +110,7 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.SELF_DESTRUCT);
@ -124,7 +124,7 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.ROLLOUT);
await game.move.forceHit();
@ -139,7 +139,7 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
game.move.select(MoveId.DRAGON_RAGE);
await game.phaseInterceptor.to("BerryPhase", false);
@ -152,8 +152,8 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.SHUCKLE]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
game.move.select(MoveId.COUNTER);
await game.phaseInterceptor.to("DamageAnimPhase");
@ -185,7 +185,7 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.EARTHQUAKE);
await game.phaseInterceptor.to("DamageAnimPhase", false);
@ -199,7 +199,7 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.MIND_BLOWN);
@ -218,8 +218,8 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.CHARIZARD]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
game.move.select(MoveId.BURN_UP);
@ -239,7 +239,7 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.HYPER_BEAM);
await game.move.forceHit();
@ -259,8 +259,8 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
game.move.select(MoveId.ANCHOR_SHOT);
await game.move.forceHit();
@ -283,8 +283,8 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
game.move.select(MoveId.SMACK_DOWN);
await game.move.forceHit();
@ -304,7 +304,7 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.BLASTOISE]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.U_TURN);
await game.move.forceHit();
@ -321,8 +321,8 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
game.move.select(MoveId.WAKE_UP_SLAP);
await game.move.forceHit();
@ -342,7 +342,7 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.TACKLE);
@ -356,7 +356,7 @@ describe("Abilities - Parental Bond", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
game.move.select(MoveId.WATER_GUN);
@ -369,7 +369,7 @@ describe("Abilities - Parental Bond", () => {
game.override.enemyLevel(1000).moveset(MoveId.FUTURE_SIGHT);
await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER, SpeciesId.SQUIRTLE]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
vi.spyOn(enemyPokemon, "damageAndUpdate");
game.move.select(MoveId.FUTURE_SIGHT);

View File

@ -34,55 +34,55 @@ describe("Abilities - Perish Song", () => {
it("should trigger when hit with damaging move", async () => {
await game.classicMode.startBattle();
const cursola = game.scene.getPlayerPokemon();
const magikarp = game.scene.getEnemyPokemon();
const cursola = game.field.getPlayerPokemon();
const magikarp = game.field.getEnemyPokemon();
game.move.select(MoveId.SPLASH);
await game.toNextTurn();
expect(cursola?.summonData.tags[0].turnCount).toBe(3);
expect(magikarp?.summonData.tags[0].turnCount).toBe(3);
expect(cursola.summonData.tags[0].turnCount).toBe(3);
expect(magikarp.summonData.tags[0].turnCount).toBe(3);
});
it("should trigger even when fainting", async () => {
game.override.enemyLevel(100).startingLevel(1);
await game.classicMode.startBattle([SpeciesId.CURSOLA, SpeciesId.FEEBAS]);
const magikarp = game.scene.getEnemyPokemon();
const magikarp = game.field.getEnemyPokemon();
game.move.select(MoveId.SPLASH);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
expect(magikarp?.summonData.tags[0].turnCount).toBe(3);
expect(magikarp.summonData.tags[0].turnCount).toBe(3);
});
it("should not activate if attacker already has perish song", async () => {
game.override.enemyMoveset([MoveId.PERISH_SONG, MoveId.AQUA_JET, MoveId.SPLASH]);
await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.CURSOLA]);
const feebas = game.scene.getPlayerPokemon();
const magikarp = game.scene.getEnemyPokemon();
const feebas = game.field.getPlayerPokemon();
const magikarp = game.field.getEnemyPokemon();
game.move.select(MoveId.SPLASH);
await game.move.selectEnemyMove(MoveId.PERISH_SONG);
await game.toNextTurn();
expect(feebas?.summonData.tags[0].turnCount).toBe(3);
expect(magikarp?.summonData.tags[0].turnCount).toBe(3);
expect(feebas.summonData.tags[0].turnCount).toBe(3);
expect(magikarp.summonData.tags[0].turnCount).toBe(3);
game.doSwitchPokemon(1);
await game.move.selectEnemyMove(MoveId.SPLASH);
await game.toNextTurn();
const cursola = game.scene.getPlayerPokemon();
expect(cursola?.summonData.tags.length).toBe(0);
expect(magikarp?.summonData.tags[0].turnCount).toBe(2);
const cursola = game.field.getPlayerPokemon();
expect(cursola.summonData.tags.length).toBe(0);
expect(magikarp.summonData.tags[0].turnCount).toBe(2);
game.move.select(MoveId.SPLASH);
await game.move.selectEnemyMove(MoveId.AQUA_JET);
await game.toNextTurn();
expect(cursola?.summonData.tags.length).toBe(0);
expect(magikarp?.summonData.tags[0].turnCount).toBe(1);
expect(cursola.summonData.tags.length).toBe(0);
expect(magikarp.summonData.tags[0].turnCount).toBe(1);
});
it("should activate if cursola already has perish song, but not reset its counter", async () => {
@ -91,22 +91,22 @@ describe("Abilities - Perish Song", () => {
.moveset([MoveId.WHIRLWIND, MoveId.SPLASH])
.startingWave(5);
await game.classicMode.startBattle([SpeciesId.CURSOLA]);
const cursola = game.scene.getPlayerPokemon();
const cursola = game.field.getPlayerPokemon();
game.move.select(MoveId.WHIRLWIND);
await game.move.selectEnemyMove(MoveId.PERISH_SONG);
await game.toNextTurn();
const magikarp = game.scene.getEnemyPokemon();
expect(cursola?.summonData.tags[0].turnCount).toBe(3);
expect(magikarp?.summonData.tags.length).toBe(0);
const magikarp = game.field.getEnemyPokemon();
expect(cursola.summonData.tags[0].turnCount).toBe(3);
expect(magikarp.summonData.tags.length).toBe(0);
game.move.select(MoveId.SPLASH);
await game.move.selectEnemyMove(MoveId.AQUA_JET);
await game.toNextTurn();
expect(cursola?.summonData.tags[0].turnCount).toBe(2);
expect(magikarp?.summonData.tags.length).toBe(1);
expect(magikarp?.summonData.tags[0].turnCount).toBe(3);
expect(cursola.summonData.tags[0].turnCount).toBe(2);
expect(magikarp.summonData.tags.length).toBe(1);
expect(magikarp.summonData.tags[0].turnCount).toBe(3);
});
});

View File

@ -42,10 +42,10 @@ describe("Abilities - Protosynthesis", () => {
.startingLevel(100)
.enemyLevel(100);
await game.classicMode.startBattle([SpeciesId.MEW]);
const mew = game.scene.getPlayerPokemon()!;
const mew = game.field.getPlayerPokemon();
// Nature of starting mon is randomized. We need to fix it to a neutral nature for the automated test.
mew.setNature(Nature.HARDY);
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
const def_before_boost = mew.getEffectiveStat(
Stat.DEF,
undefined,

View File

@ -43,8 +43,8 @@ describe("Abilities - Quick Draw", () => {
test("makes pokemon going first in its priority bracket", async () => {
await game.classicMode.startBattle();
const pokemon = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const pokemon = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
pokemon.hp = 1;
enemy.hp = 1;
@ -65,8 +65,8 @@ describe("Abilities - Quick Draw", () => {
async () => {
await game.classicMode.startBattle();
const pokemon = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const pokemon = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
pokemon.hp = 1;
enemy.hp = 1;
@ -85,8 +85,8 @@ describe("Abilities - Quick Draw", () => {
await game.classicMode.startBattle();
const pokemon = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const pokemon = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
pokemon.hp = 1;
enemy.hp = 1;

View File

@ -45,7 +45,7 @@ describe("Abilities - Sap Sipper", () => {
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const initialEnemyHp = enemyPokemon.hp;
game.move.select(moveToUse);
@ -63,7 +63,7 @@ describe("Abilities - Sap Sipper", () => {
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
game.move.select(moveToUse);
@ -86,7 +86,7 @@ describe("Abilities - Sap Sipper", () => {
expect(game.scene.arena.terrain).toBeDefined();
expect(game.scene.arena.terrain!.terrainType).toBe(TerrainType.GRASSY);
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(0);
expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(0);
});
it("activate once against multi-hit grass attacks", async () => {
@ -96,7 +96,7 @@ describe("Abilities - Sap Sipper", () => {
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const initialEnemyHp = enemyPokemon.hp;
game.move.select(moveToUse);
@ -114,7 +114,7 @@ describe("Abilities - Sap Sipper", () => {
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
game.move.select(moveToUse);
@ -140,7 +140,7 @@ describe("Abilities - Sap Sipper", () => {
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const initialEnemyHp = enemyPokemon.hp;
game.move.select(moveToUse);
@ -156,7 +156,7 @@ describe("Abilities - Sap Sipper", () => {
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
game.move.select(MoveId.LEAF_BLADE);
await game.phaseInterceptor.to("MoveEffectPhase");

View File

@ -38,8 +38,8 @@ describe("Abilities - Shield Dust", () => {
it("Shield Dust", async () => {
await game.classicMode.startBattle([SpeciesId.PIDGEOT]);
game.scene.getEnemyPokemon()!.stats[Stat.SPDEF] = 10000;
expect(game.scene.getPlayerPokemon()!.formIndex).toBe(0);
game.field.getEnemyPokemon().stats[Stat.SPDEF] = 10000;
expect(game.field.getPlayerPokemon().formIndex).toBe(0);
game.move.select(MoveId.AIR_SLASH);

View File

@ -66,7 +66,7 @@ describe("Abilities - SHIELDS DOWN", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getPlayerPokemon()!.status).toBe(undefined);
expect(game.field.getPlayerPokemon().status).toBe(undefined);
});
test("should still ignore non-volatile status moves used by a pokemon with mold breaker", async () => {
@ -78,7 +78,7 @@ describe("Abilities - SHIELDS DOWN", () => {
await game.move.selectEnemyMove(MoveId.SPORE);
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getPlayerPokemon()!.status).toBe(undefined);
expect(game.field.getPlayerPokemon().status).toBe(undefined);
});
test("should ignore non-volatile secondary status effects", async () => {
@ -89,7 +89,7 @@ describe("Abilities - SHIELDS DOWN", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getPlayerPokemon()!.status).toBe(undefined);
expect(game.field.getPlayerPokemon().status).toBe(undefined);
});
test("should ignore status moves even through mold breaker", async () => {
@ -101,7 +101,7 @@ describe("Abilities - SHIELDS DOWN", () => {
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getPlayerPokemon()!.status).toBe(undefined);
expect(game.field.getPlayerPokemon().status).toBe(undefined);
});
// toxic spikes currently does not poison flying types when gravity is in effect
@ -122,9 +122,9 @@ describe("Abilities - SHIELDS DOWN", () => {
await game.move.selectEnemyMove(MoveId.SPLASH);
await game.toNextTurn();
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(SpeciesId.MINIOR);
expect(game.scene.getPlayerPokemon()!.species.formIndex).toBe(0);
expect(game.scene.getPlayerPokemon()!.status?.effect).toBe(StatusEffect.POISON);
expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.MINIOR);
expect(game.field.getPlayerPokemon().species.formIndex).toBe(0);
expect(game.field.getPlayerPokemon().status?.effect).toBe(StatusEffect.POISON);
});
test("should ignore yawn", async () => {
@ -136,7 +136,7 @@ describe("Abilities - SHIELDS DOWN", () => {
await game.move.selectEnemyMove(MoveId.YAWN);
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getPlayerPokemon()!.findTag(tag => tag.tagType === BattlerTagType.DROWSY)).toBe(undefined);
expect(game.field.getPlayerPokemon().findTag(tag => tag.tagType === BattlerTagType.DROWSY)).toBe(undefined);
});
test("should not ignore volatile status effects", async () => {
@ -149,7 +149,7 @@ describe("Abilities - SHIELDS DOWN", () => {
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getPlayerPokemon()!.findTag(tag => tag.tagType === BattlerTagType.CONFUSED)).not.toBe(undefined);
expect(game.field.getPlayerPokemon().findTag(tag => tag.tagType === BattlerTagType.CONFUSED)).not.toBe(undefined);
});
// the `NoTransformAbilityAbAttr` attribute is not checked anywhere, so this test cannot pass.
@ -162,7 +162,7 @@ describe("Abilities - SHIELDS DOWN", () => {
await game.move.selectEnemyMove(MoveId.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.SLEEP);
expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.SLEEP);
});
test("should not prevent minior from receiving the fainted status effect in trainer battles", async () => {
@ -173,7 +173,7 @@ describe("Abilities - SHIELDS DOWN", () => {
.startingWave(5)
.enemySpecies(SpeciesId.MINIOR);
await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
const minior = game.scene.getEnemyPokemon()!;
const minior = game.field.getEnemyPokemon();
game.move.select(MoveId.THUNDERBOLT);
await game.toNextTurn();

View File

@ -33,7 +33,7 @@ describe("Abilities - Simple", () => {
it("should double stat changes when applied", async () => {
await game.classicMode.startBattle([SpeciesId.SLOWBRO]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-2);
});

View File

@ -40,7 +40,7 @@ describe("Abilities - Speed Boost", () => {
it("should increase speed by 1 stage at end of turn", async () => {
await game.classicMode.startBattle();
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
await game.toNextTurn();
@ -53,7 +53,7 @@ describe("Abilities - Speed Boost", () => {
game.move.select(MoveId.U_TURN);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0);
game.move.select(MoveId.SPLASH);
@ -69,13 +69,13 @@ describe("Abilities - Speed Boost", () => {
game.move.select(MoveId.U_TURN);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
expect(game.scene.getPlayerPokemon()!).toBe(ninjask);
expect(game.field.getPlayerPokemon()).toBe(ninjask);
expect(ninjask.getStatStage(Stat.SPD)).toBe(0);
game.move.select(MoveId.U_TURN);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
expect(game.scene.getPlayerPokemon()!).toBe(shuckle);
expect(game.field.getPlayerPokemon()).toBe(shuckle);
expect(shuckle.getStatStage(Stat.SPD)).toBe(0);
game.move.select(MoveId.SPLASH);
@ -88,7 +88,7 @@ describe("Abilities - Speed Boost", () => {
game.doSwitchPokemon(1);
await game.toNextTurn();
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0);
game.move.select(MoveId.SPLASH);
@ -109,7 +109,7 @@ describe("Abilities - Speed Boost", () => {
await game.phaseInterceptor.to(AttemptRunPhase);
await game.toNextTurn();
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0);
game.move.select(MoveId.SPLASH);

View File

@ -40,8 +40,8 @@ describe("Abilities - Stall", () => {
it("Pokemon with Stall should move last in its priority bracket regardless of speed", async () => {
await game.classicMode.startBattle([SpeciesId.SHUCKLE]);
const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex();
const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex();
const playerIndex = game.field.getPlayerPokemon().getBattlerIndex();
const enemyIndex = game.field.getEnemyPokemon().getBattlerIndex();
game.move.select(MoveId.QUICK_ATTACK);
@ -58,8 +58,8 @@ describe("Abilities - Stall", () => {
it("Pokemon with Stall will go first if a move that is in a higher priority bracket than the opponent's move is used", async () => {
await game.classicMode.startBattle([SpeciesId.SHUCKLE]);
const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex();
const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex();
const playerIndex = game.field.getPlayerPokemon().getBattlerIndex();
const enemyIndex = game.field.getEnemyPokemon().getBattlerIndex();
game.move.select(MoveId.TACKLE);
@ -77,8 +77,8 @@ describe("Abilities - Stall", () => {
game.override.ability(AbilityId.STALL);
await game.classicMode.startBattle([SpeciesId.SHUCKLE]);
const playerIndex = game.scene.getPlayerPokemon()!.getBattlerIndex();
const enemyIndex = game.scene.getEnemyPokemon()!.getBattlerIndex();
const playerIndex = game.field.getPlayerPokemon().getBattlerIndex();
const enemyIndex = game.field.getEnemyPokemon().getBattlerIndex();
game.move.select(MoveId.TACKLE);

View File

@ -39,7 +39,7 @@ describe("Abilities - Steely Spirit", () => {
it("increases Steel-type moves' power used by the user and its allies by 50%", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU, SpeciesId.SHUCKLE]);
const boostSource = game.scene.getPlayerField()[1];
const enemyToCheck = game.scene.getEnemyPokemon()!;
const enemyToCheck = game.field.getEnemyPokemon();
vi.spyOn(boostSource, "getAbility").mockReturnValue(allAbilities[AbilityId.STEELY_SPIRIT]);
@ -54,7 +54,7 @@ describe("Abilities - Steely Spirit", () => {
it("stacks if multiple users with this ability are on the field.", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU, SpeciesId.PIKACHU]);
const enemyToCheck = game.scene.getEnemyPokemon()!;
const enemyToCheck = game.field.getEnemyPokemon();
game.scene.getPlayerField().forEach(p => {
vi.spyOn(p, "getAbility").mockReturnValue(allAbilities[AbilityId.STEELY_SPIRIT]);
@ -74,7 +74,7 @@ describe("Abilities - Steely Spirit", () => {
it("does not take effect when suppressed", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU, SpeciesId.SHUCKLE]);
const boostSource = game.scene.getPlayerField()[1];
const enemyToCheck = game.scene.getEnemyPokemon()!;
const enemyToCheck = game.field.getEnemyPokemon();
vi.spyOn(boostSource, "getAbility").mockReturnValue(allAbilities[AbilityId.STEELY_SPIRIT]);
expect(boostSource.hasAbility(AbilityId.STEELY_SPIRIT)).toBe(true);

View File

@ -32,7 +32,7 @@ describe("Abilities - Super Luck", () => {
it("should increase the user's crit stage by 1", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
const critSpy = vi.spyOn(enemy, "getCritStage"); // crit stage is called on enemy
game.move.select(MoveId.TACKLE);

View File

@ -38,7 +38,7 @@ describe("Abilities - Synchronize", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.status).toBeUndefined();
expect(game.field.getPlayerPokemon().status).toBeUndefined();
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
});
@ -48,8 +48,8 @@ describe("Abilities - Synchronize", () => {
game.move.select(MoveId.THUNDER_WAVE);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.status?.effect).toBe(StatusEffect.PARALYSIS);
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.PARALYSIS);
expect(game.field.getPlayerPokemon().status?.effect).toBe(StatusEffect.PARALYSIS);
expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.PARALYSIS);
expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase");
});
@ -60,8 +60,8 @@ describe("Abilities - Synchronize", () => {
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.status?.effect).toBeUndefined();
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.SLEEP);
expect(game.field.getPlayerPokemon().status?.effect).toBeUndefined();
expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.SLEEP);
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
});
@ -76,8 +76,8 @@ describe("Abilities - Synchronize", () => {
game.doSwitchPokemon(1);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.status?.effect).toBe(StatusEffect.POISON);
expect(game.scene.getEnemyPokemon()!.status?.effect).toBeUndefined();
expect(game.field.getPlayerPokemon().status?.effect).toBe(StatusEffect.POISON);
expect(game.field.getEnemyPokemon().status?.effect).toBeUndefined();
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
});
@ -87,8 +87,8 @@ describe("Abilities - Synchronize", () => {
game.move.select(MoveId.THUNDER_WAVE);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.status?.effect).toBeUndefined();
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.PARALYSIS);
expect(game.field.getPlayerPokemon().status?.effect).toBeUndefined();
expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.PARALYSIS);
expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase");
});
});

View File

@ -36,7 +36,7 @@ describe("Abilities - Tera Shell", () => {
it("should change the effectiveness of non-resisted attacks when the source is at full HP", async () => {
await game.classicMode.startBattle([SpeciesId.SNORLAX]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
vi.spyOn(playerPokemon, "getMoveEffectiveness");
game.move.select(MoveId.SPLASH);
@ -57,7 +57,7 @@ describe("Abilities - Tera Shell", () => {
await game.classicMode.startBattle([SpeciesId.SNORLAX]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
vi.spyOn(playerPokemon, "getMoveEffectiveness");
game.move.select(MoveId.SPLASH);
@ -71,7 +71,7 @@ describe("Abilities - Tera Shell", () => {
await game.classicMode.startBattle([SpeciesId.AGGRON]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
vi.spyOn(playerPokemon, "getMoveEffectiveness");
game.move.select(MoveId.SPLASH);
@ -85,7 +85,7 @@ describe("Abilities - Tera Shell", () => {
await game.classicMode.startBattle([SpeciesId.CHARIZARD]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const spy = vi.spyOn(playerPokemon, "getMoveEffectiveness");
game.move.select(MoveId.SPLASH);
@ -100,7 +100,7 @@ describe("Abilities - Tera Shell", () => {
await game.classicMode.startBattle([SpeciesId.SNORLAX]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const spy = vi.spyOn(playerPokemon, "getMoveEffectiveness");
game.move.select(MoveId.SPLASH);

View File

@ -38,7 +38,7 @@ describe("Abilities - Trace", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(AbilityId.BALL_FETCH);
expect(game.field.getPlayerPokemon().getAbility().id).toBe(AbilityId.BALL_FETCH);
});
it("should activate a copied post-summon ability", async () => {
@ -48,6 +48,6 @@ describe("Abilities - Trace", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1);
});
});

View File

@ -64,7 +64,7 @@ describe("Abilities - Unburden", () => {
game.override.enemyMoveset(MoveId.FALSE_SWIPE);
await game.classicMode.startBattle([SpeciesId.TREECKO]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const playerHeldItems = getHeldItemCount(playerPokemon);
const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD);
@ -80,7 +80,7 @@ describe("Abilities - Unburden", () => {
game.override.enemyMoveset(MoveId.FALSE_SWIPE).startingModifier([{ name: "BERRY_POUCH", count: 5850 }]);
await game.classicMode.startBattle([SpeciesId.TREECKO]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const playerHeldItems = getHeldItemCount(playerPokemon);
const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD);
@ -95,9 +95,9 @@ describe("Abilities - Unburden", () => {
it("should activate for the target, and not the stealer, when a berry is stolen", async () => {
await game.classicMode.startBattle([SpeciesId.TREECKO]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const enemyHeldItemCt = getHeldItemCount(enemyPokemon);
const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD);
@ -113,7 +113,7 @@ describe("Abilities - Unburden", () => {
it("should activate when an item is knocked off", async () => {
await game.classicMode.startBattle([SpeciesId.TREECKO]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const enemyHeldItemCt = getHeldItemCount(enemyPokemon);
const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD);
@ -129,7 +129,7 @@ describe("Abilities - Unburden", () => {
game.override.ability(AbilityId.MAGICIAN).startingHeldItems([]); // Remove player's full stacks of held items so it can steal opponent's held items
await game.classicMode.startBattle([SpeciesId.TREECKO]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const enemyHeldItemCt = getHeldItemCount(enemyPokemon);
const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD);
@ -145,7 +145,7 @@ describe("Abilities - Unburden", () => {
game.override.enemyAbility(AbilityId.PICKPOCKET).enemyHeldItems([]); // Remove opponent's full stacks of held items so it can steal player's held items
await game.classicMode.startBattle([SpeciesId.TREECKO]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const playerHeldItems = getHeldItemCount(playerPokemon);
const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD);
@ -161,7 +161,7 @@ describe("Abilities - Unburden", () => {
game.override.moveset(MoveId.THIEF).startingHeldItems([]); // Remove player's full stacks of held items so it can steal opponent's held items
await game.classicMode.startBattle([SpeciesId.TREECKO]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const enemyHeldItemCt = getHeldItemCount(enemyPokemon);
const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD);
@ -177,11 +177,11 @@ describe("Abilities - Unburden", () => {
game.override.startingHeldItems([{ name: "GRIP_CLAW", count: 1 }]);
await game.classicMode.startBattle([SpeciesId.TREECKO]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const gripClaw = playerPokemon.getHeldItems()[0] as ContactHeldItemTransferChanceModifier;
vi.spyOn(gripClaw, "chance", "get").mockReturnValue(100);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const enemyHeldItemCt = getHeldItemCount(enemyPokemon);
const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD);
@ -197,7 +197,7 @@ describe("Abilities - Unburden", () => {
game.override.enemyAbility(AbilityId.NEUTRALIZING_GAS).enemyMoveset(MoveId.FALSE_SWIPE);
await game.classicMode.startBattle([SpeciesId.TREECKO]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const playerHeldItems = getHeldItemCount(playerPokemon);
const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD);
@ -214,7 +214,7 @@ describe("Abilities - Unburden", () => {
game.override.moveset(MoveId.STUFF_CHEEKS);
await game.classicMode.startBattle([SpeciesId.TREECKO]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const playerHeldItemCt = getHeldItemCount(playerPokemon);
const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD);
@ -292,7 +292,7 @@ describe("Abilities - Unburden", () => {
game.override.enemyMoveset([MoveId.FALSE_SWIPE, MoveId.WORRY_SEED]);
await game.classicMode.startBattle([SpeciesId.PURRLOIN]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const playerHeldItems = getHeldItemCount(playerPokemon);
const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD);
@ -317,7 +317,7 @@ describe("Abilities - Unburden", () => {
game.override.startingHeldItems([{ name: "REVIVER_SEED" }]).enemyMoveset([MoveId.WING_ATTACK]);
await game.classicMode.startBattle([SpeciesId.TREECKO]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const playerHeldItems = getHeldItemCount(playerPokemon);
const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD);
@ -334,7 +334,7 @@ describe("Abilities - Unburden", () => {
game.override.enemyMoveset([MoveId.SPLASH, MoveId.THIEF]);
await game.classicMode.startBattle([SpeciesId.TREECKO, SpeciesId.FEEBAS]);
const treecko = game.scene.getPlayerPokemon()!;
const treecko = game.field.getPlayerPokemon();
const treeckoInitialHeldItems = getHeldItemCount(treecko);
const initialSpeed = treecko.getStat(Stat.SPD);
@ -348,7 +348,7 @@ describe("Abilities - Unburden", () => {
await game.move.selectEnemyMove(MoveId.SPLASH);
await game.toNextTurn();
expect(game.scene.getPlayerPokemon()!).toBe(treecko);
expect(game.field.getPlayerPokemon()).toBe(treecko);
expect(getHeldItemCount(treecko)).toBeLessThan(treeckoInitialHeldItems);
expect(treecko.getEffectiveStat(Stat.SPD)).toBe(initialSpeed);
});

View File

@ -55,7 +55,7 @@ describe("Abilities - Unseen Fist", () => {
await game.classicMode.startBattle();
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
enemyPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, MoveId.NONE, enemyPokemon.id);
game.move.select(MoveId.TACKLE);
@ -77,10 +77,10 @@ async function testUnseenFistHitResult(
await game.classicMode.startBattle();
const leadPokemon = game.scene.getPlayerPokemon()!;
const leadPokemon = game.field.getPlayerPokemon();
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon).not.toBe(undefined);
const enemyStartingHp = enemyPokemon.hp;

View File

@ -42,7 +42,7 @@ describe("Abilities - Volt Absorb", () => {
await game.classicMode.startBattle();
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
game.move.select(moveToUse);
@ -62,7 +62,7 @@ describe("Abilities - Volt Absorb", () => {
await game.classicMode.startBattle();
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
game.move.select(MoveId.THUNDERBOLT);
enemyPokemon.hp = enemyPokemon.hp - 1;
@ -83,7 +83,7 @@ describe("Abilities - Volt Absorb", () => {
await game.classicMode.startBattle();
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
game.move.select(MoveId.THUNDERBOLT);
enemyPokemon.hp = enemyPokemon.hp - 1;

View File

@ -38,8 +38,8 @@ describe("Abilities - Wandering Spirit", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(AbilityId.BALL_FETCH);
expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(AbilityId.WANDERING_SPIRIT);
expect(game.field.getPlayerPokemon().getAbility().id).toBe(AbilityId.BALL_FETCH);
expect(game.field.getEnemyPokemon().getAbility().id).toBe(AbilityId.WANDERING_SPIRIT);
});
it("should not exchange abilities when hit with a non-contact move", async () => {
@ -49,8 +49,8 @@ describe("Abilities - Wandering Spirit", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()?.getAbility().id).toBe(AbilityId.WANDERING_SPIRIT);
expect(game.scene.getEnemyPokemon()?.getAbility().id).toBe(AbilityId.BALL_FETCH);
expect(game.field.getPlayerPokemon().getAbility().id).toBe(AbilityId.WANDERING_SPIRIT);
expect(game.field.getEnemyPokemon().getAbility().id).toBe(AbilityId.BALL_FETCH);
});
it("should activate post-summon abilities", async () => {
@ -60,6 +60,6 @@ describe("Abilities - Wandering Spirit", () => {
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1);
});
});

View File

@ -70,7 +70,7 @@ describe("Abilities - Wimp Out", () => {
game.override.passiveAbility(AbilityId.REGENERATOR).startingLevel(5).enemyLevel(100);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
const wimpod = game.scene.getPlayerPokemon()!;
const wimpod = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
game.doSelectPartyPokemon(1);
@ -84,7 +84,7 @@ describe("Abilities - Wimp Out", () => {
game.override.enemyAbility(AbilityId.WIMP_OUT);
await game.classicMode.startBattle([SpeciesId.GOLISOPOD, SpeciesId.TYRUNT]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
enemyPokemon.hp *= 0.52;
game.move.select(MoveId.FALSE_SWIPE);
@ -97,7 +97,7 @@ describe("Abilities - Wimp Out", () => {
it("Does not trigger when HP already below half", async () => {
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
const wimpod = game.scene.getPlayerPokemon()!;
const wimpod = game.field.getPlayerPokemon();
wimpod.hp = 5;
game.move.select(MoveId.SPLASH);
@ -117,7 +117,7 @@ describe("Abilities - Wimp Out", () => {
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
expect(game.scene.getPlayerPokemon()!.getTag(BattlerTagType.TRAPPED)).toBeUndefined();
expect(game.field.getPlayerPokemon().getTag(BattlerTagType.TRAPPED)).toBeUndefined();
expect(game.scene.getPlayerParty()[1].getTag(BattlerTagType.TRAPPED)).toBeUndefined();
confirmSwitch();
});
@ -130,7 +130,7 @@ describe("Abilities - Wimp Out", () => {
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const hasFled = enemyPokemon.switchOutStatus;
expect(hasFled).toBe(false);
confirmSwitch();
@ -139,17 +139,17 @@ describe("Abilities - Wimp Out", () => {
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 () => {
game.override.startingLevel(190).startingWave(8).enemyMoveset([MoveId.U_TURN]);
await game.classicMode.startBattle([SpeciesId.GOLISOPOD, SpeciesId.TYRUNT]);
const RIVAL_NINJASK1 = game.scene.getEnemyPokemon()?.id;
const RIVAL_NINJASK1 = game.field.getEnemyPokemon().id;
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("BerryPhase", false);
expect(game.scene.getEnemyPokemon()?.id !== RIVAL_NINJASK1);
expect(game.field.getEnemyPokemon().id !== RIVAL_NINJASK1);
});
it("Dragon Tail and Circle Throw switch out Pokémon before the Ability activates.", async () => {
game.override.startingLevel(69).enemyMoveset([MoveId.DRAGON_TAIL]);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
const wimpod = game.scene.getPlayerPokemon()!;
const wimpod = game.field.getPlayerPokemon();
game.move.select(MoveId.SPLASH);
game.doSelectPartyPokemon(1);
@ -159,7 +159,7 @@ describe("Abilities - Wimp Out", () => {
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerPokemon()!.species.speciesId).not.toBe(SpeciesId.WIMPOD);
expect(game.field.getPlayerPokemon().species.speciesId).not.toBe(SpeciesId.WIMPOD);
});
it("triggers when recoil damage is taken", async () => {
@ -177,7 +177,7 @@ describe("Abilities - Wimp Out", () => {
game.override.moveset([MoveId.SUBSTITUTE]).enemyMoveset([MoveId.SPLASH]);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
const wimpod = game.scene.getPlayerPokemon()!;
const wimpod = game.field.getPlayerPokemon();
wimpod.hp *= 0.52;
game.move.select(MoveId.SUBSTITUTE);
@ -208,7 +208,7 @@ describe("Abilities - Wimp Out", () => {
.startingHeldItems([{ name: "SHELL_BELL", count: 4 }]);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
const wimpod = game.scene.getPlayerPokemon()!;
const wimpod = game.field.getPlayerPokemon();
wimpod.damageAndUpdate(toDmgValue(wimpod.getMaxHp() * 0.4));
@ -219,7 +219,7 @@ describe("Abilities - Wimp Out", () => {
expect(game.scene.getPlayerParty()[1]).toBe(wimpod);
expect(wimpod.hp).toBeGreaterThan(toDmgValue(wimpod.getMaxHp() / 2));
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(SpeciesId.TYRUNT);
expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.TYRUNT);
},
);
@ -227,7 +227,7 @@ describe("Abilities - Wimp Out", () => {
game.override.weather(WeatherType.HAIL).enemyMoveset([MoveId.SPLASH]);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.field.getPlayerPokemon().hp *= 0.51;
game.move.select(MoveId.SPLASH);
game.doSelectPartyPokemon(1);
@ -240,7 +240,7 @@ describe("Abilities - Wimp Out", () => {
game.override.enemyAbility(AbilityId.SHEER_FORCE).enemyMoveset(MoveId.SLUDGE_BOMB).startingLevel(95);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.field.getPlayerPokemon().hp *= 0.51;
game.move.select(MoveId.ENDURE);
await game.phaseInterceptor.to("TurnEndPhase");
@ -252,7 +252,7 @@ describe("Abilities - Wimp Out", () => {
game.override.statusEffect(StatusEffect.POISON).enemyMoveset([MoveId.SPLASH]);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.field.getPlayerPokemon().hp *= 0.51;
game.move.select(MoveId.SPLASH);
game.doSelectPartyPokemon(1);
@ -265,7 +265,7 @@ describe("Abilities - Wimp Out", () => {
game.override.statusEffect(StatusEffect.SLEEP).enemyAbility(AbilityId.BAD_DREAMS);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.52;
game.field.getPlayerPokemon().hp *= 0.52;
game.move.select(MoveId.SPLASH);
game.doSelectPartyPokemon(1);
@ -277,7 +277,7 @@ describe("Abilities - Wimp Out", () => {
it("Wimp Out will activate due to leech seed", async () => {
game.override.enemyMoveset([MoveId.LEECH_SEED]);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.52;
game.field.getPlayerPokemon().hp *= 0.52;
game.move.select(MoveId.SPLASH);
game.doSelectPartyPokemon(1);
@ -289,7 +289,7 @@ describe("Abilities - Wimp Out", () => {
it("Wimp Out will activate due to curse damage", async () => {
game.override.enemySpecies(SpeciesId.DUSKNOIR).enemyMoveset([MoveId.CURSE]);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.52;
game.field.getPlayerPokemon().hp *= 0.52;
game.move.select(MoveId.SPLASH);
game.doSelectPartyPokemon(1);
@ -301,7 +301,7 @@ describe("Abilities - Wimp Out", () => {
it("Wimp Out will activate due to salt cure damage", async () => {
game.override.enemySpecies(SpeciesId.NACLI).enemyMoveset([MoveId.SALT_CURE]).enemyLevel(1);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.7;
game.field.getPlayerPokemon().hp *= 0.7;
game.move.select(MoveId.SPLASH);
game.doSelectPartyPokemon(1);
@ -313,7 +313,7 @@ describe("Abilities - Wimp Out", () => {
it("Wimp Out will activate due to damaging trap damage", async () => {
game.override.enemySpecies(SpeciesId.MAGIKARP).enemyMoveset([MoveId.WHIRLPOOL]).enemyLevel(1);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.55;
game.field.getPlayerPokemon().hp *= 0.55;
game.move.select(MoveId.SPLASH);
game.doSelectPartyPokemon(1);
@ -331,14 +331,14 @@ describe("Abilities - Wimp Out", () => {
.weather(WeatherType.HAIL)
.statusEffect(StatusEffect.POISON);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.field.getPlayerPokemon().hp *= 0.51;
game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerParty()[0].getHpRatio()).toEqual(0.51);
expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase");
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(SpeciesId.WIMPOD);
expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.WIMPOD);
});
it("Wimp Out activating should not cancel a double battle", async () => {
@ -369,7 +369,7 @@ describe("Abilities - Wimp Out", () => {
.enemyMoveset([MoveId.SPLASH])
.enemyLevel(1);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.field.getPlayerPokemon().hp *= 0.51;
game.move.select(MoveId.THUNDER_PUNCH);
game.doSelectPartyPokemon(1);
@ -391,7 +391,7 @@ describe("Abilities - Wimp Out", () => {
it("Wimp Out will activate due to Nightmare", async () => {
game.override.enemyMoveset([MoveId.NIGHTMARE]).statusEffect(StatusEffect.SLEEP);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.65;
game.field.getPlayerPokemon().hp *= 0.65;
game.move.select(MoveId.SPLASH);
game.doSelectPartyPokemon(1);
@ -417,13 +417,13 @@ describe("Abilities - Wimp Out", () => {
game.override.enemyMoveset(MoveId.BULLET_SEED).enemyAbility(AbilityId.SKILL_LINK);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.field.getPlayerPokemon().hp *= 0.51;
game.move.select(MoveId.ENDURE);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon.turnData.hitsLeft).toBe(0);
expect(enemyPokemon.turnData.hitCount).toBe(5);
confirmSwitch();
@ -433,13 +433,13 @@ describe("Abilities - Wimp Out", () => {
game.override.enemyMoveset(MoveId.TACKLE).enemyHeldItems([{ name: "MULTI_LENS", count: 1 }]);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.field.getPlayerPokemon().hp *= 0.51;
game.move.select(MoveId.ENDURE);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon.turnData.hitsLeft).toBe(0);
expect(enemyPokemon.turnData.hitCount).toBe(2);
confirmSwitch();
@ -448,13 +448,13 @@ describe("Abilities - Wimp Out", () => {
game.override.enemyMoveset(MoveId.TACKLE).enemyAbility(AbilityId.PARENTAL_BOND);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
game.scene.getPlayerPokemon()!.hp *= 0.51;
game.field.getPlayerPokemon().hp *= 0.51;
game.move.select(MoveId.ENDURE);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon.turnData.hitsLeft).toBe(0);
expect(enemyPokemon.turnData.hitCount).toBe(2);
confirmSwitch();
@ -466,7 +466,7 @@ describe("Abilities - Wimp Out", () => {
async () => {
game.override.moveset([MoveId.SWORDS_DANCE]).enemyMoveset([MoveId.SWAGGER]);
await game.classicMode.startBattle([SpeciesId.WIMPOD, SpeciesId.TYRUNT]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
playerPokemon.hp *= 0.51;
playerPokemon.setStatStage(Stat.ATK, 6);
playerPokemon.addTag(BattlerTagType.CONFUSED);
@ -486,7 +486,7 @@ describe("Abilities - Wimp Out", () => {
game.override.enemyAbility(AbilityId.WIMP_OUT).startingLevel(5850).startingWave(10);
await game.classicMode.startBattle([SpeciesId.GOLISOPOD]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
// Use 2 turns of False Swipe due to opponent's health bar shield
game.move.select(MoveId.FALSE_SWIPE);

View File

@ -33,7 +33,7 @@ describe("Abilities - Wind Power", () => {
it("becomes charged when hit by wind moves", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const shiftry = game.scene.getEnemyPokemon()!;
const shiftry = game.field.getEnemyPokemon();
expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined();
@ -47,7 +47,7 @@ describe("Abilities - Wind Power", () => {
game.override.ability(AbilityId.WIND_POWER).enemySpecies(SpeciesId.MAGIKARP);
await game.classicMode.startBattle([SpeciesId.SHIFTRY]);
const shiftry = game.scene.getPlayerPokemon()!;
const shiftry = game.field.getPlayerPokemon();
expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined();
@ -61,8 +61,8 @@ describe("Abilities - Wind Power", () => {
game.override.enemySpecies(SpeciesId.MAGIKARP).ability(AbilityId.WIND_POWER);
await game.classicMode.startBattle([SpeciesId.SHIFTRY]);
const magikarp = game.scene.getEnemyPokemon()!;
const shiftry = game.scene.getPlayerPokemon()!;
const magikarp = game.field.getEnemyPokemon();
const shiftry = game.field.getPlayerPokemon();
expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined();
expect(magikarp.getTag(BattlerTagType.CHARGED)).toBeUndefined();
@ -79,7 +79,7 @@ describe("Abilities - Wind Power", () => {
game.override.enemySpecies(SpeciesId.MAGIKARP);
await game.classicMode.startBattle([SpeciesId.SHIFTRY]);
const shiftry = game.scene.getPlayerPokemon()!;
const shiftry = game.field.getPlayerPokemon();
expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined();

View File

@ -32,7 +32,7 @@ describe("Abilities - Wind Rider", () => {
it("takes no damage from wind moves and its ATK stat stage is raised by 1 when hit by one", async () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const shiftry = game.scene.getEnemyPokemon()!;
const shiftry = game.field.getEnemyPokemon();
expect(shiftry.getStatStage(Stat.ATK)).toBe(0);
@ -48,7 +48,7 @@ describe("Abilities - Wind Rider", () => {
game.override.enemySpecies(SpeciesId.MAGIKARP).ability(AbilityId.WIND_RIDER);
await game.classicMode.startBattle([SpeciesId.SHIFTRY]);
const shiftry = game.scene.getPlayerPokemon()!;
const shiftry = game.field.getPlayerPokemon();
expect(shiftry.getStatStage(Stat.ATK)).toBe(0);
@ -63,8 +63,8 @@ describe("Abilities - Wind Rider", () => {
game.override.enemySpecies(SpeciesId.MAGIKARP).ability(AbilityId.WIND_RIDER);
await game.classicMode.startBattle([SpeciesId.SHIFTRY]);
const magikarp = game.scene.getEnemyPokemon()!;
const shiftry = game.scene.getPlayerPokemon()!;
const magikarp = game.field.getEnemyPokemon();
const shiftry = game.field.getPlayerPokemon();
expect(shiftry.getStatStage(Stat.ATK)).toBe(0);
expect(magikarp.getStatStage(Stat.ATK)).toBe(0);
@ -81,8 +81,8 @@ describe("Abilities - Wind Rider", () => {
game.override.enemySpecies(SpeciesId.MAGIKARP).ability(AbilityId.WIND_RIDER);
await game.classicMode.startBattle([SpeciesId.SHIFTRY]);
const magikarp = game.scene.getEnemyPokemon()!;
const shiftry = game.scene.getPlayerPokemon()!;
const magikarp = game.field.getEnemyPokemon();
const shiftry = game.field.getPlayerPokemon();
expect(shiftry.getStatStage(Stat.ATK)).toBe(0);
expect(magikarp.getStatStage(Stat.ATK)).toBe(0);
@ -99,7 +99,7 @@ describe("Abilities - Wind Rider", () => {
game.override.enemySpecies(SpeciesId.MAGIKARP);
await game.classicMode.startBattle([SpeciesId.SHIFTRY]);
const shiftry = game.scene.getPlayerPokemon()!;
const shiftry = game.field.getPlayerPokemon();
expect(shiftry.getStatStage(Stat.ATK)).toBe(0);
expect(shiftry.isFullHp()).toBe(true);

View File

@ -38,7 +38,7 @@ describe("Abilities - ZEN MODE", () => {
it("shouldn't change form when taking damage if not dropping below 50% HP", async () => {
await game.classicMode.startBattle([SpeciesId.DARMANITAN]);
const darmanitan = game.scene.getPlayerPokemon()!;
const darmanitan = game.field.getPlayerPokemon();
expect(darmanitan.formIndex).toBe(baseForm);
game.move.select(MoveId.SPLASH);
@ -52,7 +52,7 @@ describe("Abilities - ZEN MODE", () => {
it("should change form when falling below 50% HP", async () => {
await game.classicMode.startBattle([SpeciesId.DARMANITAN]);
const darmanitan = game.scene.getPlayerPokemon()!;
const darmanitan = game.field.getPlayerPokemon();
darmanitan.hp = darmanitan.getMaxHp() / 2 + 1;
expect(darmanitan.formIndex).toBe(baseForm);
@ -65,7 +65,7 @@ describe("Abilities - ZEN MODE", () => {
it("should stay zen mode when fainted", async () => {
await game.classicMode.startBattle([SpeciesId.DARMANITAN, SpeciesId.CHARIZARD]);
const darmanitan = game.scene.getPlayerPokemon()!;
const darmanitan = game.field.getPlayerPokemon();
darmanitan.hp = darmanitan.getMaxHp() / 2 + 1;
expect(darmanitan.formIndex).toBe(baseForm);

View File

@ -62,7 +62,7 @@ describe("Abilities - ZERO TO HERO", () => {
it("should swap to Hero form when switching out during a battle", async () => {
await game.classicMode.startBattle([SpeciesId.PALAFIN, SpeciesId.FEEBAS]);
const palafin = game.scene.getPlayerPokemon()!;
const palafin = game.field.getPlayerPokemon();
expect(palafin.formIndex).toBe(baseForm);
game.doSwitchPokemon(1);
@ -73,7 +73,7 @@ describe("Abilities - ZERO TO HERO", () => {
it("should not swap to Hero form if switching due to faint", async () => {
await game.classicMode.startBattle([SpeciesId.PALAFIN, SpeciesId.FEEBAS]);
const palafin = game.scene.getPlayerPokemon()!;
const palafin = game.field.getPlayerPokemon();
expect(palafin.formIndex).toBe(baseForm);
game.move.select(MoveId.SPLASH);
@ -90,7 +90,7 @@ describe("Abilities - ZERO TO HERO", () => {
await game.classicMode.startBattle([SpeciesId.PALAFIN, SpeciesId.FEEBAS]);
const palafin = game.scene.getPlayerPokemon()!;
const palafin = game.field.getPlayerPokemon();
expect(palafin.formIndex).toBe(heroForm);
game.move.select(MoveId.SPLASH);

View File

@ -84,7 +84,7 @@ describe("Arena - Gravity", () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const pidgeot = game.scene.getEnemyPokemon()!;
const pidgeot = game.field.getEnemyPokemon();
vi.spyOn(pidgeot, "getAttackTypeEffectiveness");
// Try earthquake on 1st turn (fails!);
@ -113,7 +113,7 @@ describe("Arena - Gravity", () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const pidgeot = game.scene.getEnemyPokemon()!;
const pidgeot = game.field.getEnemyPokemon();
vi.spyOn(pidgeot, "getAttackTypeEffectiveness");
// Setup Gravity on 1st turn
@ -136,8 +136,8 @@ describe("Arena - Gravity", () => {
await game.classicMode.startBattle([SpeciesId.CHARIZARD]);
const charizard = game.scene.getPlayerPokemon()!;
const snorlax = game.scene.getEnemyPokemon()!;
const charizard = game.field.getPlayerPokemon();
const snorlax = game.field.getEnemyPokemon();
game.move.select(MoveId.SPLASH);

View File

@ -42,7 +42,7 @@ describe("Arena - Psychic Terrain", () => {
game.move.select(MoveId.DARK_VOID);
await game.toEndOfTurn();
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.SLEEP);
expect(game.field.getEnemyPokemon().status?.effect).toBe(StatusEffect.SLEEP);
});
it("Rain Dance with Prankster is not blocked", async () => {

View File

@ -52,8 +52,8 @@ describe("Weather - Hail", () => {
await game.phaseInterceptor.to("TurnEndPhase");
const playerPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp());
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - Math.max(Math.floor(enemyPokemon.getMaxHp() / 16), 1));
@ -66,8 +66,8 @@ describe("Weather - Hail", () => {
await game.phaseInterceptor.to("TurnEndPhase");
const playerPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp());
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - Math.max(Math.floor(enemyPokemon.getMaxHp() / 16), 1));

View File

@ -51,8 +51,8 @@ describe("Weather - Sandstorm", () => {
await game.phaseInterceptor.to("TurnEndPhase");
const playerPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp());
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - Math.max(Math.floor(enemyPokemon.getMaxHp() / 16), 1));
@ -80,11 +80,11 @@ describe("Weather - Sandstorm", () => {
it("increases Rock type Pokemon Sp.Def by 50%", async () => {
await game.classicMode.startBattle([SpeciesId.ROCKRUFF]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const playerSpdef = playerPokemon.getStat(Stat.SPDEF);
expect(playerPokemon.getEffectiveStat(Stat.SPDEF)).toBe(Math.floor(playerSpdef * 1.5));
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
const enemySpdef = enemyPokemon.getStat(Stat.SPDEF);
expect(enemyPokemon.getEffectiveStat(Stat.SPDEF)).toBe(enemySpdef);
});

View File

@ -36,8 +36,8 @@ describe("Weather - Strong Winds", () => {
game.override.enemySpecies(SpeciesId.RAYQUAZA);
await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const pikachu = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const pikachu = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
game.move.select(MoveId.THUNDERBOLT);
@ -47,8 +47,8 @@ describe("Weather - Strong Winds", () => {
it("electric type move is neutral for flying type pokemon", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const pikachu = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const pikachu = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
game.move.select(MoveId.THUNDERBOLT);
@ -58,8 +58,8 @@ describe("Weather - Strong Winds", () => {
it("ice type move is neutral for flying type pokemon", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const pikachu = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const pikachu = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
game.move.select(MoveId.ICE_BEAM);
@ -69,8 +69,8 @@ describe("Weather - Strong Winds", () => {
it("rock type move is neutral for flying type pokemon", async () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const pikachu = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
const pikachu = game.field.getPlayerPokemon();
const enemy = game.field.getEnemyPokemon();
game.move.select(MoveId.ROCK_SLIDE);
@ -83,7 +83,7 @@ describe("Weather - Strong Winds", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const enemy = game.scene.getEnemyPokemon()!;
const enemy = game.field.getEnemyPokemon();
enemy.hp = 1;
game.move.use(MoveId.SPLASH);

View File

@ -37,10 +37,10 @@ describe("Test Ability Swapping", () => {
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
game.move.select(MoveId.SPLASH);
game.scene.getPlayerPokemon()?.setTempAbility(allAbilities[AbilityId.INTIMIDATE]);
game.field.getPlayerPokemon().setTempAbility(allAbilities[AbilityId.INTIMIDATE]);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1);
});
it("should remove primal weather when the setter's ability is removed", async () => {
@ -48,7 +48,7 @@ describe("Test Ability Swapping", () => {
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
game.move.select(MoveId.SPLASH);
game.scene.getPlayerPokemon()?.setTempAbility(allAbilities[AbilityId.BALL_FETCH]);
game.field.getPlayerPokemon().setTempAbility(allAbilities[AbilityId.BALL_FETCH]);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.arena.weather?.weatherType).toBeUndefined();
@ -59,10 +59,10 @@ describe("Test Ability Swapping", () => {
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
game.move.select(MoveId.SPLASH);
game.scene.getPlayerPokemon()?.setTempAbility(allAbilities[AbilityId.BALL_FETCH]);
game.field.getPlayerPokemon().setTempAbility(allAbilities[AbilityId.BALL_FETCH]);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()?.getStatStage(Stat.ATK)).toBe(1); // would be 2 if passive activated again
expect(game.field.getPlayerPokemon().getStatStage(Stat.ATK)).toBe(1); // would be 2 if passive activated again
});
// Pickup and Honey Gather are special cases as they're the only abilities to be Unsuppressable but not Unswappable
@ -73,6 +73,6 @@ describe("Test Ability Swapping", () => {
game.move.select(MoveId.ROLE_PLAY);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1);
});
});

View File

@ -35,8 +35,8 @@ describe("Battle order", () => {
it("opponent faster than player 50 vs 150", async () => {
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set playerPokemon's speed to 50
vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150
@ -54,8 +54,8 @@ describe("Battle order", () => {
it("Player faster than opponent 150 vs 50", async () => {
await game.classicMode.startBattle([SpeciesId.BULBASAUR]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
const enemyPokemon = game.field.getEnemyPokemon();
vi.spyOn(playerPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set playerPokemon's speed to 150
vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set enemyPokemon's speed to 50

View File

@ -293,7 +293,7 @@ describe("Phase - Battle Phase", () => {
.startingHeldItems([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ACC }]);
await game.classicMode.startBattle();
game.scene.getPlayerPokemon()!.hp = 1;
game.field.getPlayerPokemon().hp = 1;
game.move.select(moveToUse);
await game.phaseInterceptor.to(BattleEndPhase);

View File

@ -38,10 +38,10 @@ describe("Battle Mechanics - Damage Calculation", () => {
it("Tackle deals expected base damage", async () => {
await game.classicMode.startBattle([SpeciesId.CHARIZARD]);
const playerPokemon = game.scene.getPlayerPokemon()!;
const playerPokemon = game.field.getPlayerPokemon();
vi.spyOn(playerPokemon, "getEffectiveStat").mockReturnValue(80);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.field.getEnemyPokemon();
vi.spyOn(enemyPokemon, "getEffectiveStat").mockReturnValue(90);
// expected base damage = [(2*level/5 + 2) * power * playerATK / enemyDEF / 50] + 2
@ -56,7 +56,7 @@ describe("Battle Mechanics - Damage Calculation", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const aggron = game.scene.getEnemyPokemon()!;
const aggron = game.field.getEnemyPokemon();
game.move.select(MoveId.TACKLE);
@ -75,7 +75,7 @@ describe("Battle Mechanics - Damage Calculation", () => {
dmg_redux_modifier.stackCount = 1000;
await game.scene.addEnemyModifier(modifierTypes.ENEMY_DAMAGE_REDUCTION().newModifier() as EnemyPersistentModifier);
const aggron = game.scene.getEnemyPokemon()!;
const aggron = game.field.getEnemyPokemon();
game.move.select(MoveId.TACKLE);
@ -89,8 +89,8 @@ describe("Battle Mechanics - Damage Calculation", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const magikarp = game.scene.getPlayerPokemon()!;
const dragonite = game.scene.getEnemyPokemon()!;
const magikarp = game.field.getPlayerPokemon();
const dragonite = game.field.getEnemyPokemon();
expect(dragonite.getAttackDamage({ source: magikarp, move: allMoves[MoveId.DRAGON_RAGE] }).damage).toBe(40);
});
@ -100,8 +100,8 @@ describe("Battle Mechanics - Damage Calculation", () => {
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
const magikarp = game.scene.getPlayerPokemon()!;
const aggron = game.scene.getEnemyPokemon()!;
const magikarp = game.field.getPlayerPokemon();
const aggron = game.field.getEnemyPokemon();
expect(aggron.getAttackDamage({ source: magikarp, move: allMoves[MoveId.FISSURE] }).damage).toBe(aggron.hp);
});
@ -111,7 +111,7 @@ describe("Battle Mechanics - Damage Calculation", () => {
await game.classicMode.startBattle([SpeciesId.SHEDINJA]);
const shedinja = game.scene.getPlayerPokemon()!;
const shedinja = game.field.getPlayerPokemon();
game.move.select(MoveId.JUMP_KICK);
@ -126,7 +126,7 @@ describe("Battle Mechanics - Damage Calculation", () => {
await game.classicMode.startBattle([SpeciesId.PIKACHU]);
const charizard = game.scene.getEnemyPokemon()!;
const charizard = game.field.getEnemyPokemon();
if (charizard.getMaxHp() % 2 === 1) {
expect(charizard.hp).toBeGreaterThan(charizard.getMaxHp() / 2);

Some files were not shown because too many files have changed in this diff Show More