[Bug] Fix scrappy (+ some immunity move and ability) in inverse battle (#4067)

* fix scrappy + etc. update inverse battle test code

* update test code following request from swain
This commit is contained in:
Leo Kim 2024-09-08 13:37:14 +09:00 committed by frutescens
parent b73fd97760
commit 31fcbf49f4
2 changed files with 126 additions and 41 deletions

View File

@ -1427,22 +1427,26 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
let multiplier = types.map(defType => {
const multiplier = new Utils.NumberHolder(getTypeDamageMultiplier(moveType, defType));
applyChallenges(this.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, multiplier);
if (source) {
const ignoreImmunity = new Utils.BooleanHolder(false);
if (source.isActive(true) && source.hasAbilityWithAttr(IgnoreTypeImmunityAbAttr)) {
applyAbAttrs(IgnoreTypeImmunityAbAttr, source, ignoreImmunity, simulated, moveType, defType);
}
if (ignoreImmunity.value) {
return 1;
if (multiplier.value === 0) {
return 1;
}
}
const exposedTags = this.findTags(tag => tag instanceof ExposedTag) as ExposedTag[];
if (exposedTags.some(t => t.ignoreImmunity(defType, moveType))) {
return 1;
if (multiplier.value === 0) {
return 1;
}
}
}
const multiplier = new Utils.NumberHolder(getTypeDamageMultiplier(moveType, defType));
applyChallenges(this.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, multiplier);
return multiplier.value;
}).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier;

View File

@ -1,8 +1,5 @@
import { BattlerIndex } from "#app/battle";
import { allMoves } from "#app/data/move";
import { Type } from "#app/data/type";
import { MoveEndPhase } from "#app/phases/move-end-phase";
import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { Abilities } from "#enums/abilities";
import { ArenaTagType } from "#enums/arena-tag-type";
import { Challenges } from "#enums/challenges";
@ -11,7 +8,8 @@ import { Species } from "#enums/species";
import { StatusEffect } from "#enums/status-effect";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const TIMEOUT = 20 * 1000;
@ -39,43 +37,63 @@ describe("Inverse Battle", () => {
.starterSpecies(Species.FEEBAS)
.ability(Abilities.BALL_FETCH)
.enemySpecies(Species.MAGIKARP)
.enemyAbility(Abilities.BALL_FETCH);
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(SPLASH_ONLY);
});
it("1. immune types are 2x effective - Thunderbolt against Ground Type", async () => {
game.override.enemySpecies(Species.SANDSHREW);
it("Immune types are 2x effective - Thunderbolt against Ground Type", async () => {
game.override
.moveset([Moves.THUNDERBOLT])
.enemySpecies(Species.SANDSHREW);
await game.challengeMode.startBattle();
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.THUNDERBOLT])).toBe(2);
game.move.select(Moves.THUNDERBOLT);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MoveEffectPhase");
expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2);
}, TIMEOUT);
it("2. 2x effective types are 0.5x effective - Thunderbolt against Flying Type", async () => {
game.override.enemySpecies(Species.PIDGEY);
it("2x effective types are 0.5x effective - Thunderbolt against Flying Type", async () => {
game.override
.moveset([Moves.THUNDERBOLT])
.enemySpecies(Species.PIDGEY);
await game.challengeMode.startBattle();
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.THUNDERBOLT])).toBe(0.5);
game.move.select(Moves.THUNDERBOLT);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MoveEffectPhase");
expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(0.5);
}, TIMEOUT);
it("3. 0.5x effective types are 2x effective - Thunderbolt against Electric Type", async () => {
game.override.enemySpecies(Species.CHIKORITA);
it("0.5x effective types are 2x effective - Thunderbolt against Electric Type", async () => {
game.override
.moveset([Moves.THUNDERBOLT])
.enemySpecies(Species.CHIKORITA);
await game.challengeMode.startBattle();
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.THUNDERBOLT])).toBe(2);
game.move.select(Moves.THUNDERBOLT);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MoveEffectPhase");
expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2);
}, TIMEOUT);
it("4. Stealth Rock follows the inverse matchups - Stealth Rock against Charizard deals 1/32 of max HP", async () => {
it("Stealth Rock follows the inverse matchups - Stealth Rock against Charizard deals 1/32 of max HP", async () => {
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0);
game.override
.enemySpecies(Species.CHARIZARD)
@ -95,18 +113,24 @@ describe("Inverse Battle", () => {
expect(currentHp).toBeGreaterThan(maxHp * 31 / 32 - 1);
}, TIMEOUT);
it("5. Freeze Dry is 2x effective against Water Type like other Ice type Move - Freeze Dry against Squirtle", async () => {
game.override.enemySpecies(Species.SQUIRTLE);
it("Freeze Dry is 2x effective against Water Type like other Ice type Move - Freeze Dry against Squirtle", async () => {
game.override
.moveset([Moves.FREEZE_DRY])
.enemySpecies(Species.SQUIRTLE);
await game.challengeMode.startBattle();
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.FREEZE_DRY])).toBe(2);
game.move.select(Moves.FREEZE_DRY);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MoveEffectPhase");
expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2);
}, TIMEOUT);
it("6. Water Absorb should heal against water moves - Water Absorb against Water gun", async () => {
it("Water Absorb should heal against water moves - Water Absorb against Water gun", async () => {
game.override
.moveset([Moves.WATER_GUN])
.enemyAbility(Abilities.WATER_ABSORB);
@ -117,13 +141,12 @@ describe("Inverse Battle", () => {
enemy.hp = enemy.getMaxHp() - 1;
game.move.select(Moves.WATER_GUN);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEndPhase);
await game.phaseInterceptor.to("MoveEndPhase");
expect(enemy.hp).toBe(enemy.getMaxHp());
}, TIMEOUT);
it("7. Fire type does not get burned - Will-O-Wisp against Charmander", async () => {
it("Fire type does not get burned - Will-O-Wisp against Charmander", async () => {
game.override
.moveset([Moves.WILL_O_WISP])
.enemySpecies(Species.CHARMANDER);
@ -135,13 +158,12 @@ describe("Inverse Battle", () => {
game.move.select(Moves.WILL_O_WISP);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.move.forceHit();
await game.phaseInterceptor.to(MoveEndPhase);
await game.phaseInterceptor.to("MoveEndPhase");
expect(enemy.status?.effect).not.toBe(StatusEffect.BURN);
}, TIMEOUT);
it("8. Electric type does not get paralyzed - Nuzzle against Pikachu", async () => {
it("Electric type does not get paralyzed - Nuzzle against Pikachu", async () => {
game.override
.moveset([Moves.NUZZLE])
.enemySpecies(Species.PIKACHU)
@ -153,14 +175,30 @@ describe("Inverse Battle", () => {
game.move.select(Moves.NUZZLE);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEndPhase);
await game.phaseInterceptor.to("MoveEndPhase");
expect(enemy.status?.effect).not.toBe(StatusEffect.PARALYSIS);
}, TIMEOUT);
it("Ground type is not immune to Thunder Wave - Thunder Wave against Sandshrew", async () => {
game.override
.moveset([Moves.THUNDER_WAVE])
.enemySpecies(Species.SANDSHREW);
it("10. Anticipation should trigger on 2x effective moves - Anticipation against Thunderbolt", async () => {
await game.challengeMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
game.move.select(Moves.THUNDER_WAVE);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.move.forceHit();
await game.phaseInterceptor.to("MoveEndPhase");
expect(enemy.status?.effect).toBe(StatusEffect.PARALYSIS);
}, TIMEOUT);
it("Anticipation should trigger on 2x effective moves - Anticipation against Thunderbolt", async () => {
game.override
.moveset([Moves.THUNDERBOLT])
.enemySpecies(Species.SANDSHREW)
@ -171,7 +209,7 @@ describe("Inverse Battle", () => {
expect(game.scene.getEnemyPokemon()?.summonData.abilitiesApplied[0]).toBe(Abilities.ANTICIPATION);
}, TIMEOUT);
it("11. Conversion 2 should change the type to the resistive type - Conversion 2 against Dragonite", async () => {
it("Conversion 2 should change the type to the resistive type - Conversion 2 against Dragonite", async () => {
game.override
.moveset([Moves.CONVERSION_2])
.enemyMoveset([Moves.DRAGON_CLAW, Moves.DRAGON_CLAW, Moves.DRAGON_CLAW, Moves.DRAGON_CLAW]);
@ -183,21 +221,64 @@ describe("Inverse Battle", () => {
game.move.select(Moves.CONVERSION_2);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to(TurnEndPhase);
await game.phaseInterceptor.to("TurnEndPhase");
expect(player.getTypes()[0]).toBe(Type.DRAGON);
}, TIMEOUT);
it("12. Flying Press should be 0.25x effective against Grass + Dark Type - Flying Press against Meowscarada", async () => {
it("Flying Press should be 0.25x effective against Grass + Dark Type - Flying Press against Meowscarada", async () => {
game.override
.moveset([Moves.FLYING_PRESS])
.enemySpecies(Species.MEOWSCARADA);
await game.challengeMode.startBattle();
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.FLYING_PRESS])).toBe(0.25);
game.move.select(Moves.FLYING_PRESS);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MoveEffectPhase");
expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(0.25);
}, TIMEOUT);
it("Scrappy ability has no effect - Tackle against Ghost Type still 2x effective with Scrappy", async () => {
game.override
.moveset([Moves.TACKLE])
.ability(Abilities.SCRAPPY)
.enemySpecies(Species.GASTLY);
await game.challengeMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");
game.move.select(Moves.TACKLE);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MoveEffectPhase");
expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2);
}, TIMEOUT);
it("FORESIGHT has no effect - Tackle against Ghost Type still 2x effective with Foresight", async () => {
game.override
.moveset([Moves.FORESIGHT, Moves.TACKLE])
.enemySpecies(Species.GASTLY);
await game.challengeMode.startBattle();
const enemy = game.scene.getEnemyPokemon()!;
vi.spyOn(enemy, "getMoveEffectiveness");
game.move.select(Moves.FORESIGHT);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("TurnEndPhase");
game.move.select(Moves.TACKLE);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MoveEffectPhase");
expect(enemy.getMoveEffectiveness).toHaveLastReturnedWith(2);
}, TIMEOUT);
});