[Bug] Fix: Gravity tag removes flying type during damage calculation (#3670)
* add `hasTag` to arena * fix flying type damage calculation for grounded states Before the grounded state would make e.g. electric moves no more very-effective. This is invalid Co-authored-by: Tristan D Gant <tgant2017@outlook.com> * add tests for gravity v. ground v. electric * Update src/test/arena/arena_gravity.test.ts fix typo Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> * use `arena` instead of this.scene.arena * use `const arena = this.scene.arean` instead of destructoring * Apply suggestions from code review Co-authored-by: Mumble <171087428+frutescens@users.noreply.github.com> * fix es-lint --------- Co-authored-by: Tristan D Gant <tgant2017@outlook.com> Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> Co-authored-by: Mumble <171087428+frutescens@users.noreply.github.com>
This commit is contained in:
parent
6b21a777a1
commit
3a167610cf
|
@ -584,6 +584,10 @@ export class Arena {
|
|||
return this.getTagOnSide(tagType, ArenaTagSide.BOTH);
|
||||
}
|
||||
|
||||
hasTag(tagType: ArenaTagType) : boolean {
|
||||
return !!this.getTag(tagType);
|
||||
}
|
||||
|
||||
getTagOnSide(tagType: ArenaTagType | Constructor<ArenaTag>, side: ArenaTagSide): ArenaTag | undefined {
|
||||
return typeof(tagType) === "string"
|
||||
? this.tags.find(t => t.tagType === tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side))
|
||||
|
|
|
@ -978,12 +978,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
|
||||
// this.scene potentially can be undefined for a fainted pokemon in doubles
|
||||
// use optional chaining to avoid runtime errors
|
||||
if (forDefend && (this.getTag(GroundedTag) || this.scene?.arena.getTag(ArenaTagType.GRAVITY))) {
|
||||
const flyingIndex = types.indexOf(Type.FLYING);
|
||||
if (flyingIndex > -1) {
|
||||
types.splice(flyingIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!types.length) { // become UNKNOWN if no types are present
|
||||
types.push(Type.UNKNOWN);
|
||||
|
@ -1272,6 +1266,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
return this.isTerastallized() ? 2 : 1;
|
||||
}
|
||||
const types = this.getTypes(true, true);
|
||||
const arena = this.scene.arena;
|
||||
|
||||
// Handle flying v ground type immunity without removing flying type so effective types are still effective
|
||||
// Related to https://github.com/pagefaultgames/pokerogue/issues/524
|
||||
if (moveType === Type.GROUND && (this.isGrounded() || arena.hasTag(ArenaTagType.GRAVITY))) {
|
||||
const flyingIndex = types.indexOf(Type.FLYING);
|
||||
if (flyingIndex > -1) {
|
||||
types.splice(flyingIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
let multiplier = types.map(defType => {
|
||||
if (source) {
|
||||
|
@ -1293,7 +1297,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier;
|
||||
|
||||
// Handle strong winds lowering effectiveness of types super effective against pure flying
|
||||
if (!ignoreStrongWinds && this.scene.arena.weather?.weatherType === WeatherType.STRONG_WINDS && !this.scene.arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) {
|
||||
if (!ignoreStrongWinds && arena.weather?.weatherType === WeatherType.STRONG_WINDS && !arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) {
|
||||
multiplier /= 2;
|
||||
if (!simulated) {
|
||||
this.scene.queueMessage(i18next.t("weather:strongWindsEffectMessage"));
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import { allMoves } from "#app/data/move.js";
|
||||
import { Abilities } from "#app/enums/abilities.js";
|
||||
import { ArenaTagType } from "#app/enums/arena-tag-type.js";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
import { allMoves } from "#app/data/move";
|
||||
import { Abilities } from "#app/enums/abilities";
|
||||
import { ArenaTagType } from "#app/enums/arena-tag-type";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { getMovePosition } from "#test/utils/gameManagerUtils";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase.js";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase.js";
|
||||
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||
|
||||
describe("Arena - Gravity", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
|
@ -26,14 +27,17 @@ describe("Arena - Gravity", () => {
|
|||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override.battleType("single");
|
||||
game.override.moveset([Moves.TACKLE, Moves.GRAVITY, Moves.FISSURE]);
|
||||
game.override.ability(Abilities.UNNERVE);
|
||||
game.override.enemyAbility(Abilities.BALL_FETCH);
|
||||
game.override.enemySpecies(Species.SHUCKLE);
|
||||
game.override.enemyMoveset(new Array(4).fill(Moves.SPLASH));
|
||||
game.override
|
||||
.battleType("single")
|
||||
.moveset([Moves.TACKLE, Moves.GRAVITY, Moves.FISSURE])
|
||||
.ability(Abilities.UNNERVE)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemySpecies(Species.SHUCKLE)
|
||||
.enemyMoveset(SPLASH_ONLY);
|
||||
});
|
||||
|
||||
// Reference: https://bulbapedia.bulbagarden.net/wiki/Gravity_(move)
|
||||
|
||||
it("non-OHKO move accuracy is multiplied by 1.67", async () => {
|
||||
const moveToCheck = allMoves[Moves.TACKLE];
|
||||
|
||||
|
@ -77,4 +81,65 @@ describe("Arena - Gravity", () => {
|
|||
|
||||
expect(moveToCheck.calculateBattleAccuracy).toHaveReturnedWith(30);
|
||||
});
|
||||
|
||||
describe("Against flying types", () => {
|
||||
it("can be hit by ground-type moves now", async () => {
|
||||
game.override
|
||||
.startingLevel(5)
|
||||
.enemyLevel(5)
|
||||
.enemySpecies(Species.PIDGEOT)
|
||||
.moveset([Moves.GRAVITY, Moves.EARTHQUAKE]);
|
||||
|
||||
await game.startBattle([Species.PIKACHU]);
|
||||
|
||||
const pidgeot = game.scene.getEnemyPokemon()!;
|
||||
vi.spyOn(pidgeot, "getAttackTypeEffectiveness");
|
||||
|
||||
// Try earthquake on 1st turn (fails!);
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(0);
|
||||
|
||||
// Setup Gravity on 2nd turn
|
||||
await game.toNextTurn();
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.GRAVITY));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined();
|
||||
|
||||
// Use ground move on 3rd turn
|
||||
await game.toNextTurn();
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(1);
|
||||
});
|
||||
|
||||
it("keeps super-effective moves super-effective after using gravity", async () => {
|
||||
game.override
|
||||
.startingLevel(5)
|
||||
.enemyLevel(5)
|
||||
.enemySpecies(Species.PIDGEOT)
|
||||
.moveset([Moves.GRAVITY, Moves.THUNDERBOLT]);
|
||||
|
||||
await game.startBattle([Species.PIKACHU]);
|
||||
|
||||
const pidgeot = game.scene.getEnemyPokemon()!;
|
||||
vi.spyOn(pidgeot, "getAttackTypeEffectiveness");
|
||||
|
||||
// Setup Gravity on 1st turn
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.GRAVITY));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(game.scene.arena.getTag(ArenaTagType.GRAVITY)).toBeDefined();
|
||||
|
||||
// Use electric move on 2nd turn
|
||||
await game.toNextTurn();
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT));
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(pidgeot.getAttackTypeEffectiveness).toHaveReturnedWith(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue