mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-12-16 14:55:22 +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 turnCount: number;
|
||||||
private readonly side?: ArenaTagSide;
|
private readonly side?: ArenaTagSide;
|
||||||
private readonly quiet?: boolean;
|
private readonly quiet?: boolean;
|
||||||
|
// TODO: This should not need to track the source ID in a tempvar
|
||||||
private sourceId: number;
|
private sourceId: number;
|
||||||
|
|
||||||
constructor(showAbility: boolean, tagType: ArenaTagType, turnCount: number, side?: ArenaTagSide, quiet?: boolean) {
|
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 {
|
export class PostSummonUnnamedMessageAbAttr extends PostSummonAbAttr {
|
||||||
//Attr doesn't force pokemon name on the message
|
//Attr doesn't force pokemon name on the message
|
||||||
private readonly message: string;
|
private readonly message: string;
|
||||||
@ -2811,13 +2813,13 @@ export class PostSummonStatStageChangeAbAttr extends PostSummonAbAttr {
|
|||||||
private readonly selfTarget: boolean;
|
private readonly selfTarget: boolean;
|
||||||
private readonly intimidate: 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);
|
super(true);
|
||||||
|
|
||||||
this.stats = stats;
|
this.stats = stats;
|
||||||
this.stages = stages;
|
this.stages = stages;
|
||||||
this.selfTarget = !!selfTarget;
|
this.selfTarget = selfTarget;
|
||||||
this.intimidate = !!intimidate;
|
this.intimidate = intimidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
override apply({ pokemon, simulated }: AbAttrBaseParams): void {
|
override apply({ pokemon, simulated }: AbAttrBaseParams): void {
|
||||||
@ -5012,25 +5014,26 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr {
|
|||||||
*/
|
*/
|
||||||
export class FetchBallAbAttr extends PostTurnAbAttr {
|
export class FetchBallAbAttr extends PostTurnAbAttr {
|
||||||
override canApply({ simulated, pokemon }: AbAttrBaseParams): boolean {
|
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
|
* Adds the last used Pokeball back into the player's inventory
|
||||||
*/
|
*/
|
||||||
override apply({ pokemon }: AbAttrBaseParams): void {
|
override apply({ pokemon }: AbAttrBaseParams): void {
|
||||||
const lastUsed = globalScene.currentBattle.lastUsedPokeball;
|
const lastUsed = globalScene.currentBattle.lastUsedPokeball!;
|
||||||
globalScene.pokeballCounts[lastUsed!]++;
|
globalScene.pokeballCounts[lastUsed]++;
|
||||||
globalScene.currentBattle.lastUsedPokeball = null;
|
globalScene.currentBattle.lastUsedPokeball = null;
|
||||||
globalScene.phaseManager.queueMessage(
|
globalScene.phaseManager.queueMessage(
|
||||||
i18next.t("abilityTriggers:fetchBall", {
|
i18next.t("abilityTriggers:fetchBall", {
|
||||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
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 {
|
export class PostBiomeChangeAbAttr extends AbAttr {
|
||||||
private declare readonly _: never;
|
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 */
|
/** @sealed */
|
||||||
export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr {
|
export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr {
|
||||||
private readonly terrainType: TerrainType;
|
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]==================");
|
console.log("==================[New Turn]==================");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Transition to the {@linkcode TurnEndPhase | end of the current turn}. */
|
/**
|
||||||
async toEndOfTurn() {
|
* Transition to the {@linkcode TurnEndPhase | end of the current turn}.
|
||||||
await this.phaseInterceptor.to("TurnEndPhase");
|
* @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]==================");
|
console.log("==================[End of Turn]==================");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user