From 07201ad8f4eb3d92b24f926f15ce49544559e807 Mon Sep 17 00:00:00 2001 From: DustinLin Date: Thu, 15 Aug 2024 13:01:37 -0400 Subject: [PATCH 1/7] correcting un-run test --- src/test/moves/parting_shot.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/moves/parting_shot.test.ts b/src/test/moves/parting_shot.test.ts index b8b0faba4ce..658d64a57dd 100644 --- a/src/test/moves/parting_shot.test.ts +++ b/src/test/moves/parting_shot.test.ts @@ -198,8 +198,8 @@ describe("Moves - Parting Shot", () => { await game.phaseInterceptor.to(BerryPhase, false); const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats; - expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); - expect(battleStatsOpponent[BattleStat.SPATK]).toBe(0); + expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1); + expect(battleStatsOpponent[BattleStat.SPATK]).toBe(-1); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MEOWTH); }, TIMEOUT ); From 91976fc0c2b3a2d67873f3a0b8f4bfdfc1fdcbfe Mon Sep 17 00:00:00 2001 From: DustinLin Date: Thu, 15 Aug 2024 13:55:08 -0400 Subject: [PATCH 2/7] implement partingshot to pass tests - TODO figure out dup msg queue --- src/data/move.ts | 47 +++++++++++++++++++++++++++-- src/test/moves/parting_shot.test.ts | 18 +++++------ 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index 0b0af00a370..c10df481c96 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -4877,6 +4877,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { // Check if the move category is not STATUS or if the switch out condition is not met if (!this.getSwitchOutCondition()(user, target, move)) { + console.log("switching out condition is false in the apply"); return resolve(false); } @@ -4885,6 +4886,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { const switchOutTarget = this.user ? user : target; if (switchOutTarget instanceof PlayerPokemon) { switchOutTarget.leaveField(!this.batonPass); + console.log("switching out , what isgoing on here"); if (switchOutTarget.hp > 0) { user.scene.prependToPhase(new SwitchPhase(user.scene, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase); @@ -4977,6 +4979,47 @@ 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: StatChangeAttr; + private canLowerStats: boolean; + + constructor(user?: boolean, batonPass?: boolean) { + super(user, batonPass); + this.statChange = new StatChangeAttr([ BattleStat.ATK, BattleStat.SPATK ], -1, false, null, true, true, MoveEffectTrigger.PRE_APPLY); + this.canLowerStats = true; + } + + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { + // 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 + if (target.hasAbility(Abilities.CLEAR_BODY, true, false) || + (target.summonData.battleStats[0] === -6 && target.summonData.battleStats[2] === -6) || target.scene.arena.findTagsOnSide(t => t.tagType === ArenaTagType.MIST, ArenaTagSide.ENEMY).length > 0 + ) { + this.canLowerStats = false; + } else { + this.canLowerStats = true; + } + return true; + }; + } +} + export class RemoveTypeAttr extends MoveEffectAttr { private removedType: Type; @@ -7760,9 +7803,9 @@ export function initMoves() { .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES), new StatusMove(Moves.PARTING_SHOT, Type.DARK, 100, 20, -1, 0, 6) - .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.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), new AttackMove(Moves.DRAINING_KISS, Type.FAIRY, MoveCategory.SPECIAL, 50, 100, 10, -1, 0, 6) diff --git a/src/test/moves/parting_shot.test.ts b/src/test/moves/parting_shot.test.ts index 658d64a57dd..2f1cb270fe0 100644 --- a/src/test/moves/parting_shot.test.ts +++ b/src/test/moves/parting_shot.test.ts @@ -4,7 +4,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, test, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import GameManager from "../utils/gameManager"; import { getMovePosition } from "../utils/gameManagerUtils"; import { BattleStat } from "#app/data/battle-stat"; @@ -77,8 +77,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]); @@ -120,7 +120,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 @@ -138,11 +138,11 @@ describe("Moves - Parting Shot", () => { const battleStatsOpponent = enemyPokemon.summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); expect(battleStatsOpponent[BattleStat.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 @@ -159,11 +159,11 @@ describe("Moves - Parting Shot", () => { const battleStatsOpponent = enemyPokemon.summonData.battleStats; expect(battleStatsOpponent[BattleStat.ATK]).toBe(0); expect(battleStatsOpponent[BattleStat.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 should de-buff and not fail if no party available to switch - party size 1", async () => { await game.startBattle([Species.MURKROW]); @@ -181,7 +181,7 @@ describe("Moves - Parting Shot", () => { }, TIMEOUT ); - it.skip( // TODO: fix this bug to pass the test! + test( "Parting shot regularly not fail if no party available to switch - party fainted", async () => { await game.startBattle([Species.MURKROW, Species.MEOWTH]); From af52a01692049350806163837b49460baac79c84 Mon Sep 17 00:00:00 2001 From: DustinLin Date: Fri, 16 Aug 2024 20:36:57 -0400 Subject: [PATCH 3/7] adding comments, dialogue is expected behavior --- src/data/move.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/move.ts b/src/data/move.ts index c10df481c96..190250bab66 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -5007,7 +5007,7 @@ export class PartingShotAttr extends ForceSwitchOutAttr { 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 + // getCondition() is called before move is applied: move will only switch out if canLowerStats === true if (target.hasAbility(Abilities.CLEAR_BODY, true, false) || (target.summonData.battleStats[0] === -6 && target.summonData.battleStats[2] === -6) || target.scene.arena.findTagsOnSide(t => t.tagType === ArenaTagType.MIST, ArenaTagSide.ENEMY).length > 0 ) { From 57ea31bd23a58d117c73422cfcdf544ff09af1f9 Mon Sep 17 00:00:00 2001 From: DustinLin Date: Fri, 16 Aug 2024 20:48:01 -0400 Subject: [PATCH 4/7] cleaning up --- src/data/move.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index 190250bab66..2675f1c40dd 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -4877,7 +4877,6 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { // Check if the move category is not STATUS or if the switch out condition is not met if (!this.getSwitchOutCondition()(user, target, move)) { - console.log("switching out condition is false in the apply"); return resolve(false); } @@ -4886,8 +4885,6 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { const switchOutTarget = this.user ? user : target; if (switchOutTarget instanceof PlayerPokemon) { switchOutTarget.leaveField(!this.batonPass); - console.log("switching out , what isgoing on here"); - if (switchOutTarget.hp > 0) { user.scene.prependToPhase(new SwitchPhase(user.scene, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase); resolve(true); @@ -7805,7 +7802,6 @@ export function initMoves() { new StatusMove(Moves.PARTING_SHOT, Type.DARK, 100, 20, -1, 0, 6) .attr(PartingShotAttr, true, false) .soundBased(), - new StatusMove(Moves.TOPSY_TURVY, Type.DARK, -1, 20, -1, 0, 6) .attr(InvertStatsAttr), new AttackMove(Moves.DRAINING_KISS, Type.FAIRY, MoveCategory.SPECIAL, 50, 100, 10, -1, 0, 6) From ec8c40066e53f4babbf85ee0b1ff066218c154cf Mon Sep 17 00:00:00 2001 From: DustinLin Date: Fri, 6 Sep 2024 09:07:33 -0700 Subject: [PATCH 5/7] refactor edits --- src/data/move.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index 5301f377cfb..1294a135bc4 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -5126,14 +5126,10 @@ 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: StatStageChangeAttr; - private canLowerStats: boolean; + private statChange = new StatStageChangeAttr([ Stat.ATK, Stat.SPATK ], -1, false, null, true, true, MoveEffectTrigger.PRE_APPLY); + private canLowerStats = true; - constructor(user?: boolean, batonPass?: boolean) { - super(user, batonPass); - this.statChange = new StatStageChangeAttr([ Stat.ATK, Stat.SPATK ], -1, false, null, true, true, MoveEffectTrigger.PRE_APPLY); - this.canLowerStats = true; - } + // using inherited constructor apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { // apply stat change, conditionally apply switch-out From 64b407fccdc760e79cbbb9a0e98f5d37679d4743 Mon Sep 17 00:00:00 2001 From: DustinLin Date: Sun, 8 Sep 2024 17:57:03 -0700 Subject: [PATCH 6/7] adding contrary and white smoke aadditions --- src/data/move.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index 77c735fa93b..7199da490c0 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -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"; @@ -5147,8 +5147,10 @@ export class PartingShotAttr extends ForceSwitchOutAttr { 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.hasAbility(Abilities.CLEAR_BODY, true, false) || - (target.getStatStage(Stat.ATK) === -6 && target.getStatStage(Stat.SPATK) === -6) || target.scene.arena.findTagsOnSide(t => t.tagType === ArenaTagType.MIST, ArenaTagSide.ENEMY).length > 0 + 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 { From e0c97b8ebb9e58b1b2b4a9779ed8c3a0c176fef1 Mon Sep 17 00:00:00 2001 From: DustinLin Date: Sun, 8 Sep 2024 20:34:53 -0700 Subject: [PATCH 7/7] adding white smoke and contrary ability --- src/test/moves/parting_shot.test.ts | 68 +++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/test/moves/parting_shot.test.ts b/src/test/moves/parting_shot.test.ts index 132392103d7..bdd3779f20f 100644 --- a/src/test/moves/parting_shot.test.ts +++ b/src/test/moves/parting_shot.test.ts @@ -160,6 +160,72 @@ describe("Moves - Parting Shot", () => { }, TIMEOUT ); + 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 () => { @@ -182,6 +248,8 @@ describe("Moves - Parting Shot", () => { 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);