[Bug] Fix Thousand Arrows not hitting targets under the effects of Magnet Rise (#3100)
* Fix Thousand Arrows not hitting through Magnet Rise * Add integration test for Thousand Arrows vs. Magnet Rise * ESLint * Remove unnecessary checks in integration tests
This commit is contained in:
parent
630deb92e5
commit
1d39f8d638
|
@ -7531,6 +7531,7 @@ export function initMoves() {
|
|||
.attr(NeutralDamageAgainstFlyingTypeMultiplierAttr)
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true)
|
||||
.attr(HitsTagAttr, BattlerTagType.FLYING, false)
|
||||
.attr(HitsTagAttr, BattlerTagType.MAGNET_RISEN, false)
|
||||
.attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED)
|
||||
.attr(RemoveBattlerTagAttr, [BattlerTagType.FLYING, BattlerTagType.MAGNET_RISEN])
|
||||
.makesContact(false)
|
||||
|
|
|
@ -1170,7 +1170,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
getAttackMoveEffectiveness(source: Pokemon, pokemonMove: PokemonMove, ignoreAbility: boolean = false): TypeDamageMultiplier {
|
||||
const move = pokemonMove.getMove();
|
||||
const typeless = move.hasAttr(TypelessAttr);
|
||||
const typeMultiplier = new Utils.NumberHolder(this.getAttackTypeEffectiveness(move.type, source));
|
||||
const typeMultiplier = new Utils.NumberHolder(this.getAttackTypeEffectiveness(move, source));
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier);
|
||||
if (!typeless && !ignoreAbility) {
|
||||
|
@ -1185,13 +1185,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
|
||||
/**
|
||||
* Calculates the type effectiveness multiplier for an attack type
|
||||
* @param moveType Type of the move
|
||||
* @param moveOrType The move being used, or a type if the move is unknown
|
||||
* @param source the Pokemon using the move
|
||||
* @param ignoreStrongWinds whether or not this ignores strong winds (anticipation, forewarn, stealth rocks)
|
||||
* @param simulated tag to only apply the strong winds effect message when the move is used
|
||||
* @returns a multiplier for the type effectiveness
|
||||
*/
|
||||
getAttackTypeEffectiveness(moveType: Type, source?: Pokemon, ignoreStrongWinds: boolean = false, simulated: boolean = true): TypeDamageMultiplier {
|
||||
getAttackTypeEffectiveness(moveOrType: Move | Type, source?: Pokemon, ignoreStrongWinds: boolean = false, simulated: boolean = true): TypeDamageMultiplier {
|
||||
const move = (moveOrType instanceof Move)
|
||||
? moveOrType
|
||||
: undefined;
|
||||
const moveType = (moveOrType instanceof Move)
|
||||
? move.type
|
||||
: moveOrType;
|
||||
|
||||
if (moveType === Type.STELLAR) {
|
||||
return this.isTerastallized() ? 2 : 1;
|
||||
}
|
||||
|
@ -1219,9 +1226,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
}
|
||||
|
||||
if (!!this.summonData?.tags.find((tag) => tag instanceof TypeImmuneTag && tag.immuneType === moveType)) {
|
||||
multiplier = 0;
|
||||
}
|
||||
const immuneTags = this.findTags(tag => tag instanceof TypeImmuneTag && tag.immuneType === moveType);
|
||||
immuneTags.forEach(tag => {
|
||||
if (move !== undefined) {
|
||||
const hitsTagAttrs = move.getAttrs(HitsTagAttr).filter(attr => attr.tagType === tag.tagType);
|
||||
if (hitsTagAttrs.length === 0) {
|
||||
multiplier = 0;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return multiplier;
|
||||
}
|
||||
|
@ -1806,7 +1819,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
const cancelled = new Utils.BooleanHolder(false);
|
||||
const typeless = move.hasAttr(TypelessAttr);
|
||||
const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveCategory.STATUS || move.getAttrs(StatusMoveTypeImmunityAttr).find(attr => types.includes(attr.immuneType)))
|
||||
? this.getAttackTypeEffectiveness(move.type, source, false, false)
|
||||
? this.getAttackTypeEffectiveness(move, source, false, false)
|
||||
: 1);
|
||||
applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier);
|
||||
if (typeless) {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import {afterEach, beforeAll, beforeEach, describe, expect, test, vi} from "vitest";
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import overrides from "#app/overrides";
|
||||
import {
|
||||
MoveEffectPhase,
|
||||
TurnEndPhase
|
||||
BerryPhase,
|
||||
MoveEffectPhase
|
||||
} from "#app/phases";
|
||||
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
|
||||
import { Moves } from "#enums/moves";
|
||||
|
@ -38,18 +38,12 @@ describe("Moves - Thousand Arrows", () => {
|
|||
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH,Moves.SPLASH,Moves.SPLASH,Moves.SPLASH]);
|
||||
});
|
||||
|
||||
test(
|
||||
it(
|
||||
"move should hit and ground Flying-type targets",
|
||||
async () => {
|
||||
await game.startBattle([ Species.ILLUMISE ]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon();
|
||||
expect(leadPokemon).toBeDefined();
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon();
|
||||
expect(enemyPokemon).toBeDefined();
|
||||
|
||||
const enemyStartingHp = enemyPokemon.hp;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS));
|
||||
|
||||
|
@ -57,14 +51,14 @@ describe("Moves - Thousand Arrows", () => {
|
|||
// Enemy should not be grounded before move effect is applied
|
||||
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
|
||||
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||
expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp);
|
||||
expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp());
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
test(
|
||||
it(
|
||||
"move should hit and ground targets with Levitate",
|
||||
async () => {
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.SNORLAX);
|
||||
|
@ -72,13 +66,7 @@ describe("Moves - Thousand Arrows", () => {
|
|||
|
||||
await game.startBattle([ Species.ILLUMISE ]);
|
||||
|
||||
const leadPokemon = game.scene.getPlayerPokemon();
|
||||
expect(leadPokemon).toBeDefined();
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon();
|
||||
expect(enemyPokemon).toBeDefined();
|
||||
|
||||
const enemyStartingHp = enemyPokemon.hp;
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS));
|
||||
|
||||
|
@ -86,10 +74,31 @@ describe("Moves - Thousand Arrows", () => {
|
|||
// Enemy should not be grounded before move effect is applied
|
||||
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
|
||||
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||
expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp);
|
||||
expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp());
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
it(
|
||||
"move should hit and ground targets under the effects of Magnet Rise",
|
||||
async () => {
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.SNORLAX);
|
||||
|
||||
await game.startBattle([ Species.ILLUMISE ]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon();
|
||||
|
||||
enemyPokemon.addTag(BattlerTagType.MAGNET_RISEN, null, Moves.MAGNET_RISE);
|
||||
|
||||
game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS));
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
|
||||
expect(enemyPokemon.getTag(BattlerTagType.MAGNET_RISEN)).toBeUndefined();
|
||||
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||
expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp());
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue