Merge 836964300d
into fe69bd2b55
This commit is contained in:
commit
4e596dfe77
|
@ -8,7 +8,7 @@ import { Constructor } from "#app/utils";
|
|||
import * as Utils from "../utils";
|
||||
import { WeatherType } from "./weather";
|
||||
import { ArenaTagSide, ArenaTrapTag, WeakenMoveTypeTag } from "./arena-tag";
|
||||
import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, BlockItemTheftAbAttr, applyPostAttackAbAttrs, ConfusionOnStatusEffectAbAttr, HealFromBerryUseAbAttr, IgnoreProtectOnContactAbAttr, IgnoreMoveEffectsAbAttr, applyPreDefendAbAttrs, MoveEffectChanceMultiplierAbAttr, WonderSkinAbAttr, applyPreAttackAbAttrs, MoveTypeChangeAbAttr, UserFieldMoveTypePowerBoostAbAttr, FieldMoveTypePowerBoostAbAttr, AllyMoveCategoryPowerBoostAbAttr, VariableMovePowerAbAttr } from "./ability";
|
||||
import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, BlockItemTheftAbAttr, applyPostAttackAbAttrs, ConfusionOnStatusEffectAbAttr, HealFromBerryUseAbAttr, IgnoreProtectOnContactAbAttr, IgnoreMoveEffectsAbAttr, applyPreDefendAbAttrs, MoveEffectChanceMultiplierAbAttr, WonderSkinAbAttr, applyPreAttackAbAttrs, MoveTypeChangeAbAttr, UserFieldMoveTypePowerBoostAbAttr, FieldMoveTypePowerBoostAbAttr, AllyMoveCategoryPowerBoostAbAttr, VariableMovePowerAbAttr, ProtectStatAbAttr } from "./ability";
|
||||
import { allAbilities } from "./ability";
|
||||
import { PokemonHeldItemModifier, BerryModifier, PreserveBerryModifier, PokemonMoveAccuracyBoosterModifier, AttackTypeBoosterModifier, PokemonMultiHitModifier } from "../modifier/modifier";
|
||||
import { BattlerIndex, BattleType } from "../battle";
|
||||
|
@ -5180,7 +5180,6 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||
const switchOutTarget = this.user ? user : target;
|
||||
if (switchOutTarget instanceof PlayerPokemon) {
|
||||
switchOutTarget.leaveField(!this.batonPass);
|
||||
|
||||
if (switchOutTarget.hp > 0) {
|
||||
user.scene.prependToPhase(new SwitchPhase(user.scene, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase);
|
||||
resolve(true);
|
||||
|
@ -5276,6 +5275,45 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attr used by parting shot, it is a combo of ForceSwitchOut and StatChange with a special getCondition()
|
||||
*/
|
||||
export class PartingShotAttr extends ForceSwitchOutAttr {
|
||||
private statChange = new StatStageChangeAttr([ Stat.ATK, Stat.SPATK ], -1, false, null, true, true, MoveEffectTrigger.PRE_APPLY);
|
||||
private canLowerStats = true;
|
||||
|
||||
// using inherited constructor
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
||||
// apply stat change, conditionally apply switch-out
|
||||
this.statChange.apply(user, target, move, args);
|
||||
if (this.canLowerStats) {
|
||||
return super.apply(user, target, move, args);
|
||||
} else {
|
||||
return new Promise(resolve => {
|
||||
resolve(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getCondition(): MoveConditionFunc {
|
||||
return (user, target, move) => {
|
||||
// conditions on if move should fail or not don't depend on if user is able to switch
|
||||
// getCondition() is called before move is applied: move will only switch out if canLowerStats === true
|
||||
if (target.hasAbilityWithAttr(ProtectStatAbAttr) ||
|
||||
(target.getStatStage(Stat.ATK) === -6 && target.getStatStage(Stat.SPATK) === -6 && !target.hasAbility(Abilities.CONTRARY, true, false)) ||
|
||||
target.scene.arena.findTagsOnSide(t => t.tagType === ArenaTagType.MIST, ArenaTagSide.ENEMY).length > 0 ||
|
||||
(target.getStatStage(Stat.ATK) === 6 && target.getStatStage(Stat.SPATK) === 6 && target.hasAbility(Abilities.CONTRARY, true, false))
|
||||
) {
|
||||
this.canLowerStats = false;
|
||||
} else {
|
||||
this.canLowerStats = true;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class RemoveTypeAttr extends MoveEffectAttr {
|
||||
|
||||
private removedType: Type;
|
||||
|
@ -8380,8 +8418,7 @@ export function initMoves() {
|
|||
.soundBased()
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||
new StatusMove(Moves.PARTING_SHOT, Type.DARK, 100, 20, -1, 0, 6)
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1, false, null, true, true, MoveEffectTrigger.PRE_APPLY)
|
||||
.attr(ForceSwitchOutAttr, true, false)
|
||||
.attr(PartingShotAttr, true, false)
|
||||
.soundBased(),
|
||||
new StatusMove(Moves.TOPSY_TURVY, Type.DARK, -1, 20, -1, 0, 6)
|
||||
.attr(InvertStatsAttr),
|
||||
|
|
|
@ -2,7 +2,7 @@ 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, it, test } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||
import GameManager from "../utils/gameManager";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { BerryPhase } from "#app/phases/berry-phase";
|
||||
|
@ -76,8 +76,8 @@ describe("Moves - Parting Shot", () => {
|
|||
}, TIMEOUT
|
||||
);
|
||||
|
||||
it.skip( // TODO: fix this bug to pass the test!
|
||||
"Parting shot should fail if target is -6/-6 de-buffed",
|
||||
test(
|
||||
"Parting shot should not switch out if target is -6/-6 de-buffed",
|
||||
async () => {
|
||||
game.override.moveset([Moves.PARTING_SHOT, Moves.MEMENTO, Moves.SPLASH]);
|
||||
await game.startBattle([Species.MEOWTH, Species.MEOWTH, Species.MEOWTH, Species.MURKROW, Species.ABRA]);
|
||||
|
@ -118,7 +118,7 @@ describe("Moves - Parting Shot", () => {
|
|||
}, TIMEOUT
|
||||
);
|
||||
|
||||
it.skip( // TODO: fix this bug to pass the test!
|
||||
test(
|
||||
"Parting shot shouldn't allow switch out when mist is active",
|
||||
async () => {
|
||||
game.override
|
||||
|
@ -135,11 +135,11 @@ describe("Moves - Parting Shot", () => {
|
|||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MURKROW);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.SNORLAX);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
it.skip( // TODO: fix this bug to pass the test!
|
||||
test(
|
||||
"Parting shot shouldn't allow switch out against clear body ability",
|
||||
async () => {
|
||||
game.override
|
||||
|
@ -155,13 +155,81 @@ describe("Moves - Parting Shot", () => {
|
|||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MURKROW);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.SNORLAX);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
it.skip( // TODO: fix this bug to pass the test!
|
||||
test(
|
||||
"Parting shot shouldn't allow switch out against white smoke ability",
|
||||
async () => {
|
||||
game.override
|
||||
.enemySpecies(Species.TORKOAL)
|
||||
.enemyAbility(Abilities.WHITE_SMOKE);
|
||||
await game.startBattle([Species.SNORLAX, Species.MEOWTH]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
expect(enemyPokemon).toBeDefined();
|
||||
|
||||
game.move.select(Moves.PARTING_SHOT);
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.SNORLAX);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
||||
test(
|
||||
"Parting shot should not switch out if target is +6/+6 buffed and has contrary ability",
|
||||
async () => {
|
||||
game.override
|
||||
.enemySpecies(Species.SHUCKLE)
|
||||
.enemyAbility(Abilities.CONTRARY)
|
||||
.enemyMoveset(Array(4).fill(Moves.SPLASH));
|
||||
|
||||
game.override.moveset([Moves.PARTING_SHOT, Moves.MEMENTO, Moves.SPLASH]);
|
||||
await game.startBattle([Species.MEOWTH, Species.MEOWTH, Species.MEOWTH, Species.MURKROW, Species.ABRA]);
|
||||
|
||||
// use Memento 3 times to buff enemy
|
||||
game.move.select(Moves.MEMENTO);
|
||||
await game.phaseInterceptor.to(FaintPhase);
|
||||
expect(game.scene.getParty()[0].isFainted()).toBe(true);
|
||||
game.doSelectPartyPokemon(1);
|
||||
|
||||
await game.phaseInterceptor.to(TurnInitPhase, false);
|
||||
game.move.select(Moves.MEMENTO);
|
||||
await game.phaseInterceptor.to(FaintPhase);
|
||||
expect(game.scene.getParty()[0].isFainted()).toBe(true);
|
||||
game.doSelectPartyPokemon(2);
|
||||
|
||||
await game.phaseInterceptor.to(TurnInitPhase, false);
|
||||
game.move.select(Moves.MEMENTO);
|
||||
await game.phaseInterceptor.to(FaintPhase);
|
||||
expect(game.scene.getParty()[0].isFainted()).toBe(true);
|
||||
game.doSelectPartyPokemon(3);
|
||||
|
||||
// set up done - enemy should be at +6/+6
|
||||
await game.phaseInterceptor.to(TurnInitPhase, false);
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
expect(enemyPokemon).toBeDefined();
|
||||
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(6);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(6);
|
||||
|
||||
// now parting shot should fail
|
||||
game.move.select(Moves.PARTING_SHOT);
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(6);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(6);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MURKROW);
|
||||
}, TIMEOUT
|
||||
);
|
||||
test(
|
||||
"Parting shot should de-buff and not fail if no party available to switch - party size 1",
|
||||
async () => {
|
||||
game.override
|
||||
.enemySpecies(Species.MAGIKARP);
|
||||
await game.startBattle([Species.MURKROW]);
|
||||
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
@ -176,9 +244,11 @@ describe("Moves - Parting Shot", () => {
|
|||
}, TIMEOUT
|
||||
);
|
||||
|
||||
it.skip( // TODO: fix this bug to pass the test!
|
||||
"Parting shot regularly not fail if no party available to switch - party fainted",
|
||||
test(
|
||||
"Parting shot shouldn't fail if no party available to switch - party fainted",
|
||||
async () => {
|
||||
game.override
|
||||
.enemySpecies(Species.MAGIKARP);
|
||||
await game.startBattle([Species.MURKROW, Species.MEOWTH]);
|
||||
game.move.select(Moves.SPLASH);
|
||||
|
||||
|
@ -193,8 +263,8 @@ describe("Moves - Parting Shot", () => {
|
|||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-1);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MEOWTH);
|
||||
}, TIMEOUT
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue