[Bug] Prevent evolution causing a swap from the second ability to the HA (#3138)

* Prevent evolution causing a swap from the second ability to the HA

* Add tests

* Update `starter-select-ui-handler.ts`
This commit is contained in:
NightKev 2024-07-25 04:50:17 -07:00 committed by GitHub
parent 2d51fc5394
commit 9f01b52d6b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 108 additions and 9 deletions

View File

@ -147,7 +147,7 @@ export abstract class PokemonSpeciesForm {
this.height = height;
this.weight = weight;
this.ability1 = ability1;
this.ability2 = ability2;
this.ability2 = ability2 === Abilities.NONE ? ability1 : ability2;
this.abilityHidden = abilityHidden;
this.baseTotal = baseTotal;
this.baseStats = [ baseHp, baseAtk, baseDef, baseSpatk, baseSpdef, baseSpd ];

View File

@ -219,7 +219,7 @@ export class EvolutionPhase extends Phase {
this.scene.time.delayedCall(900, () => {
evolutionHandler.canCancel = false;
this.pokemon.evolve(this.evolution).then(() => {
this.pokemon.evolve(this.evolution, this.pokemon.species).then(() => {
const levelMoves = this.pokemon.getLevelMoves(this.lastLevel + 1, true);
for (const lm of levelMoves) {
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.scene.getParty().indexOf(this.pokemon), lm[1]));

View File

@ -3303,9 +3303,10 @@ export class PlayerPokemon extends Pokemon {
});
}
evolve(evolution: SpeciesFormEvolution): Promise<void> {
evolve(evolution: SpeciesFormEvolution, preEvolution: PokemonSpeciesForm): Promise<void> {
return new Promise(resolve => {
this.pauseEvolutions = false;
// Handles Nincada evolving into Ninjask + Shedinja
this.handleSpecialEvolutions(evolution);
const isFusion = evolution instanceof FusionSpeciesFormEvolution;
if (!isFusion) {
@ -3323,16 +3324,26 @@ export class PlayerPokemon extends Pokemon {
}
this.generateName();
if (!isFusion) {
// Prevent pokemon with an illegal ability value from breaking things too badly
const abilityCount = this.getSpeciesForm().getAbilityCount();
if (this.abilityIndex >= abilityCount) {
const preEvoAbilityCount = preEvolution.getAbilityCount();
if ([0, 1, 2].includes(this.abilityIndex)) {
// Handles cases where a Pokemon with 3 abilities evolves into a Pokemon with 2 abilities (ie: Eevee -> any Eeveelution)
if (this.abilityIndex === 2 && preEvoAbilityCount === 3 && abilityCount === 2) {
this.abilityIndex = 1;
}
} else { // Prevent pokemon with an illegal ability value from breaking things
console.warn("this.abilityIndex is somehow an illegal value, please report this");
console.warn(this.abilityIndex);
this.abilityIndex = 0;
}
} else { // Do the same as above, but for fusions
const abilityCount = this.getFusionSpeciesForm().getAbilityCount();
if (this.fusionAbilityIndex >= abilityCount) {
const preEvoAbilityCount = preEvolution.getAbilityCount();
if ([0, 1, 2].includes(this.fusionAbilityIndex)) {
if (this.fusionAbilityIndex === 2 && preEvoAbilityCount === 3 && abilityCount === 2) {
this.fusionAbilityIndex = 1;
}
} else {
console.warn("this.fusionAbilityIndex is somehow an illegal value, please report this");
console.warn(this.fusionAbilityIndex);
this.fusionAbilityIndex = 0;
@ -3379,7 +3390,7 @@ export class PlayerPokemon extends Pokemon {
newPokemon.fusionLuck = this.fusionLuck;
this.scene.getParty().push(newPokemon);
newPokemon.evolve(!isFusion ? newEvolution : new FusionSpeciesFormEvolution(this.id, newEvolution));
newPokemon.evolve((!isFusion ? newEvolution : new FusionSpeciesFormEvolution(this.id, newEvolution)), evoSpecies);
const modifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& m.pokemonId === this.id, true) as PokemonHeldItemModifier[];
modifiers.forEach(m => {

View File

@ -0,0 +1,87 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import Phaser from "phaser";
import GameManager from "#app/test/utils/gameManager";
import { Species } from "#app/enums/species.js";
import { Abilities } from "#app/enums/abilities.js";
import Overrides from "#app/overrides";
import { pokemonEvolutions } from "#app/data/pokemon-evolutions.js";
describe("Evolution", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
const TIMEOUT = 1000 * 20;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
vi.spyOn(Overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue("single");
vi.spyOn(Overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MAGIKARP);
vi.spyOn(Overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.BALL_FETCH);
vi.spyOn(Overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(60);
});
it("should keep hidden ability after evolving", async () => {
await game.runToSummon([Species.EEVEE, Species.TRAPINCH]);
const eevee = game.scene.getParty()[0];
const trapinch = game.scene.getParty()[1];
eevee.abilityIndex = 2;
trapinch.abilityIndex = 2;
eevee.evolve(pokemonEvolutions[Species.EEVEE][6], eevee.getSpeciesForm());
expect(eevee.abilityIndex).toBe(2);
trapinch.evolve(pokemonEvolutions[Species.TRAPINCH][0], trapinch.getSpeciesForm());
expect(trapinch.abilityIndex).toBe(1);
}, TIMEOUT);
it("should keep same ability slot after evolving", async () => {
await game.runToSummon([Species.BULBASAUR, Species.CHARMANDER]);
const bulbasaur = game.scene.getParty()[0];
const charmander = game.scene.getParty()[1];
bulbasaur.abilityIndex = 0;
charmander.abilityIndex = 1;
bulbasaur.evolve(pokemonEvolutions[Species.BULBASAUR][0], bulbasaur.getSpeciesForm());
expect(bulbasaur.abilityIndex).toBe(0);
charmander.evolve(pokemonEvolutions[Species.CHARMANDER][0], charmander.getSpeciesForm());
expect(charmander.abilityIndex).toBe(1);
}, TIMEOUT);
it("should handle illegal abilityIndex values", async () => {
await game.runToSummon([Species.SQUIRTLE]);
const squirtle = game.scene.getPlayerPokemon();
squirtle.abilityIndex = 5;
squirtle.evolve(pokemonEvolutions[Species.SQUIRTLE][0], squirtle.getSpeciesForm());
expect(squirtle.abilityIndex).toBe(0);
}, TIMEOUT);
it("should handle nincada's unique evolution", async () => {
await game.runToSummon([Species.NINCADA]);
const nincada = game.scene.getPlayerPokemon();
nincada.abilityIndex = 2;
nincada.evolve(pokemonEvolutions[Species.NINCADA][0], nincada.getSpeciesForm());
const ninjask = game.scene.getParty()[0];
const shedinja = game.scene.getParty()[1];
expect(ninjask.abilityIndex).toBe(2);
expect(shedinja.abilityIndex).toBe(1);
}, TIMEOUT);
});

View File

@ -1574,9 +1574,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
break;
}
} else if (newAbilityIndex === 1) {
if (abilityAttr & (this.lastSpecies.ability2 ? AbilityAttr.ABILITY_2 : AbilityAttr.ABILITY_HIDDEN)) {
break;
if (this.lastSpecies.ability1 === this.lastSpecies.ability2) {
newAbilityIndex = (newAbilityIndex + 1) % abilityCount;
}
break;
} else {
if (abilityAttr & AbilityAttr.ABILITY_HIDDEN) {
break;