[Bug] Toxic Spikes implementation issues fixed
Adjusted MoveEffectPhase.start() so that ENEMY_SIDE targeted moves no longer occur twice per use in double battles. Updated Toxic Orb test to no longer expect a tick of damage turn 1. Fixed Toxic/Poison dealing damage immediately when applied. Fixed Hazards not persisting through save Added unit tests Fixed flyout not displaying correct number of Spikes/Toxic Spikes after a refresh
This commit is contained in:
parent
ae2ab120dc
commit
063aa24588
|
@ -64,6 +64,18 @@ export abstract class ArenaTag {
|
||||||
? allMoves[this.sourceMove].name
|
? allMoves[this.sourceMove].name
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When given a arena tag or json representing one, load the data for it.
|
||||||
|
* This is meant to be inherited from by any arena tag with custom attributes
|
||||||
|
* @param {ArenaTag | any} source An arena tag
|
||||||
|
*/
|
||||||
|
loadTag(source : ArenaTag | any) : void {
|
||||||
|
this.turnCount = source.turnCount;
|
||||||
|
this.sourceMove = source.sourceMove;
|
||||||
|
this.sourceId = source.sourceId;
|
||||||
|
this.side = source.side;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -557,6 +569,12 @@ export class ArenaTrapTag extends ArenaTag {
|
||||||
getMatchupScoreMultiplier(pokemon: Pokemon): number {
|
getMatchupScoreMultiplier(pokemon: Pokemon): number {
|
||||||
return pokemon.isGrounded() ? 1 : Phaser.Math.Linear(0, 1 / Math.pow(2, this.layers), Math.min(pokemon.getHpRatio(), 0.5) * 2);
|
return pokemon.isGrounded() ? 1 : Phaser.Math.Linear(0, 1 / Math.pow(2, this.layers), Math.min(pokemon.getHpRatio(), 0.5) * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadTag(source: any): void {
|
||||||
|
super.loadTag(source);
|
||||||
|
this.layers = source.layers;
|
||||||
|
this.maxLayers = source.maxLayers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -905,6 +923,12 @@ class HappyHourTag extends ArenaTag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class NoneTag extends ArenaTag {
|
||||||
|
constructor() {
|
||||||
|
super(ArenaTagType.NONE, 0, undefined, undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null {
|
export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null {
|
||||||
switch (tagType) {
|
switch (tagType) {
|
||||||
case ArenaTagType.MIST:
|
case ArenaTagType.MIST:
|
||||||
|
@ -954,3 +978,16 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When given a battler tag or json representing one, creates an actual ArenaTag object with the same data.
|
||||||
|
* @param {ArenaTag | any} source An arena tag
|
||||||
|
* @return {ArenaTag} The valid arena tag
|
||||||
|
*/
|
||||||
|
export function loadArenaTag(source: ArenaTag | any): ArenaTag {
|
||||||
|
const tag = getArenaTag(source.tagType, source.turnCount, source.sourceMove, source.sourceId, source.targetIndex, source.side)
|
||||||
|
?? new NoneTag();
|
||||||
|
tag.loadTag(source);
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import {PostTurnStatusEffectPhase} from "#app/phases/post-turn-status-effect-phase";
|
||||||
|
import {Phase} from "#app/phase";
|
||||||
|
import {BattlerIndex} from "#app/battle";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
|
||||||
|
export class CheckStatusEffectPhase extends Phase {
|
||||||
|
private order : BattlerIndex[];
|
||||||
|
constructor(scene : BattleScene, order : BattlerIndex[]) {
|
||||||
|
super(scene);
|
||||||
|
this.scene = scene;
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
const field = this.scene.getField();
|
||||||
|
for (const o of this.order) {
|
||||||
|
if (field[o].status && field[o].status.isPostTurn()) {
|
||||||
|
this.scene.unshiftPhase(new PostTurnStatusEffectPhase(this.scene, o));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.end();
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import { PokemonMultiHitModifier, FlinchChanceModifier, EnemyAttackStatusEffectC
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import * as Utils from "#app/utils.js";
|
import * as Utils from "#app/utils.js";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
|
import {Abilities} from "#enums/abilities";
|
||||||
|
|
||||||
export class MoveEffectPhase extends PokemonPhase {
|
export class MoveEffectPhase extends PokemonPhase {
|
||||||
public move: PokemonMove;
|
public move: PokemonMove;
|
||||||
|
@ -123,6 +124,11 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||||
new MoveAnim(move.id as Moves, user, this.getTarget()?.getBattlerIndex()!).play(this.scene, () => { // TODO: is the bang correct here?
|
new MoveAnim(move.id as Moves, user, this.getTarget()?.getBattlerIndex()!).play(this.scene, () => { // TODO: is the bang correct here?
|
||||||
/** Has the move successfully hit a target (for damage) yet? */
|
/** Has the move successfully hit a target (for damage) yet? */
|
||||||
let hasHit: boolean = false;
|
let hasHit: boolean = false;
|
||||||
|
|
||||||
|
/** Hazards bounce if any opposing Pokémon has the ability **/
|
||||||
|
/** TODO: Update to support future Magic Coat / Magic Bounce implementation **/
|
||||||
|
let hazardBounce: boolean = false;
|
||||||
|
|
||||||
for (const target of targets) {
|
for (const target of targets) {
|
||||||
/**
|
/**
|
||||||
* If the move missed a target, stop all future hits against that target
|
* If the move missed a target, stop all future hits against that target
|
||||||
|
@ -157,6 +163,17 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||||
/** Does this phase represent the invoked move's first strike? */
|
/** Does this phase represent the invoked move's first strike? */
|
||||||
const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount);
|
const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount);
|
||||||
|
|
||||||
|
/** Check if current target is affected by Magic Bounce or used Magic Coat
|
||||||
|
* If they did, then field hazards should be bounced back in their entirety
|
||||||
|
*/
|
||||||
|
const bounceStatus = target.hasAbility(Abilities.MAGIC_BOUNCE) || (this.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move === Moves.MAGIC_COAT);
|
||||||
|
hazardBounce = hazardBounce && bounceStatus;
|
||||||
|
|
||||||
|
// Prevent ENEMY_SIDE targeted moves from occurring twice in double battles
|
||||||
|
if (move.moveTarget === MoveTarget.ENEMY_SIDE && target !== targets[targets.length-1]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Only log the move's result on the first strike
|
// Only log the move's result on the first strike
|
||||||
if (firstHit) {
|
if (firstHit) {
|
||||||
user.pushMoveHistory(moveHistoryEntry);
|
user.pushMoveHistory(moveHistoryEntry);
|
||||||
|
|
|
@ -6,7 +6,6 @@ import { StatusEffect } from "#app/enums/status-effect.js";
|
||||||
import Pokemon from "#app/field/pokemon.js";
|
import Pokemon from "#app/field/pokemon.js";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages.js";
|
import { getPokemonNameWithAffix } from "#app/messages.js";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
import { PostTurnStatusEffectPhase } from "./post-turn-status-effect-phase";
|
|
||||||
|
|
||||||
export class ObtainStatusEffectPhase extends PokemonPhase {
|
export class ObtainStatusEffectPhase extends PokemonPhase {
|
||||||
private statusEffect: StatusEffect | undefined;
|
private statusEffect: StatusEffect | undefined;
|
||||||
|
@ -33,9 +32,6 @@ export class ObtainStatusEffectPhase extends PokemonPhase {
|
||||||
pokemon.updateInfo(true);
|
pokemon.updateInfo(true);
|
||||||
new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect! - 1), pokemon).play(this.scene, () => {
|
new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect! - 1), pokemon).play(this.scene, () => {
|
||||||
this.scene.queueMessage(getStatusEffectObtainText(this.statusEffect, getPokemonNameWithAffix(pokemon), this.sourceText ?? undefined));
|
this.scene.queueMessage(getStatusEffectObtainText(this.statusEffect, getPokemonNameWithAffix(pokemon), this.sourceText ?? undefined));
|
||||||
if (pokemon.status?.isPostTurn()) {
|
|
||||||
this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, this.battlerIndex));
|
|
||||||
}
|
|
||||||
this.end();
|
this.end();
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -13,10 +13,10 @@ import { BerryPhase } from "./berry-phase";
|
||||||
import { FieldPhase } from "./field-phase";
|
import { FieldPhase } from "./field-phase";
|
||||||
import { MoveHeaderPhase } from "./move-header-phase";
|
import { MoveHeaderPhase } from "./move-header-phase";
|
||||||
import { MovePhase } from "./move-phase";
|
import { MovePhase } from "./move-phase";
|
||||||
import { PostTurnStatusEffectPhase } from "./post-turn-status-effect-phase";
|
|
||||||
import { SwitchSummonPhase } from "./switch-summon-phase";
|
import { SwitchSummonPhase } from "./switch-summon-phase";
|
||||||
import { TurnEndPhase } from "./turn-end-phase";
|
import { TurnEndPhase } from "./turn-end-phase";
|
||||||
import { WeatherEffectPhase } from "./weather-effect-phase";
|
import { WeatherEffectPhase } from "./weather-effect-phase";
|
||||||
|
import {CheckStatusEffectPhase} from "#app/phases/check-status-effect-phase";
|
||||||
|
|
||||||
export class TurnStartPhase extends FieldPhase {
|
export class TurnStartPhase extends FieldPhase {
|
||||||
constructor(scene: BattleScene) {
|
constructor(scene: BattleScene) {
|
||||||
|
@ -153,11 +153,8 @@ export class TurnStartPhase extends FieldPhase {
|
||||||
|
|
||||||
this.scene.pushPhase(new WeatherEffectPhase(this.scene));
|
this.scene.pushPhase(new WeatherEffectPhase(this.scene));
|
||||||
|
|
||||||
for (const o of order) {
|
/** Add a new phase to check who should be taking status damage */
|
||||||
if (field[o].status && field[o].status.isPostTurn()) {
|
this.scene.pushPhase(new CheckStatusEffectPhase(this.scene, order));
|
||||||
this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, o));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.scene.pushPhase(new BerryPhase(this.scene));
|
this.scene.pushPhase(new BerryPhase(this.scene));
|
||||||
this.scene.pushPhase(new TurnEndPhase(this.scene));
|
this.scene.pushPhase(new TurnEndPhase(this.scene));
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Arena } from "../field/arena";
|
import { Arena } from "../field/arena";
|
||||||
import { ArenaTag } from "../data/arena-tag";
|
import {ArenaTag, loadArenaTag} from "../data/arena-tag";
|
||||||
import { Biome } from "#enums/biome";
|
import { Biome } from "#enums/biome";
|
||||||
import { Weather } from "../data/weather";
|
import { Weather } from "../data/weather";
|
||||||
import { Terrain } from "#app/data/terrain.js";
|
import { Terrain } from "#app/data/terrain.js";
|
||||||
|
@ -15,6 +15,10 @@ export default class ArenaData {
|
||||||
this.biome = sourceArena ? sourceArena.biomeType : source.biome;
|
this.biome = sourceArena ? sourceArena.biomeType : source.biome;
|
||||||
this.weather = sourceArena ? sourceArena.weather : source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : null;
|
this.weather = sourceArena ? sourceArena.weather : source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : null;
|
||||||
this.terrain = sourceArena ? sourceArena.terrain : source.terrain ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) : null;
|
this.terrain = sourceArena ? sourceArena.terrain : source.terrain ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) : null;
|
||||||
this.tags = sourceArena ? sourceArena.tags : [];
|
this.tags = [];
|
||||||
|
|
||||||
|
if (source.tags) {
|
||||||
|
this.tags = source.tags.map(t => loadArenaTag(t));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,8 @@ import { WeatherType } from "#app/enums/weather-type.js";
|
||||||
import { TerrainType } from "#app/data/terrain.js";
|
import { TerrainType } from "#app/data/terrain.js";
|
||||||
import { OutdatedPhase } from "#app/phases/outdated-phase.js";
|
import { OutdatedPhase } from "#app/phases/outdated-phase.js";
|
||||||
import { ReloadSessionPhase } from "#app/phases/reload-session-phase.js";
|
import { ReloadSessionPhase } from "#app/phases/reload-session-phase.js";
|
||||||
|
import {TagAddedEvent} from "#app/events/arena";
|
||||||
|
import {ArenaTrapTag} from "#app/data/arena-tag";
|
||||||
|
|
||||||
export const defaultStarterSpecies: Species[] = [
|
export const defaultStarterSpecies: Species[] = [
|
||||||
Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE,
|
Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE,
|
||||||
|
@ -966,8 +968,19 @@ export class GameData {
|
||||||
|
|
||||||
scene.arena.terrain = sessionData.arena.terrain;
|
scene.arena.terrain = sessionData.arena.terrain;
|
||||||
scene.arena.eventTarget.dispatchEvent(new TerrainChangedEvent(TerrainType.NONE, scene.arena.terrain?.terrainType!, scene.arena.terrain?.turnsLeft!)); // TODO: is this bang correct?
|
scene.arena.eventTarget.dispatchEvent(new TerrainChangedEvent(TerrainType.NONE, scene.arena.terrain?.terrainType!, scene.arena.terrain?.turnsLeft!)); // TODO: is this bang correct?
|
||||||
// TODO
|
|
||||||
//scene.arena.tags = sessionData.arena.tags;
|
scene.arena.tags = sessionData.arena.tags;
|
||||||
|
if (scene.arena.tags) {
|
||||||
|
for (const tag of scene.arena.tags) {
|
||||||
|
if (tag instanceof ArenaTrapTag) {
|
||||||
|
const { tagType, side, turnCount, layers, maxLayers } = tag as ArenaTrapTag;
|
||||||
|
scene.arena.eventTarget.dispatchEvent(new TagAddedEvent(tagType, side, turnCount, layers, maxLayers));
|
||||||
|
} else {
|
||||||
|
scene.arena.eventTarget.dispatchEvent(new TagAddedEvent(tag.tagType, tag.side, tag.turnCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const modifiersModule = await import("../modifier/modifier");
|
const modifiersModule = await import("../modifier/modifier");
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,10 @@ import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
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 { CommandPhase } from "#app/phases/command-phase.js";
|
import {CommandPhase} from "#app/phases/command-phase";
|
||||||
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase.js";
|
import {EnemyCommandPhase} from "#app/phases/enemy-command-phase";
|
||||||
import { MessagePhase } from "#app/phases/message-phase.js";
|
import {TurnEndPhase} from "#app/phases/turn-end-phase";
|
||||||
import { TurnEndPhase } from "#app/phases/turn-end-phase.js";
|
import {MessagePhase} from "#app/phases/message-phase";
|
||||||
|
|
||||||
|
|
||||||
describe("Items - Toxic orb", () => {
|
describe("Items - Toxic orb", () => {
|
||||||
|
@ -71,10 +71,8 @@ describe("Items - Toxic orb", () => {
|
||||||
await game.phaseInterceptor.run(MessagePhase);
|
await game.phaseInterceptor.run(MessagePhase);
|
||||||
const message = game.textInterceptor.getLatestMessage();
|
const message = game.textInterceptor.getLatestMessage();
|
||||||
expect(message).toContain("was badly poisoned by the Toxic Orb");
|
expect(message).toContain("was badly poisoned by the Toxic Orb");
|
||||||
await game.phaseInterceptor.run(MessagePhase);
|
|
||||||
const message2 = game.textInterceptor.getLatestMessage();
|
|
||||||
expect(message2).toContain("is hurt");
|
|
||||||
expect(message2).toContain("by poison");
|
|
||||||
expect(game.scene.getParty()[0].status!.effect).toBe(StatusEffect.TOXIC);
|
expect(game.scene.getParty()[0].status!.effect).toBe(StatusEffect.TOXIC);
|
||||||
}, 20000);
|
// Damage should not have ticked yet.
|
||||||
|
expect(game.scene.getParty()[0].status!.turnCount).toBe(0);
|
||||||
|
}, 2000);
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,294 @@
|
||||||
|
import { CommandPhase } from "#app/phases/command-phase";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
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 } from "vitest";
|
||||||
|
import {StatusEffect} from "#app/data/status-effect";
|
||||||
|
import {ArenaTagType} from "#enums/arena-tag-type";
|
||||||
|
import {ArenaTrapTag} from "#app/data/arena-tag";
|
||||||
|
import {decrypt, encrypt, GameData, SessionSaveData} from "#app/system/game-data";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// I am frankly, not that comfortable with the testing system, these could prob be more precise
|
||||||
|
describe("Moves - Toxic Spikes", () => {
|
||||||
|
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.scene.battleStyle = 1;
|
||||||
|
game.override.battleType("single");
|
||||||
|
game.override.enemySpecies(Species.RATTATA);
|
||||||
|
game.override.enemyAbility(Abilities.HYDRATION);
|
||||||
|
game.override.enemyPassiveAbility(Abilities.HYDRATION);
|
||||||
|
game.override.ability(Abilities.NO_GUARD);
|
||||||
|
game.override.passiveAbility(Abilities.HUGE_POWER);
|
||||||
|
game.override.startingWave(3);
|
||||||
|
game.override.enemyMoveset([Moves.SPLASH,Moves.SPLASH,Moves.SPLASH,Moves.SPLASH]);
|
||||||
|
game.override.moveset([Moves.TOXIC_SPIKES,Moves.SPLASH, Moves.ROAR, Moves.GLACIAL_LANCE]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("no switches, no effect", async() => {
|
||||||
|
game.override.enemySpecies(Species.RATTATA);
|
||||||
|
// player set toxic spikes on the field and do splash for 3 turns
|
||||||
|
// opponent do splash for 4 turns
|
||||||
|
// nobody should take damage
|
||||||
|
await game.classicMode.runToSummon([
|
||||||
|
Species.MIGHTYENA,
|
||||||
|
Species.POOCHYENA,
|
||||||
|
]);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase, true);
|
||||||
|
const initialHp = game.scene.getEnemyParty()[0].hp;
|
||||||
|
expect(game.scene.getEnemyParty()[0].hp).toBe(initialHp);
|
||||||
|
game.doAttack(0);
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.doAttack(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.doAttack(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.doAttack(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.doAttack(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
// Opponent should be full health and not statused
|
||||||
|
expect(game.scene.getEnemyParty()[0].hp).toBe(initialHp);
|
||||||
|
expect(!(game.scene.getEnemyParty()[0].status?.effect));
|
||||||
|
console.log(game.textInterceptor.logs);
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
it("user switch, no effect", async() => {
|
||||||
|
game.override.enemySpecies(Species.RATTATA);
|
||||||
|
// player set toxic spikes on the field and switch back to back
|
||||||
|
// opponent do splash for 2 turns
|
||||||
|
// nobody should be poisoned or damaged
|
||||||
|
await game.classicMode.runToSummon([
|
||||||
|
Species.MIGHTYENA,
|
||||||
|
Species.POOCHYENA,
|
||||||
|
]);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase, false);
|
||||||
|
|
||||||
|
const initialHp = game.scene.getParty()[0].hp;
|
||||||
|
game.doSwitchPokemon(1);
|
||||||
|
await game.phaseInterceptor.run(CommandPhase);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase, false);
|
||||||
|
|
||||||
|
game.doSwitchPokemon(1);
|
||||||
|
await game.phaseInterceptor.run(CommandPhase);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase, false);
|
||||||
|
|
||||||
|
expect(game.scene.getParty()[0].hp).toBe(initialHp);
|
||||||
|
expect(!(game.scene.getEnemyParty()[0].status?.effect));
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
it("force enemy switch - poisoned", async() => {
|
||||||
|
game.override.startingWave(5);
|
||||||
|
game.override.enemySpecies(Species.RATTATA);
|
||||||
|
// player sets 1 layer of toxic spikes
|
||||||
|
// then forces a switch with roar
|
||||||
|
// opponent should be damaged and poisoned at end of next turn
|
||||||
|
await game.classicMode.runToSummon([
|
||||||
|
Species.MIGHTYENA,
|
||||||
|
Species.POOCHYENA,
|
||||||
|
]);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase, true);
|
||||||
|
game.doAttack(0);
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.doAttack(2);
|
||||||
|
await game.toNextTurn();
|
||||||
|
const opponent = game.scene.currentBattle.enemyParty[0];
|
||||||
|
expect(opponent.hp).toBeLessThan(opponent.getMaxHp());
|
||||||
|
expect(game.scene.currentBattle.enemyParty[0].status?.effect).toBe(StatusEffect.POISON);
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
it("force enemy switch - toxic", async() => {
|
||||||
|
game.override.startingWave(5);
|
||||||
|
game.override.enemySpecies(Species.RATTATA);
|
||||||
|
// player sets 1 layer of toxic spikes
|
||||||
|
// then forces a switch with roar
|
||||||
|
// opponent should be damaged and poisoned at end of next turn
|
||||||
|
await game.classicMode.runToSummon([
|
||||||
|
Species.MIGHTYENA,
|
||||||
|
Species.POOCHYENA,
|
||||||
|
]);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase, true);
|
||||||
|
game.doAttack(0);
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.doAttack(0);
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.doAttack(2);
|
||||||
|
await game.toNextTurn();
|
||||||
|
const opponent = game.scene.currentBattle.enemyParty[0];
|
||||||
|
expect(opponent.hp).toBeLessThan(opponent.getMaxHp());
|
||||||
|
expect(game.scene.currentBattle.enemyParty[0].status?.effect).toBe(StatusEffect.TOXIC);
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
it("enemy switch - poison", async() => {
|
||||||
|
game.override.startingWave(5);
|
||||||
|
game.override.startingLevel(5000);
|
||||||
|
game.override.enemySpecies(Species.RATTATA);
|
||||||
|
// turn 1: player set toxic spikes, opponent do splash
|
||||||
|
// turn 2: player do splash, opponent switch pokemon
|
||||||
|
// opponent pokemon should trigger spikes and get poisoned
|
||||||
|
await game.classicMode.runToSummon([
|
||||||
|
Species.MIGHTYENA,
|
||||||
|
Species.POOCHYENA,
|
||||||
|
]);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase, true);
|
||||||
|
|
||||||
|
game.doAttack(0);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
game.forceOpponentToSwitch();
|
||||||
|
game.doAttack(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
const opponent = game.scene.currentBattle.enemyParty[0];
|
||||||
|
expect(opponent.hp).toBeLessThan(opponent.getMaxHp());
|
||||||
|
expect(opponent.status?.effect).toBe(StatusEffect.POISON);
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
it("enemy switch - toxic", async() => {
|
||||||
|
game.override.startingWave(5);
|
||||||
|
game.override.startingLevel(5000);
|
||||||
|
game.override.enemySpecies(Species.RATTATA);
|
||||||
|
// turn 1: player set toxic spikes, opponent do splash
|
||||||
|
// turn 2: player do splash, opponent switch pokemon
|
||||||
|
// opponent pokemon should trigger spikes and get poisoned
|
||||||
|
await game.classicMode.runToSummon([
|
||||||
|
Species.MIGHTYENA,
|
||||||
|
Species.POOCHYENA,
|
||||||
|
]);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase, true);
|
||||||
|
|
||||||
|
game.doAttack(0);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
game.doAttack(0);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
game.forceOpponentToSwitch();
|
||||||
|
game.doAttack(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
const opponent = game.scene.currentBattle.enemyParty[0];
|
||||||
|
expect(opponent.hp).toBeLessThan(opponent.getMaxHp());
|
||||||
|
expect(opponent.status?.effect).toBe(StatusEffect.TOXIC);
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
it("grounded poison switch - absorb ", async() => {
|
||||||
|
game.override.startingWave(5);
|
||||||
|
game.override.startingLevel(5000);
|
||||||
|
game.override.enemySpecies(Species.GRIMER);
|
||||||
|
// turn 1: player set toxic spikes, opponent do splash
|
||||||
|
// turn 2: player do splash, opponent switch pokemon
|
||||||
|
// opponent pokemon should trigger spikes and get poisoned
|
||||||
|
await game.classicMode.runToSummon([
|
||||||
|
Species.MIGHTYENA,
|
||||||
|
Species.POOCHYENA,
|
||||||
|
]);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase, true);
|
||||||
|
|
||||||
|
game.doAttack(0);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
game.doAttack(0);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
game.forceOpponentToSwitch();
|
||||||
|
game.doAttack(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
const opponent = game.scene.currentBattle.enemyParty[0];
|
||||||
|
|
||||||
|
// Enemy pokemon should be undamaged, and not poisoned
|
||||||
|
expect(opponent.hp).toBe(opponent.getMaxHp());
|
||||||
|
expect(!opponent.status?.effect);
|
||||||
|
|
||||||
|
// There should be no Arena Tags, as the Toxic Spikes have been absorbed
|
||||||
|
expect(game.scene.arena.tags.length === 0);
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
it("doubles - one stack per use ", async() => {
|
||||||
|
game.override.startingWave(5);
|
||||||
|
game.override.startingLevel(5000);
|
||||||
|
game.override.enemySpecies(Species.GRIMER);
|
||||||
|
game.override.battleType("double");
|
||||||
|
// turn 1: player set toxic spikes, verify one layer down
|
||||||
|
// turn 2: player set toxic spikes, verify two layers down
|
||||||
|
// turn 3: player do splash, opponent switch pokemon
|
||||||
|
// incoming grimer should absorb all layers
|
||||||
|
await game.classicMode.runToSummon([
|
||||||
|
Species.MIGHTYENA,
|
||||||
|
Species.POOCHYENA,
|
||||||
|
]);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase, true);
|
||||||
|
game.doAttack(0);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(CommandPhase, true);
|
||||||
|
game.doAttack(1);
|
||||||
|
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.arena.tags[0].tagType).toBe(ArenaTagType.TOXIC_SPIKES);
|
||||||
|
expect(game.scene.arena.tags[0] instanceof ArenaTrapTag);
|
||||||
|
expect((game.scene.arena.tags[0] as ArenaTrapTag).layers).toBe(1);
|
||||||
|
|
||||||
|
game.doAttack(0);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase, true);
|
||||||
|
game.doAttack(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(game.scene.arena.tags[0].tagType).toBe(ArenaTagType.TOXIC_SPIKES);
|
||||||
|
expect(game.scene.arena.tags[0] instanceof ArenaTrapTag);
|
||||||
|
expect((game.scene.arena.tags[0] as ArenaTrapTag).layers).toBe(2);
|
||||||
|
|
||||||
|
game.forceOpponentToSwitch();
|
||||||
|
game.doAttack(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
// There should ben o Arena Tags, as the Toxic Spikes have been absorbed
|
||||||
|
expect(game.scene.arena.tags.length === 0);
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
it("persist through reload", async() => {
|
||||||
|
game.override.startingWave(5);
|
||||||
|
game.override.startingLevel(5000);
|
||||||
|
game.override.enemySpecies(Species.RATTATA);
|
||||||
|
const scene = game.scene;
|
||||||
|
const gameData = new GameData(scene);
|
||||||
|
|
||||||
|
|
||||||
|
// turn 1: player set toxic spikes, opponent do splash
|
||||||
|
// export, save, and then load session Data
|
||||||
|
// tags should not change through reload.
|
||||||
|
await game.classicMode.runToSummon([
|
||||||
|
Species.MIGHTYENA,
|
||||||
|
Species.POOCHYENA,
|
||||||
|
]);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase, true);
|
||||||
|
|
||||||
|
game.doAttack(0);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
const sessionData : SessionSaveData = gameData["getSessionSaveData"](game.scene);
|
||||||
|
localStorage.setItem("sessionTestData", encrypt(JSON.stringify(sessionData), true));
|
||||||
|
const recoveredData : SessionSaveData = gameData.parseSessionData(decrypt(localStorage.getItem("sessionTestData")!, true));
|
||||||
|
gameData.loadSession(game.scene,0,recoveredData);
|
||||||
|
|
||||||
|
|
||||||
|
expect(sessionData.arena.tags).toEqual(recoveredData.arena.tags);
|
||||||
|
localStorage.removeItem("sessionTestData");
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in New Issue