mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-12-15 06:15:20 +01:00
[Bug] Fix Ball Fetch activating on enemy Pokemon (#6777)
This commit is contained in:
parent
bf68f59161
commit
4e080465b9
@ -2707,6 +2707,7 @@ export class PostSummonAddArenaTagAbAttr extends PostSummonAbAttr {
|
||||
private readonly turnCount: number;
|
||||
private readonly side?: ArenaTagSide;
|
||||
private readonly quiet?: boolean;
|
||||
// TODO: This should not need to track the source ID in a tempvar
|
||||
private sourceId: number;
|
||||
|
||||
constructor(showAbility: boolean, tagType: ArenaTagType, turnCount: number, side?: ArenaTagSide, quiet?: boolean) {
|
||||
@ -2741,6 +2742,7 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This should be merged with message func
|
||||
export class PostSummonUnnamedMessageAbAttr extends PostSummonAbAttr {
|
||||
//Attr doesn't force pokemon name on the message
|
||||
private readonly message: string;
|
||||
@ -2811,13 +2813,13 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr {
|
||||
private readonly selfTarget: boolean;
|
||||
private readonly intimidate: boolean;
|
||||
|
||||
constructor(stats: readonly BattleStat[], stages: number, selfTarget?: boolean, intimidate?: boolean) {
|
||||
constructor(stats: readonly BattleStat[], stages: number, selfTarget = false, intimidate = true) {
|
||||
super(true);
|
||||
|
||||
this.stats = stats;
|
||||
this.stages = stages;
|
||||
this.selfTarget = !!selfTarget;
|
||||
this.intimidate = !!intimidate;
|
||||
this.selfTarget = selfTarget;
|
||||
this.intimidate = intimidate;
|
||||
}
|
||||
|
||||
override apply({ pokemon, simulated }: AbAttrBaseParams): void {
|
||||
@ -5012,25 +5014,26 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr {
|
||||
*/
|
||||
export class FetchBallAbAttr extends PostTurnAbAttr {
|
||||
override canApply({ simulated, pokemon }: AbAttrBaseParams): boolean {
|
||||
return !simulated && globalScene.currentBattle.lastUsedPokeball != null && !!pokemon.isPlayer;
|
||||
return !simulated && globalScene.currentBattle.lastUsedPokeball != null && pokemon.isPlayer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the last used Pokeball back into the player's inventory
|
||||
*/
|
||||
override apply({ pokemon }: AbAttrBaseParams): void {
|
||||
const lastUsed = globalScene.currentBattle.lastUsedPokeball;
|
||||
globalScene.pokeballCounts[lastUsed!]++;
|
||||
const lastUsed = globalScene.currentBattle.lastUsedPokeball!;
|
||||
globalScene.pokeballCounts[lastUsed]++;
|
||||
globalScene.currentBattle.lastUsedPokeball = null;
|
||||
globalScene.phaseManager.queueMessage(
|
||||
i18next.t("abilityTriggers:fetchBall", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
pokeballName: getPokeballName(lastUsed!),
|
||||
pokeballName: getPokeballName(lastUsed),
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove this and just replace it with applying `PostSummonChangeTerrainAbAttr` again
|
||||
export class PostBiomeChangeAbAttr extends AbAttr {
|
||||
private declare readonly _: never;
|
||||
}
|
||||
@ -5055,6 +5058,7 @@ export class PostBiomeChangeWeatherChangeAbAttr extends PostBiomeChangeAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove this and just replace it with applying `PostSummonChangeTerrainAbAttr` again
|
||||
/** @sealed */
|
||||
export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr {
|
||||
private readonly terrainType: TerrainType;
|
||||
|
||||
92
test/abilities/ball-fetch.test.ts
Normal file
92
test/abilities/ball-fetch.test.ts
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Pagefault Games
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { getPokeballName } from "#data/pokeball";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import i18next from "i18next";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
// NB: These tests pass when done locally, but we currently have no mechanism to make catches fail
|
||||
// due to battle scene RNG overrides making ball shake checks always succeed.
|
||||
//
|
||||
// TODO: Enable suite once `AttemptCapturePhase` is made sane
|
||||
describe.todo("Ability - Ball Fetch", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.ability(AbilityId.BALL_FETCH)
|
||||
.battleStyle("single")
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyMoveset(MoveId.SPLASH)
|
||||
.startingLevel(100)
|
||||
.enemyLevel(100);
|
||||
});
|
||||
|
||||
it("should restore the user's first failed ball throw at end of turn", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||
|
||||
const feebas = game.field.getPlayerPokemon();
|
||||
const karp = game.field.getEnemyPokemon();
|
||||
|
||||
vi.spyOn(karp.species, "catchRate", "get").mockReturnValue(0);
|
||||
|
||||
game.doThrowPokeball(PokeballType.POKEBALL);
|
||||
await game.toEndOfTurn(false);
|
||||
|
||||
expect(game.scene.pokeballCounts[PokeballType.POKEBALL]).toBe(4);
|
||||
|
||||
await game.toEndOfTurn();
|
||||
|
||||
expect(feebas).toHaveAbilityApplied(AbilityId.BALL_FETCH);
|
||||
expect(game.scene.pokeballCounts[PokeballType.POKEBALL]).toBe(5);
|
||||
expect(game).toHaveShownMessage(
|
||||
i18next.t("abilityTriggers:fetchBall", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(feebas),
|
||||
pokeballName: getPokeballName(PokeballType.POKEBALL),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("should not work on enemies", async () => {
|
||||
game.override.ability(AbilityId.AIR_LOCK).enemyAbility(AbilityId.BALL_FETCH);
|
||||
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||
|
||||
const karp = game.field.getEnemyPokemon();
|
||||
|
||||
vi.spyOn(karp.species, "catchRate", "get").mockReturnValue(0);
|
||||
|
||||
game.doThrowPokeball(PokeballType.POKEBALL);
|
||||
await game.toEndOfTurn(false);
|
||||
|
||||
expect(game.scene.pokeballCounts[PokeballType.POKEBALL]).toBe(4);
|
||||
|
||||
await game.toEndOfTurn();
|
||||
|
||||
// did nothing; still at 4 balls
|
||||
expect(karp).not.toHaveAbilityApplied(AbilityId.BALL_FETCH);
|
||||
expect(game.scene.pokeballCounts[PokeballType.POKEBALL]).toBe(4);
|
||||
});
|
||||
});
|
||||
@ -372,9 +372,13 @@ export class GameManager {
|
||||
console.log("==================[New Turn]==================");
|
||||
}
|
||||
|
||||
/** Transition to the {@linkcode TurnEndPhase | end of the current turn}. */
|
||||
async toEndOfTurn() {
|
||||
await this.phaseInterceptor.to("TurnEndPhase");
|
||||
/**
|
||||
* Transition to the {@linkcode TurnEndPhase | end of the current turn}.
|
||||
* @param endTurn - Whether to run the `TurnEndPhase` or not; default `true`
|
||||
* @returns A Promise that resolves once the current turn has ended.
|
||||
*/
|
||||
async toEndOfTurn(endTurn = true): Promise<void> {
|
||||
await this.phaseInterceptor.to("TurnEndPhase", endTurn);
|
||||
console.log("==================[End of Turn]==================");
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user