Added checks for substitute in contact logic

This commit is contained in:
innerthunder 2024-08-15 16:57:45 -07:00
parent 3ca5453e48
commit 70516abb16
3 changed files with 56 additions and 15 deletions

View File

@ -599,7 +599,8 @@ export default class Move implements Localizable {
// special cases below, eg: if the move flag is MAKES_CONTACT, and the user pokemon has an ability that ignores contact (like "Long Reach"), then overrides and move does not make contact
switch (flag) {
case MoveFlags.MAKES_CONTACT:
if (user.hasAbilityWithAttr(IgnoreContactAbAttr)) {
if (user.hasAbilityWithAttr(IgnoreContactAbAttr) ||
(target?.getTag(BattlerTagType.SUBSTITUTE) && !this.canIgnoreSubstitute(user))) {
return false;
}
break;
@ -612,8 +613,9 @@ export default class Move implements Localizable {
}
}
case MoveFlags.IGNORE_PROTECT:
if (user.hasAbilityWithAttr(IgnoreProtectOnContactAbAttr) &&
this.checkFlag(MoveFlags.MAKES_CONTACT, user, target)) {
if (user.hasAbilityWithAttr(IgnoreProtectOnContactAbAttr)
&& this.hasFlag(MoveFlags.MAKES_CONTACT)
&& !user.hasAbilityWithAttr(IgnoreContactAbAttr)) {
return true;
}
}

View File

@ -1,11 +1,12 @@
import { TurnEndPhase } from "#app/phases.js";
import { BerryPhase, TurnEndPhase } from "#app/phases.js";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import GameManager from "#test/utils/gameManager";
import { getMovePosition } from "#test/utils/gameManagerUtils";
import { BattlerTagType } from "#app/enums/battler-tag-type.js";
const TIMEOUT = 20 * 1000;
@ -33,37 +34,57 @@ describe("Abilities - Unseen Fist", () => {
game.override.enemyLevel(100);
});
test(
"ability causes a contact move to ignore Protect",
it(
"should cause a contact move to ignore Protect",
() => testUnseenFistHitResult(game, Moves.QUICK_ATTACK, Moves.PROTECT, true),
TIMEOUT
);
test(
"ability does not cause a non-contact move to ignore Protect",
it(
"should not cause a non-contact move to ignore Protect",
() => testUnseenFistHitResult(game, Moves.ABSORB, Moves.PROTECT, false),
TIMEOUT
);
test(
"ability does not apply if the source has Long Reach",
it(
"should not apply if the source has Long Reach",
() => {
game.override.passiveAbility(Abilities.LONG_REACH);
testUnseenFistHitResult(game, Moves.QUICK_ATTACK, Moves.PROTECT, false);
}, TIMEOUT
);
test(
"ability causes a contact move to ignore Wide Guard",
it(
"should cause a contact move to ignore Wide Guard",
() => testUnseenFistHitResult(game, Moves.BREAKING_SWIPE, Moves.WIDE_GUARD, true),
TIMEOUT
);
test(
"ability does not cause a non-contact move to ignore Wide Guard",
it(
"should not cause a non-contact move to ignore Wide Guard",
() => testUnseenFistHitResult(game, Moves.BULLDOZE, Moves.WIDE_GUARD, false),
TIMEOUT
);
it(
"should cause a contact move to ignore Protect, but not Substitute",
async () => {
game.override.enemyLevel(1);
game.override.moveset([Moves.TACKLE]);
await game.startBattle();
const enemyPokemon = game.scene.getEnemyPokemon()!;
enemyPokemon.addTag(BattlerTagType.SUBSTITUTE, 0, Moves.NONE, enemyPokemon.id);
game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE));
await game.phaseInterceptor.to(BerryPhase, false);
expect(enemyPokemon.getTag(BattlerTagType.SUBSTITUTE)).toBeUndefined();
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp());
}, TIMEOUT
);
});
async function testUnseenFistHitResult(game: GameManager, attackMove: Moves, protectMove: Moves, shouldSucceed: boolean = true): Promise<void> {

View File

@ -406,4 +406,22 @@ describe("Moves - Substitute", () => {
expect(subTag.hp).toBe(Math.floor(leadPokemon.getMaxHp() * 1/4));
}, TIMEOUT
);
it (
"should prevent the source's Rough Skin from activating when hit",
async () => {
game.override.enemyMoveset(Array(4).fill(Moves.TACKLE));
game.override.ability(Abilities.ROUGH_SKIN);
await game.startBattle([Species.BLASTOISE]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
game.doAttack(getMovePosition(game.scene, 0, Moves.SUBSTITUTE));
await game.phaseInterceptor.to(BerryPhase, false);
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp());
}, TIMEOUT
);
});