From 063aa24588b7c4afb0c5a90370bc20021385369d Mon Sep 17 00:00:00 2001 From: Mason Date: Thu, 11 Jul 2024 15:16:58 -0400 Subject: [PATCH] [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 --- src/data/arena-tag.ts | 37 +++ src/phases/check-status-effect-phase.ts | 23 ++ src/phases/move-effect-phase.ts | 17 ++ src/phases/obtain-status-effect-phase.ts | 4 - src/phases/turn-start-phase.ts | 9 +- src/system/arena-data.ts | 8 +- src/system/game-data.ts | 17 +- src/test/items/toxic_orb.test.ts | 16 +- src/test/moves/toxic_spikes.test.ts | 294 +++++++++++++++++++++++ 9 files changed, 402 insertions(+), 23 deletions(-) create mode 100644 src/phases/check-status-effect-phase.ts create mode 100644 src/test/moves/toxic_spikes.test.ts diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 3394df827fb..c545b04e733 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -64,6 +64,18 @@ export abstract class ArenaTag { ? allMoves[this.sourceMove].name : 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 { 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 { switch (tagType) { case ArenaTagType.MIST: @@ -954,3 +978,16 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov 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; +} + diff --git a/src/phases/check-status-effect-phase.ts b/src/phases/check-status-effect-phase.ts new file mode 100644 index 00000000000..8dafdfed227 --- /dev/null +++ b/src/phases/check-status-effect-phase.ts @@ -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(); + } +} diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index a5ac913cc5d..175e8fc3bd9 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -14,6 +14,7 @@ import { PokemonMultiHitModifier, FlinchChanceModifier, EnemyAttackStatusEffectC import i18next from "i18next"; import * as Utils from "#app/utils.js"; import { PokemonPhase } from "./pokemon-phase"; +import {Abilities} from "#enums/abilities"; export class MoveEffectPhase extends PokemonPhase { 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? /** Has the move successfully hit a target (for damage) yet? */ 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) { /** * 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? */ 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 if (firstHit) { user.pushMoveHistory(moveHistoryEntry); diff --git a/src/phases/obtain-status-effect-phase.ts b/src/phases/obtain-status-effect-phase.ts index ac6e66a2e9f..9f15de84ca8 100644 --- a/src/phases/obtain-status-effect-phase.ts +++ b/src/phases/obtain-status-effect-phase.ts @@ -6,7 +6,6 @@ import { StatusEffect } from "#app/enums/status-effect.js"; import Pokemon from "#app/field/pokemon.js"; import { getPokemonNameWithAffix } from "#app/messages.js"; import { PokemonPhase } from "./pokemon-phase"; -import { PostTurnStatusEffectPhase } from "./post-turn-status-effect-phase"; export class ObtainStatusEffectPhase extends PokemonPhase { private statusEffect: StatusEffect | undefined; @@ -33,9 +32,6 @@ export class ObtainStatusEffectPhase extends PokemonPhase { pokemon.updateInfo(true); new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect! - 1), pokemon).play(this.scene, () => { 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(); }); return; diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index 1320cb6235c..68d62602389 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -13,10 +13,10 @@ import { BerryPhase } from "./berry-phase"; import { FieldPhase } from "./field-phase"; import { MoveHeaderPhase } from "./move-header-phase"; import { MovePhase } from "./move-phase"; -import { PostTurnStatusEffectPhase } from "./post-turn-status-effect-phase"; import { SwitchSummonPhase } from "./switch-summon-phase"; import { TurnEndPhase } from "./turn-end-phase"; import { WeatherEffectPhase } from "./weather-effect-phase"; +import {CheckStatusEffectPhase} from "#app/phases/check-status-effect-phase"; export class TurnStartPhase extends FieldPhase { constructor(scene: BattleScene) { @@ -153,11 +153,8 @@ export class TurnStartPhase extends FieldPhase { this.scene.pushPhase(new WeatherEffectPhase(this.scene)); - for (const o of order) { - if (field[o].status && field[o].status.isPostTurn()) { - this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, o)); - } - } + /** Add a new phase to check who should be taking status damage */ + this.scene.pushPhase(new CheckStatusEffectPhase(this.scene, order)); this.scene.pushPhase(new BerryPhase(this.scene)); this.scene.pushPhase(new TurnEndPhase(this.scene)); diff --git a/src/system/arena-data.ts b/src/system/arena-data.ts index 886129edcf6..682ade58e48 100644 --- a/src/system/arena-data.ts +++ b/src/system/arena-data.ts @@ -1,5 +1,5 @@ import { Arena } from "../field/arena"; -import { ArenaTag } from "../data/arena-tag"; +import {ArenaTag, loadArenaTag} from "../data/arena-tag"; import { Biome } from "#enums/biome"; import { Weather } from "../data/weather"; import { Terrain } from "#app/data/terrain.js"; @@ -15,6 +15,10 @@ export default class ArenaData { this.biome = sourceArena ? sourceArena.biomeType : source.biome; 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.tags = sourceArena ? sourceArena.tags : []; + this.tags = []; + + if (source.tags) { + this.tags = source.tags.map(t => loadArenaTag(t)); + } } } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index a9acd80fdee..78bb2523a64 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -44,6 +44,8 @@ import { WeatherType } from "#app/enums/weather-type.js"; import { TerrainType } from "#app/data/terrain.js"; import { OutdatedPhase } from "#app/phases/outdated-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[] = [ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, @@ -966,8 +968,19 @@ export class GameData { 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? - // 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"); diff --git a/src/test/items/toxic_orb.test.ts b/src/test/items/toxic_orb.test.ts index dc54a5a1c36..83259b67866 100644 --- a/src/test/items/toxic_orb.test.ts +++ b/src/test/items/toxic_orb.test.ts @@ -9,10 +9,10 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; -import { CommandPhase } from "#app/phases/command-phase.js"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase.js"; -import { MessagePhase } from "#app/phases/message-phase.js"; -import { TurnEndPhase } from "#app/phases/turn-end-phase.js"; +import {CommandPhase} from "#app/phases/command-phase"; +import {EnemyCommandPhase} from "#app/phases/enemy-command-phase"; +import {TurnEndPhase} from "#app/phases/turn-end-phase"; +import {MessagePhase} from "#app/phases/message-phase"; describe("Items - Toxic orb", () => { @@ -71,10 +71,8 @@ describe("Items - Toxic orb", () => { await game.phaseInterceptor.run(MessagePhase); const message = game.textInterceptor.getLatestMessage(); 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); - }, 20000); + // Damage should not have ticked yet. + expect(game.scene.getParty()[0].status!.turnCount).toBe(0); + }, 2000); }); diff --git a/src/test/moves/toxic_spikes.test.ts b/src/test/moves/toxic_spikes.test.ts new file mode 100644 index 00000000000..2d7a10f21cd --- /dev/null +++ b/src/test/moves/toxic_spikes.test.ts @@ -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); + + + +});