[Misc][Bug] Add isBatonPassable property to BattlerTags (#3472)

* Add `isTransferrable` property to `BattlerTag`s

* Update Baton Pass to check `isTransferrable` for `BattlerTag`s

* Don't mark Salt Cure as transferrable

* Add Destiny Bond, remove `GroundedTag` and `ExposedTag`

* Fix daily mode test

* Add test

* Rename `isTransferrable` to `isBatonPassable`
This commit is contained in:
NightKev 2024-09-04 10:58:35 -07:00 committed by GitHub
parent 207b3e1eb7
commit fde32cea6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 43 additions and 35 deletions

View File

@ -39,13 +39,15 @@ export class BattlerTag {
public turnCount: number; public turnCount: number;
public sourceMove: Moves; public sourceMove: Moves;
public sourceId?: number; public sourceId?: number;
public isBatonPassable: boolean;
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: number, sourceMove?: Moves, sourceId?: number) { constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: number, sourceMove?: Moves, sourceId?: number, isBatonPassable: boolean = false) {
this.tagType = tagType; this.tagType = tagType;
this.lapseTypes = Array.isArray(lapseType) ? lapseType : [ lapseType ]; this.lapseTypes = Array.isArray(lapseType) ? lapseType : [ lapseType ];
this.turnCount = turnCount; this.turnCount = turnCount;
this.sourceMove = sourceMove!; // TODO: is this bang correct? this.sourceMove = sourceMove!; // TODO: is this bang correct?
this.sourceId = sourceId; this.sourceId = sourceId;
this.isBatonPassable = isBatonPassable;
} }
canAdd(pokemon: Pokemon): boolean { canAdd(pokemon: Pokemon): boolean {
@ -206,7 +208,7 @@ export class ShellTrapTag extends BattlerTag {
export class TrappedTag extends BattlerTag { export class TrappedTag extends BattlerTag {
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: number, sourceMove: Moves, sourceId: number) { constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: number, sourceMove: Moves, sourceId: number) {
super(tagType, lapseType, turnCount, sourceMove, sourceId); super(tagType, lapseType, turnCount, sourceMove, sourceId, true);
} }
canAdd(pokemon: Pokemon): boolean { canAdd(pokemon: Pokemon): boolean {
@ -326,7 +328,7 @@ export class InterruptedTag extends BattlerTag {
*/ */
export class ConfusedTag extends BattlerTag { export class ConfusedTag extends BattlerTag {
constructor(turnCount: number, sourceMove: Moves) { constructor(turnCount: number, sourceMove: Moves) {
super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount, sourceMove); super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount, sourceMove, undefined, true);
} }
canAdd(pokemon: Pokemon): boolean { canAdd(pokemon: Pokemon): boolean {
@ -386,7 +388,7 @@ export class ConfusedTag extends BattlerTag {
*/ */
export class DestinyBondTag extends BattlerTag { export class DestinyBondTag extends BattlerTag {
constructor(sourceMove: Moves, sourceId: number) { constructor(sourceMove: Moves, sourceId: number) {
super(BattlerTagType.DESTINY_BOND, BattlerTagLapseType.PRE_MOVE, 1, sourceMove, sourceId); super(BattlerTagType.DESTINY_BOND, BattlerTagLapseType.PRE_MOVE, 1, sourceMove, sourceId, true);
} }
/** /**
@ -505,7 +507,7 @@ export class SeedTag extends BattlerTag {
private sourceIndex: number; private sourceIndex: number;
constructor(sourceId: number) { constructor(sourceId: number) {
super(BattlerTagType.SEEDED, BattlerTagLapseType.TURN_END, 1, Moves.LEECH_SEED, sourceId); super(BattlerTagType.SEEDED, BattlerTagLapseType.TURN_END, 1, Moves.LEECH_SEED, sourceId, true);
} }
/** /**
@ -776,7 +778,7 @@ export class OctolockTag extends TrappedTag {
export class AquaRingTag extends BattlerTag { export class AquaRingTag extends BattlerTag {
constructor() { constructor() {
super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1, Moves.AQUA_RING, undefined); super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1, Moves.AQUA_RING, undefined, true);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
@ -808,7 +810,7 @@ export class AquaRingTag extends BattlerTag {
/** Tag used to allow moves that interact with {@link Moves.MINIMIZE} to function */ /** Tag used to allow moves that interact with {@link Moves.MINIMIZE} to function */
export class MinimizeTag extends BattlerTag { export class MinimizeTag extends BattlerTag {
constructor() { constructor() {
super(BattlerTagType.MINIMIZED, BattlerTagLapseType.TURN_END, 1, Moves.MINIMIZE, undefined); super(BattlerTagType.MINIMIZED, BattlerTagLapseType.TURN_END, 1, Moves.MINIMIZE);
} }
canAdd(pokemon: Pokemon): boolean { canAdd(pokemon: Pokemon): boolean {
@ -1206,7 +1208,7 @@ export class SturdyTag extends BattlerTag {
export class PerishSongTag extends BattlerTag { export class PerishSongTag extends BattlerTag {
constructor(turnCount: number) { constructor(turnCount: number) {
super(BattlerTagType.PERISH_SONG, BattlerTagLapseType.TURN_END, turnCount, Moves.PERISH_SONG); super(BattlerTagType.PERISH_SONG, BattlerTagLapseType.TURN_END, turnCount, Moves.PERISH_SONG, undefined, true);
} }
canAdd(pokemon: Pokemon): boolean { canAdd(pokemon: Pokemon): boolean {
@ -1262,7 +1264,7 @@ export class AbilityBattlerTag extends BattlerTag {
public ability: Abilities; public ability: Abilities;
constructor(tagType: BattlerTagType, ability: Abilities, lapseType: BattlerTagLapseType, turnCount: number) { constructor(tagType: BattlerTagType, ability: Abilities, lapseType: BattlerTagLapseType, turnCount: number) {
super(tagType, lapseType, turnCount, undefined); super(tagType, lapseType, turnCount);
this.ability = ability; this.ability = ability;
} }
@ -1438,7 +1440,7 @@ export class TypeImmuneTag extends BattlerTag {
public immuneType: Type; public immuneType: Type;
constructor(tagType: BattlerTagType, sourceMove: Moves, immuneType: Type, length: number = 1) { constructor(tagType: BattlerTagType, sourceMove: Moves, immuneType: Type, length: number = 1) {
super(tagType, BattlerTagLapseType.TURN_END, length, sourceMove); super(tagType, BattlerTagLapseType.TURN_END, length, sourceMove, undefined, true);
this.immuneType = immuneType; this.immuneType = immuneType;
} }
@ -1502,7 +1504,7 @@ export class TypeBoostTag extends BattlerTag {
export class CritBoostTag extends BattlerTag { export class CritBoostTag extends BattlerTag {
constructor(tagType: BattlerTagType, sourceMove: Moves) { constructor(tagType: BattlerTagType, sourceMove: Moves) {
super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove); super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove, undefined, true);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
@ -1594,7 +1596,7 @@ export class CursedTag extends BattlerTag {
private sourceIndex: number; private sourceIndex: number;
constructor(sourceId: number) { constructor(sourceId: number) {
super(BattlerTagType.CURSED, BattlerTagLapseType.TURN_END, 1, Moves.CURSE, sourceId); super(BattlerTagType.CURSED, BattlerTagLapseType.TURN_END, 1, Moves.CURSE, sourceId, true);
} }
/** /**

View File

@ -2660,11 +2660,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
for (const tag of source.summonData.tags) { for (const tag of source.summonData.tags) {
if (!tag.isBatonPassable) {
// bypass those can not be passed via Baton Pass
const excludeTagTypes = new Set([BattlerTagType.DROWSY, BattlerTagType.INFATUATED, BattlerTagType.FIRE_BOOST]);
if (excludeTagTypes.has(tag.tagType)) {
continue; continue;
} }

View File

@ -1,13 +1,13 @@
import { Stat } from "#enums/stat"; import { BattlerIndex } from "#app/battle";
import { PostSummonPhase } from "#app/phases/post-summon-phase";
import { TurnEndPhase } from "#app/phases/turn-end-phase";
import GameManager from "#app/test/utils/gameManager"; import GameManager from "#app/test/utils/gameManager";
import { Abilities } from "#enums/abilities";
import { BattlerTagType } from "#enums/battler-tag-type";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { Stat } from "#enums/stat";
import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { SPLASH_ONLY } from "../utils/testUtils";
describe("Moves - Baton Pass", () => { describe("Moves - Baton Pass", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -27,20 +27,17 @@ describe("Moves - Baton Pass", () => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override game.override
.battleType("single") .battleType("single")
.enemySpecies(Species.DUGTRIO) .enemySpecies(Species.MAGIKARP)
.startingLevel(1) .enemyAbility(Abilities.BALL_FETCH)
.startingWave(97)
.moveset([Moves.BATON_PASS, Moves.NASTY_PLOT, Moves.SPLASH]) .moveset([Moves.BATON_PASS, Moves.NASTY_PLOT, Moves.SPLASH])
.ability(Abilities.BALL_FETCH)
.enemyMoveset(SPLASH_ONLY) .enemyMoveset(SPLASH_ONLY)
.disableCrits(); .disableCrits();
}); });
it("transfers all stat stages when player uses it", async() => { it("transfers all stat stages when player uses it", async() => {
// arrange // arrange
await game.startBattle([ await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]);
Species.RAICHU,
Species.SHUCKLE
]);
// round 1 - buff // round 1 - buff
game.move.select(Moves.NASTY_PLOT); game.move.select(Moves.NASTY_PLOT);
@ -53,7 +50,7 @@ describe("Moves - Baton Pass", () => {
// round 2 - baton pass // round 2 - baton pass
game.move.select(Moves.BATON_PASS); game.move.select(Moves.BATON_PASS);
game.doSelectPartyPokemon(1); game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to("TurnEndPhase");
// assert // assert
playerPokemon = game.scene.getPlayerPokemon()!; playerPokemon = game.scene.getPlayerPokemon()!;
@ -66,10 +63,7 @@ describe("Moves - Baton Pass", () => {
game.override game.override
.startingWave(5) .startingWave(5)
.enemyMoveset(new Array(4).fill([Moves.NASTY_PLOT])); .enemyMoveset(new Array(4).fill([Moves.NASTY_PLOT]));
await game.startBattle([ await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]);
Species.RAICHU,
Species.SHUCKLE
]);
// round 1 - ai buffs // round 1 - ai buffs
game.move.select(Moves.SPLASH); game.move.select(Moves.SPLASH);
@ -79,7 +73,7 @@ describe("Moves - Baton Pass", () => {
game.scene.getEnemyPokemon()!.hp = 100; game.scene.getEnemyPokemon()!.hp = 100;
game.override.enemyMoveset(new Array(4).fill(Moves.BATON_PASS)); game.override.enemyMoveset(new Array(4).fill(Moves.BATON_PASS));
game.move.select(Moves.SPLASH); game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to(PostSummonPhase, false); await game.phaseInterceptor.to("PostSummonPhase", false);
// assert // assert
// check buffs are still there // check buffs are still there
@ -94,4 +88,20 @@ describe("Moves - Baton Pass", () => {
"PostSummonPhase" "PostSummonPhase"
]); ]);
}, 20000); }, 20000);
it("doesn't transfer effects that aren't transferrable", async() => {
game.override.enemyMoveset(Array(4).fill(Moves.SALT_CURE));
await game.classicMode.startBattle([Species.PIKACHU, Species.FEEBAS]);
const [player1, player2] = game.scene.getParty();
game.move.select(Moves.BATON_PASS);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("MoveEndPhase");
expect(player1.findTag((t) => t.tagType === BattlerTagType.SALT_CURED)).toBeTruthy();
game.doSelectPartyPokemon(1);
await game.toNextTurn();
expect(player2.findTag((t) => t.tagType === BattlerTagType.SALT_CURED)).toBeUndefined();
}, 20000);
}); });