fulling implementing forests curse and trick or treat with edge cases

This commit is contained in:
PrabbyDD 2024-10-22 14:53:11 -07:00
parent f7797603a1
commit 4251d5c919
2 changed files with 107 additions and 9 deletions

View File

@ -5873,16 +5873,29 @@ export class AddTypeAttr extends MoveEffectAttr {
constructor(type: Type) { constructor(type: Type) {
super(false, MoveEffectTrigger.HIT); super(false, MoveEffectTrigger.HIT);
this.type = type; this.type = type;
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const types = target.getTypes().slice(0, 2).filter(t => t !== Type.UNKNOWN); // TODO: Figure out some way to actually check if another version of this effect is already applied let currentTypes = target.getTypes().slice(0, 2).filter(t => t !== Type.UNKNOWN) as Type[];
if (this.type !== Type.UNKNOWN) { const baseTypes = target.getTypes(false, false, true);
types.push(this.type); const forestsCurseApplied: boolean = currentTypes.includes(Type.GRASS) && !baseTypes.includes(Type.GRASS);
const trickOrTreatApplied: boolean = currentTypes.includes(Type.GHOST) && !baseTypes.includes(Type.GHOST);
if (move.id === Moves.TRICK_OR_TREAT && forestsCurseApplied) {
// Has extra grass type, remove that and append ghost type
currentTypes = currentTypes.filter(type => type !== Type.GRASS);
currentTypes.push(this.type);
} else if (move.id === Moves.FORESTS_CURSE && trickOrTreatApplied) {
// Has extra ghost type, remove that and append grass type
currentTypes = currentTypes.filter(type => type !== Type.GHOST);
currentTypes.push(this.type);
} else if (this.type !== Type.UNKNOWN) {
currentTypes = currentTypes;
currentTypes.push(this.type);
} }
target.summonData.types = types;
target.summonData.types = currentTypes;
target.updateInfo(); target.updateInfo();
user.scene.queueMessage(i18next.t("moveTriggers:addType", { typeName: i18next.t(`pokemonInfo:Type.${Type[this.type]}`), pokemonName: getPokemonNameWithAffix(target) })); user.scene.queueMessage(i18next.t("moveTriggers:addType", { typeName: i18next.t(`pokemonInfo:Type.${Type[this.type]}`), pokemonName: getPokemonNameWithAffix(target) }));
@ -8877,8 +8890,7 @@ export function initMoves() {
.ignoresProtect() .ignoresProtect()
.ignoresVirtual(), .ignoresVirtual(),
new StatusMove(Moves.TRICK_OR_TREAT, Type.GHOST, 100, 20, -1, 0, 6) new StatusMove(Moves.TRICK_OR_TREAT, Type.GHOST, 100, 20, -1, 0, 6)
.attr(AddTypeAttr, Type.GHOST) .attr(AddTypeAttr, Type.GHOST),
.edgeCase(), // Weird interaction with Forest's Curse, reflect type, burn up
new StatusMove(Moves.NOBLE_ROAR, Type.NORMAL, 100, 30, -1, 0, 6) new StatusMove(Moves.NOBLE_ROAR, Type.NORMAL, 100, 30, -1, 0, 6)
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1)
.soundBased(), .soundBased(),
@ -8890,8 +8902,7 @@ export function initMoves() {
.target(MoveTarget.ALL_NEAR_OTHERS) .target(MoveTarget.ALL_NEAR_OTHERS)
.triageMove(), .triageMove(),
new StatusMove(Moves.FORESTS_CURSE, Type.GRASS, 100, 20, -1, 0, 6) new StatusMove(Moves.FORESTS_CURSE, Type.GRASS, 100, 20, -1, 0, 6)
.attr(AddTypeAttr, Type.GRASS) .attr(AddTypeAttr, Type.GRASS),
.edgeCase(), // Weird interaction with Trick or Treat, reflect type, burn up
new AttackMove(Moves.PETAL_BLIZZARD, Type.GRASS, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 6) new AttackMove(Moves.PETAL_BLIZZARD, Type.GRASS, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 6)
.windMove() .windMove()
.makesContact(false) .makesContact(false)

View File

@ -0,0 +1,87 @@
import { BattlerIndex } from "#app/battle";
import { Type } from "#app/data/type";
import { Moves } from "#app/enums/moves";
import { Species } from "#app/enums/species";
import { TurnEndPhase } from "#app/phases/turn-end-phase";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
describe("Moves - Forest's Curse", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override.battleType("single");
game.override.enemySpecies(Species.RELICANTH);
game.override.startingLevel(5);
game.override.enemyLevel(100);
game.override.enemyMoveset(Moves.FORESTS_CURSE);
game.override.moveset([ Moves.SPLASH, Moves.BURN_UP, Moves.DOUBLE_SHOCK ]);
});
test(
"a mono type afflicted with forest's curse should be its type + grass (2 types)",
async () => {
await game.classicMode.startBattle([ Species.RATTATA ]);
const playerPokemon = game.scene.getPlayerPokemon()!;
game.move.select(Moves.SPLASH);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to(TurnEndPhase);
// Should be normal/grass
const playerPokemonTypes = playerPokemon.getTypes();
expect(playerPokemonTypes.filter(type => type === Type.NORMAL)).toHaveLength(1);
expect(playerPokemonTypes.filter(type => type === Type.GRASS)).toHaveLength(1);
expect(playerPokemonTypes.length === 2).toBeTruthy();
}
);
test(
"a dual type afflicted with forest's curse should be its dual type + grass (3 types)",
async () => {
await game.classicMode.startBattle([ Species.MOLTRES ]);
const playerPokemon = game.scene.getPlayerPokemon()!;
game.move.select(Moves.SPLASH);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to(TurnEndPhase);
// Should be flying/fire/grass
const playerPokemonTypes = playerPokemon.getTypes();
expect(playerPokemonTypes.filter(type => type === Type.FLYING)).toHaveLength(1);
expect(playerPokemonTypes.filter(type => type === Type.FIRE)).toHaveLength(1);
expect(playerPokemonTypes.filter(type => type === Type.GRASS)).toHaveLength(1);
expect(playerPokemonTypes.length === 3).toBeTruthy();
}
);
test(
"A fire/flying type that uses burn up, then has forest's curse applied should be flying/grass",
async () => {
await game.classicMode.startBattle([ Species.MOLTRES ]);
const playerPokemon = game.scene.getPlayerPokemon()!;
game.move.select(Moves.BURN_UP);
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
await game.phaseInterceptor.to(TurnEndPhase);
// Should be flying/grass
const playerPokemonTypes = playerPokemon.getTypes();
expect(playerPokemonTypes.filter(type => type === Type.FLYING)).toHaveLength(1);
expect(playerPokemonTypes.filter(type => type === Type.FIRE)).toHaveLength(0);
expect(playerPokemonTypes.filter(type => type === Type.GRASS)).toHaveLength(1);
expect(playerPokemonTypes.length === 2).toBeTruthy();
}
);
});