Obstruct et al no longer protect against status moves

This commit is contained in:
NightKev 2024-09-08 03:26:24 -07:00
parent 7288350d45
commit 6da40f3e4a
3 changed files with 91 additions and 17 deletions

View File

@ -1180,6 +1180,8 @@ export class ProtectedTag extends BattlerTag {
}
}
export class DamageProtectedTag extends ProtectedTag {}
export class ContactDamageProtectedTag extends ProtectedTag {
private damageRatio: number;
@ -1215,7 +1217,7 @@ export class ContactDamageProtectedTag extends ProtectedTag {
}
}
export class ContactStatStageChangeProtectedTag extends ProtectedTag {
export class ContactStatStageChangeProtectedTag extends DamageProtectedTag {
private stat: BattleStat;
private levels: number;
@ -1271,7 +1273,7 @@ export class ContactPoisonProtectedTag extends ProtectedTag {
}
}
export class ContactBurnProtectedTag extends ProtectedTag {
export class ContactBurnProtectedTag extends DamageProtectedTag {
constructor(sourceMove: Moves) {
super(sourceMove, BattlerTagType.BURNING_BULWARK);
}

View File

@ -1,18 +1,18 @@
import BattleScene from "#app/battle-scene.js";
import { BattlerIndex } from "#app/battle.js";
import { applyPreAttackAbAttrs, AddSecondStrikeAbAttr, IgnoreMoveEffectsAbAttr, applyPostDefendAbAttrs, PostDefendAbAttr, applyPostAttackAbAttrs, PostAttackAbAttr, MaxMultiHitAbAttr, AlwaysHitAbAttr } from "#app/data/ability.js";
import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag.js";
import { MoveAnim } from "#app/data/battle-anims.js";
import { BattlerTagLapseType, ProtectedTag, SemiInvulnerableTag } from "#app/data/battler-tags.js";
import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr } from "#app/data/move.js";
import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms.js";
import { BattlerTagType } from "#app/enums/battler-tag-type.js";
import { Moves } from "#app/enums/moves.js";
import Pokemon, { PokemonMove, MoveResult, HitResult } from "#app/field/pokemon.js";
import { getPokemonNameWithAffix } from "#app/messages.js";
import { PokemonMultiHitModifier, FlinchChanceModifier, EnemyAttackStatusEffectChanceModifier, ContactHeldItemTransferChanceModifier, HitHealModifier } from "#app/modifier/modifier.js";
import BattleScene from "#app/battle-scene";
import { BattlerIndex } from "#app/battle";
import { applyPreAttackAbAttrs, AddSecondStrikeAbAttr, IgnoreMoveEffectsAbAttr, applyPostDefendAbAttrs, PostDefendAbAttr, applyPostAttackAbAttrs, PostAttackAbAttr, MaxMultiHitAbAttr, AlwaysHitAbAttr } from "#app/data/ability";
import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag";
import { MoveAnim } from "#app/data/battle-anims";
import { BattlerTagLapseType, DamageProtectedTag, ProtectedTag, SemiInvulnerableTag } from "#app/data/battler-tags";
import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr } from "#app/data/move";
import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms";
import { BattlerTagType } from "#app/enums/battler-tag-type";
import { Moves } from "#app/enums/moves";
import Pokemon, { PokemonMove, MoveResult, HitResult } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages";
import { PokemonMultiHitModifier, FlinchChanceModifier, EnemyAttackStatusEffectChanceModifier, ContactHeldItemTransferChanceModifier, HitHealModifier } from "#app/modifier/modifier";
import i18next from "i18next";
import * as Utils from "#app/utils.js";
import * as Utils from "#app/utils";
import { PokemonPhase } from "./pokemon-phase";
export class MoveEffectPhase extends PokemonPhase {
@ -152,7 +152,8 @@ export class MoveEffectPhase extends PokemonPhase {
/** Is the target protected by Protect, etc. or a relevant conditional protection effect? */
const isProtected = (bypassIgnoreProtect.value || !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target))
&& (hasConditionalProtectApplied.value || target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType)));
&& (hasConditionalProtectApplied.value || (!target.findTags(t => t instanceof DamageProtectedTag).length && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType)))
|| (this.move.getMove().category !== MoveCategory.STATUS && target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType))));
/** Does this phase represent the invoked move's first strike? */
const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount);

View File

@ -0,0 +1,71 @@
import { Moves } from "#app/enums/moves";
import { Stat } from "#app/enums/stat.js";
import { Abilities } from "#enums/abilities";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Moves - Obstruct", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
const TIMEOUT = 20 * 1000;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.battleType("single")
.enemyAbility(Abilities.BALL_FETCH)
.ability(Abilities.BALL_FETCH)
.moveset([Moves.OBSTRUCT]);
});
it("protects from contact damaging moves and lowers the opponent's defense by 2 stages", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.ICE_PUNCH));
await game.classicMode.startBattle();
game.move.select(Moves.OBSTRUCT);
await game.phaseInterceptor.to("BerryPhase");
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
expect(player.isFullHp()).toBe(true);
expect(enemy.getStatStage(Stat.DEF)).toBe(-2);
}, TIMEOUT);
it("protects from non-contact damaging moves and doesn't lower the opponent's defense by 2 stages", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.WATER_GUN));
await game.classicMode.startBattle();
game.move.select(Moves.OBSTRUCT);
await game.phaseInterceptor.to("BerryPhase");
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
expect(player.isFullHp()).toBe(true);
expect(enemy.getStatStage(Stat.DEF)).toBe(0);
}, TIMEOUT);
it("doesn't protect from status moves", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.GROWL));
await game.classicMode.startBattle();
game.move.select(Moves.OBSTRUCT);
await game.phaseInterceptor.to("BerryPhase");
const player = game.scene.getPlayerPokemon()!;
expect(player.getStatStage(Stat.ATK)).toBe(-1);
}, TIMEOUT);
});