diff --git a/MEs-Roadmap-Documentation.md b/MEs-Roadmap-Documentation.md index 7e5aa1b069e..97e61e0513a 100644 --- a/MEs-Roadmap-Documentation.md +++ b/MEs-Roadmap-Documentation.md @@ -1,14 +1,14 @@ # πŸ“ Most immediate things to-do list - ### High priority - - πŸ› Intimidate and other ETB abilities proc twice at the start of wild MEs (fight or flight, dark deal) - - βš™οΈ Add a tag system so MEs don't show where they shouldn't and bricking Challenge runs: + - βš™οΈ Add a tag system so MEs to filter or change spawn rates in Challenge runs: - noChallenge (cant be spawned in challenge runs) - allChallenge (can spawn in all challenge modes) - (typespecific)Challenge: - Example: fireOnly (can only spawn in fire related challenges) - ### Medium priority + - βš™οΈ Update Chest visuals for Mysterious Chest (with animated chest) - ### Low priority - πŸ› Mysterious Challengers can spawn two trainers (or three) of the same type [Dev comment: not a bug] @@ -106,9 +106,9 @@ Events (referred to as 'Mysterious Encounters, MEs' in the code) aim to be an ad ### 🌟 **Rarity** tier of the ME, common by default. - βšͺ Common pool - - πŸ”΅ Rare pool - - 🟣 Epic pool - - 🟑 Legendary pool + - πŸ”΅ Uncommon pool + - 🟣 Rare pool + - 🟑 Super Rare pool ### **Optional Requirements** for Mystery Encounters. - πŸ› οΈ They give granular control over whether encounters will spawn in certain situations @@ -135,13 +135,10 @@ Events (referred to as 'Mysterious Encounters, MEs' in the code) aim to be an ad # πŸ“ Known bugs (squash 'em all!): - ## πŸ”΄ __**Really bad ones**__ - - πŸ› Picking up certain items in Fight or Flight is still broken. Workaround is leave encounter. - - πŸ› Modifiers that are applied to pokemon get skipped in Fight or Flight. - ## 🟑 __**Bad ones under certain circumstances**__ - πŸ› Needs further replication : At wave 51, wild PKMN encounter caused a freezed after pressing "ESC" key upon being asked to switch PKMNs - πŸ› Wave seed generates different encounter data if you roll to a new wave, see the spawned stuff, and refresh the app - - πŸ› Type-buffing items (like Silk Scarf) get swapped around when offered as a reward in Fight or Flight - ## 🟒 __**Non-game breaking**__ - Both of these bugs seem to have in common that they don't "forget" their last passed string: @@ -157,7 +154,6 @@ Events (referred to as 'Mysterious Encounters, MEs' in the code) aim to be an ad #### More requirements (with helper functions) - Having X item - Having Y amount of X item -- Being in a specific Biome - A PokΓ©mon X in player's party can learn Y move - A PokΓ©mon X in player's party knows Y move - A PokΓ©mon X in player's party has Y ability diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 0515800e53f..fe3023cbc22 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -93,7 +93,7 @@ import { UiTheme } from "#enums/ui-theme"; import { TimedEventManager } from "#app/timed-event-manager.js"; import i18next from "i18next"; import MysteryEncounter, { MysteryEncounterTier, MysteryEncounterVariant } from "./data/mystery-encounter"; -import { mysteryEncountersByBiome, allMysteryEncounters, BASE_MYSTERY_ENCOUNTER_WEIGHT, AVERAGE_ENCOUNTERS_PER_RUN_TARGET } from "./data/mystery-encounters/mystery-encounters"; +import { mysteryEncountersByBiome, allMysteryEncounters, BASE_MYSTERY_ENCOUNTER_WEIGHT, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters"; import { MysteryEncounterFlags } from "#app/data/mystery-encounter-flags"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; @@ -1098,22 +1098,22 @@ export default class BattleScene extends SceneBase { // Check for mystery encounter // Can only occur in place of a standard wild battle, waves 10-180 - // let testStartingWeight = 10; - // while (testStartingWeight < 30) { + // let testStartingWeight = 0; + // while (testStartingWeight < 20) { // calculateMEAggregateStats(this, testStartingWeight); - // testStartingWeight += 2; + // testStartingWeight += 1; // } if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < 180 && newWaveIndex > 10) { const roll = Utils.randSeedInt(256); - // Base spawn weight is 3/256, and increases by 1/256 for each missed attempt at spawning an encounter on a valid floor - const sessionEncounterRate = !isNullOrUndefined(this.mysteryEncounterFlags?.encounterSpawnChance) ? this.mysteryEncounterFlags.encounterSpawnChance : BASE_MYSTERY_ENCOUNTER_WEIGHT; + // Base spawn weight is 1/256, and increases by 5/256 for each missed attempt at spawning an encounter on a valid floor + const sessionEncounterRate = !isNullOrUndefined(this.mysteryEncounterData?.encounterSpawnChance) ? this.mysteryEncounterData.encounterSpawnChance : BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT; // If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn // Do the reverse as well // Reduces occurrence of runs with very few (<6) and a ton (>10) of encounters const expectedEncountersByFloor = AVERAGE_ENCOUNTERS_PER_RUN_TARGET / (180 - 10) * newWaveIndex; - const currentRunDiffFromAvg = expectedEncountersByFloor - (this.mysteryEncounterFlags?.encounteredEvents?.length || 0); + const currentRunDiffFromAvg = expectedEncountersByFloor - (this.mysteryEncounterData?.encounteredEvents?.length || 0); const favoredEncounterRate = sessionEncounterRate + currentRunDiffFromAvg * 5; const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE) ? favoredEncounterRate : Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE; @@ -1121,9 +1121,9 @@ export default class BattleScene extends SceneBase { if (roll < successRate) { newBattleType = BattleType.MYSTERY_ENCOUNTER; // Reset base spawn weight - this.mysteryEncounterFlags.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_WEIGHT; + this.mysteryEncounterData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT; } else { - this.mysteryEncounterFlags.encounterSpawnChance = sessionEncounterRate + 1; + this.mysteryEncounterData.encounterSpawnChance = sessionEncounterRate + WIGHT_INCREMENT_ON_SPAWN_MISS; } } } @@ -1168,7 +1168,9 @@ export default class BattleScene extends SceneBase { if (newBattleType === BattleType.MYSTERY_ENCOUNTER) { // Disable double battle on mystery encounters (it may be re-enabled as part of encounter) this.currentBattle.double = false; - this.currentBattle.mysteryEncounter = this.getMysteryEncounter(mysteryEncounter); + this.executeWithSeedOffset(() => { + this.currentBattle.mysteryEncounter = this.getMysteryEncounter(mysteryEncounter); + }, this.currentBattle.waveIndex << 4); } //this.pushPhase(new TrainerMessageTestPhase(this, TrainerType.RIVAL, TrainerType.RIVAL_2, TrainerType.RIVAL_3, TrainerType.RIVAL_4, TrainerType.RIVAL_5, TrainerType.RIVAL_6)); @@ -2652,10 +2654,10 @@ export default class BattleScene extends SceneBase { } // Check for queued encounters first - if (!encounter && this.mysteryEncounterFlags?.nextEncounterQueue?.length > 0) { + if (!encounter && this.mysteryEncounterData?.nextEncounterQueue?.length > 0) { let i = 0; - while (i < this.mysteryEncounterFlags.nextEncounterQueue.length && !!encounter) { - const candidate = this.mysteryEncounterFlags.nextEncounterQueue[i]; + while (i < this.mysteryEncounterData.nextEncounterQueue.length && !!encounter) { + const candidate = this.mysteryEncounterData.nextEncounterQueue[i]; const forcedChance = candidate[1]; if (Utils.randSeedInt(100) < forcedChance) { encounter = allMysteryEncounters[candidate[0]]; @@ -2675,7 +2677,7 @@ export default class BattleScene extends SceneBase { const tierWeights = [61, 40, 21, 6]; // Adjust tier weights by previously encountered events to lower odds of only common/uncommons in run - this.mysteryEncounterFlags.encounteredEvents.forEach(val => { + this.mysteryEncounterData.encounteredEvents.forEach(val => { const tier = val[1]; if (tier === MysteryEncounterTier.COMMON) { tierWeights[0] = tierWeights[0] - 6; @@ -2697,7 +2699,7 @@ export default class BattleScene extends SceneBase { let availableEncounters = []; // New encounter will never be the same as the most recent encounter - const previousEncounter = this.mysteryEncounterFlags.encounteredEvents?.length > 0 ? this.mysteryEncounterFlags.encounteredEvents[this.mysteryEncounterFlags.encounteredEvents.length - 1][0] : null; + const previousEncounter = this.mysteryEncounterData.encounteredEvents?.length > 0 ? this.mysteryEncounterData.encounteredEvents[this.mysteryEncounterData.encounteredEvents.length - 1][0] : null; const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType); // If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available while (availableEncounters.length === 0 && tier >= 0) { diff --git a/src/data/mystery-encounter-flags.ts b/src/data/mystery-encounter-data.ts similarity index 61% rename from src/data/mystery-encounter-flags.ts rename to src/data/mystery-encounter-data.ts index 33ef84e2d1d..922bc30cbc3 100644 --- a/src/data/mystery-encounter-flags.ts +++ b/src/data/mystery-encounter-data.ts @@ -1,14 +1,14 @@ import {MysteryEncounterTier} from "#app/data/mystery-encounter"; import {MysteryEncounterType} from "#enums/mystery-encounter-type"; -import {BASE_MYSTERY_ENCOUNTER_WEIGHT} from "#app/data/mystery-encounters/mystery-encounters"; +import {BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT} from "#app/data/mystery-encounters/mystery-encounters"; import {isNullOrUndefined} from "../utils"; -export class MysteryEncounterFlags { +export class MysteryEncounterData { encounteredEvents: [MysteryEncounterType, MysteryEncounterTier][] = []; - encounterSpawnChance: number = BASE_MYSTERY_ENCOUNTER_WEIGHT; + encounterSpawnChance: number = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT; nextEncounterQueue: [MysteryEncounterType, integer][] = []; - constructor(flags: MysteryEncounterFlags) { + constructor(flags: MysteryEncounterData) { if (!isNullOrUndefined(flags)) { Object.assign(this, flags); } diff --git a/src/data/mystery-encounter-requirements.ts b/src/data/mystery-encounter-requirements.ts index d88ec530a74..1a105f2f83b 100644 --- a/src/data/mystery-encounter-requirements.ts +++ b/src/data/mystery-encounter-requirements.ts @@ -63,11 +63,11 @@ export class PreviousEncounterRequirement extends EncounterSceneRequirement { } meetsRequirement(scene: BattleScene): boolean { - return scene.mysteryEncounterFlags.encounteredEvents.some(e => e[0] === this.previousEncounterRequirement); + return scene.mysteryEncounterData.encounteredEvents.some(e => e[0] === this.previousEncounterRequirement); } getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { - return ["previousEncounter", scene.mysteryEncounterFlags.encounteredEvents.find(e => e[0] === this.previousEncounterRequirement)[0].toString()]; + return ["previousEncounter", scene.mysteryEncounterData.encounteredEvents.find(e => e[0] === this.previousEncounterRequirement)[0].toString()]; } } diff --git a/src/data/mystery-encounters/mystery-encounter-utils.ts b/src/data/mystery-encounters/mystery-encounter-utils.ts index 2a69becf1ef..8cd4963daa1 100644 --- a/src/data/mystery-encounters/mystery-encounter-utils.ts +++ b/src/data/mystery-encounters/mystery-encounter-utils.ts @@ -27,10 +27,10 @@ import {BattlerTagType} from "#enums/battler-tag-type"; import PokemonData from "#app/system/pokemon-data"; import {Biome} from "#enums/biome"; import {biomeLinks} from "#app/data/biomes"; -import {EncounterSceneRequirement} from "#app/data/mystery-encounter-requirements"; import {Mode} from "#app/ui/ui"; import {PartyOption, PartyUiMode} from "#app/ui/party-ui-handler"; import {OptionSelectConfig, OptionSelectItem} from "#app/ui/abstact-option-select-ui-handler"; +import {WIGHT_INCREMENT_ON_SPAWN_MISS} from "#app/data/mystery-encounters/mystery-encounters"; /** * @@ -62,15 +62,15 @@ export function getRandomPlayerPokemon(scene: BattleScene, isAllowedInBattle: bo return chosenPokemon; } -export function getTokensFromScene(scene: BattleScene, reqs: EncounterSceneRequirement[]): Array<[RegExp, String]> { - const arr = []; - if (scene) { - for (const req of reqs) { - req.getDialogueToken(scene); - } - } - return arr; -} +// export function getTokensFromScene(scene: BattleScene, reqs: EncounterSceneRequirement[]): Array<[RegExp, String]> { +// const arr = []; +// if (scene) { +// for (const req of reqs) { +// req.getDialogueToken(scene); +// } +// } +// return arr; +// } /** * Ties are broken by whatever mon is closer to the front of the party @@ -114,6 +114,48 @@ export function getLowestLevelPlayerPokemon(scene: BattleScene, unfainted: boole return pokemon; } +/** + * + * NOTE: This returns ANY random species, including those locked behind eggs, etc. + * @param starterTiers + * @param excludedSpecies + * @param types + * @returns + */ +export function getRandomSpeciesByStarterTier(starterTiers: number | [number, number], excludedSpecies?: Species[], types?: Type[]): Species { + let min = starterTiers instanceof Array ? starterTiers[0] : starterTiers; + let max = starterTiers instanceof Array ? starterTiers[1] : starterTiers; + + let filteredSpecies: [PokemonSpecies, number][] = Object.keys(speciesStarters) + .map(s => [parseInt(s) as Species, speciesStarters[s] as number]) + .filter(s => getPokemonSpecies(s[0]) && (!excludedSpecies || !excludedSpecies.includes(s[0]))) + .map(s => [getPokemonSpecies(s[0]), s[1]]); + + if (!isNullOrUndefined(types) && types.length > 0) { + filteredSpecies = filteredSpecies.filter(s => types.includes(s[0].type1) || types.includes(s[0].type2)); + } + + // If no filtered mons exist at specified starter tiers, will expand starter search range until there are + // Starts by decrementing starter tier min until it is 0, then increments tier max up to 10 + let tryFilterStarterTiers: [PokemonSpecies, number][] = filteredSpecies.filter(s => (s[1] >= min && s[1] <= max)); + while (tryFilterStarterTiers.length === 0 && (min !== 0 && max !== 10)) { + if (min > 0) { + min--; + } else { + max++; + } + + tryFilterStarterTiers = filteredSpecies.filter(s => s[1] >= min && s[1] <= max); + } + + if (tryFilterStarterTiers.length > 0) { + const index = Utils.randSeedInt(tryFilterStarterTiers.length); + return Phaser.Math.RND.shuffle(tryFilterStarterTiers)[index][0].speciesId; + } + + return Species.BULBASAUR; +} + export function koPlayerPokemon(pokemon: PlayerPokemon) { pokemon.hp = 0; pokemon.trySetStatus(StatusEffect.FAINT); @@ -173,48 +215,6 @@ export function showEncounterDialogue(scene: BattleScene, textContentKey: Templa scene.ui.showDialogue(text, speaker, null, callback, 0, 0); } -/** - * - * NOTE: This returns ANY random species, including those locked behind eggs, etc. - * @param starterTiers - * @param excludedSpecies - * @param types - * @returns - */ -export function getRandomSpeciesByStarterTier(starterTiers: number | [number, number], excludedSpecies?: Species[], types?: Type[]): Species { - let min = starterTiers instanceof Array ? starterTiers[0] : starterTiers; - let max = starterTiers instanceof Array ? starterTiers[1] : starterTiers; - - let filteredSpecies: [PokemonSpecies, number][] = Object.keys(speciesStarters) - .map(s => [parseInt(s) as Species, speciesStarters[s] as number]) - .filter(s => getPokemonSpecies(s[0]) && !excludedSpecies.includes(s[0])) - .map(s => [getPokemonSpecies(s[0]), s[1]]); - - if (!isNullOrUndefined(types) && types.length > 0) { - filteredSpecies = filteredSpecies.filter(s => types.includes(s[0].type1) || types.includes(s[0].type2)); - } - - // If no filtered mons exist at specified starter tiers, will expand starter search range until there are - // Starts by decrementing starter tier min until it is 0, then increments tier max up to 10 - let tryFilterStarterTiers: [PokemonSpecies, number][] = filteredSpecies.filter(s => (s[1] >= min && s[1] <= max)); - while (tryFilterStarterTiers.length === 0 && (min !== 0 && max !== 10)) { - if (min > 0) { - min--; - } else { - max++; - } - - tryFilterStarterTiers = filteredSpecies.filter(s => s[1] >= min && s[1] <= max); - } - - if (tryFilterStarterTiers.length > 0) { - const index = Utils.randSeedInt(tryFilterStarterTiers.length); - return Phaser.Math.RND.shuffle(tryFilterStarterTiers)[index][0].speciesId; - } - - return Species.BULBASAUR; -} - export class EnemyPokemonConfig { species: PokemonSpecies; isBoss: boolean = false; @@ -594,10 +594,10 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase: export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: number) { const numRuns = 1000; let run = 0; - const targetEncountersPerRun = 15; + const targetEncountersPerRun = 15; // AVERAGE_ENCOUNTERS_PER_RUN_TARGET const calculateNumEncounters = (): number[] => { - let encounterRate = baseSpawnWeight; + let encounterRate = baseSpawnWeight; // BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT const numEncounters = [0, 0, 0, 0]; let currentBiome = Biome.TOWN; let currentArena = scene.newArena(currentBiome); @@ -669,7 +669,7 @@ export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: n tierValue > commonThreshold ? ++numEncounters[0] : tierValue > uncommonThreshold ? ++numEncounters[1] : tierValue > rareThreshold ? ++numEncounters[2] : ++numEncounters[3]; } else { - encounterRate++; + encounterRate += WIGHT_INCREMENT_ON_SPAWN_MISS; } } diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index 1a0f6f2a1ce..918868d3d2c 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -8,7 +8,9 @@ import { Biome } from "#app/enums/biome"; import { SleepingSnorlaxEncounter } from "./sleeping-snorlax"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; -export const BASE_MYSTERY_ENCOUNTER_WEIGHT = 19; +// Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * ) / 256 +export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1; +export const WIGHT_INCREMENT_ON_SPAWN_MISS = 5; export const AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 15; export const allMysteryEncounters : {[encounterType:string]: MysteryEncounter} = {}; diff --git a/src/locales/en/mystery-encounter.ts b/src/locales/en/mystery-encounter.ts index b928fa38f54..f6b3a80e0ec 100644 --- a/src/locales/en/mystery-encounter.ts +++ b/src/locales/en/mystery-encounter.ts @@ -1,6 +1,8 @@ import {SimpleTranslationEntries} from "#app/interfaces/locales"; export const mysteryEncounter: SimpleTranslationEntries = { + // DO NOT REMOVE + "unit_test_dialogue": "@ec{test}@ec{test} @ec{test@ec{test}} @ec{test1} @ec{test\} @ec{test\\} @ec{test\\\} {test}", // Mysterious Encounters -- Common Tier diff --git a/src/phases.ts b/src/phases.ts index ffd9c0b609a..6bbc5f2d6d6 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -817,7 +817,7 @@ export class EncounterPhase extends BattlePhase { if (mysteryEncounter.onInit) { mysteryEncounter.onInit(this.scene); } - mysteryEncounter.populateDialogueTokensFromRequirements(); + mysteryEncounter.populateDialogueTokensFromRequirements(this.scene); }, this.scene.currentBattle.waveIndex); // Add intro visuals for mystery encounter diff --git a/src/phases/mystery-encounter-phase.ts b/src/phases/mystery-encounter-phase.ts index 2700ec32ae1..1cd04339b84 100644 --- a/src/phases/mystery-encounter-phase.ts +++ b/src/phases/mystery-encounter-phase.ts @@ -40,7 +40,7 @@ export class MysteryEncounterPhase extends Phase { // Sets flag that ME was encountered // Can be used in later MEs to check for requirements to spawn, etc. - this.scene.mysteryEncounterFlags.encounteredEvents.push([this.scene.currentBattle.mysteryEncounter.encounterType, this.scene.currentBattle.mysteryEncounter.encounterTier]); + this.scene.mysteryEncounterData.encounteredEvents.push([this.scene.currentBattle.mysteryEncounter.encounterType, this.scene.currentBattle.mysteryEncounter.encounterTier]); // Initiates encounter dialogue window and option select this.scene.ui.setMode(Mode.MYSTERY_ENCOUNTER); @@ -55,7 +55,7 @@ export class MysteryEncounterPhase extends Phase { } // Populate dialogue tokens for option requirements - this.scene.currentBattle.mysteryEncounter.populateDialogueTokensFromRequirements(); + this.scene.currentBattle.mysteryEncounter.populateDialogueTokensFromRequirements(this.scene); if (option.onPreOptionPhase) { this.scene.executeWithSeedOffset(async () => { diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 14e80623060..07ecfc74f0a 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -40,7 +40,7 @@ import { GameDataType } from "#enums/game-data-type"; import { Moves } from "#enums/moves"; import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; -import { MysteryEncounterFlags } from "../data/mystery-encounter-flags"; +import { MysteryEncounterData } from "../data/mystery-encounter-data"; import MysteryEncounter from "../data/mystery-encounter"; export const defaultStarterSpecies: Species[] = [ @@ -125,7 +125,7 @@ export interface SessionSaveData { timestamp: integer; challenges: ChallengeData[]; mysteryEncounter: MysteryEncounter; - mysteryEncounterFlags: MysteryEncounterFlags; + mysteryEncounterFlags: MysteryEncounterData; } interface Unlocks { @@ -842,7 +842,7 @@ export class GameData { timestamp: new Date().getTime(), challenges: scene.gameMode.challenges.map(c => new ChallengeData(c)), mysteryEncounter: scene.currentBattle.mysteryEncounter, - mysteryEncounterFlags: scene.mysteryEncounterFlags + mysteryEncounterFlags: scene.mysteryEncounterData } as SessionSaveData; } @@ -933,7 +933,7 @@ export class GameData { scene.score = sessionData.score; scene.updateScoreText(); - scene.mysteryEncounterFlags = sessionData?.mysteryEncounterFlags ? sessionData?.mysteryEncounterFlags : new MysteryEncounterFlags(null); + scene.mysteryEncounterData = sessionData?.mysteryEncounterFlags ? sessionData?.mysteryEncounterFlags : new MysteryEncounterData(null); scene.newArena(sessionData.arena.biome); @@ -1159,7 +1159,7 @@ export class GameData { } if (k === "mysteryEncounterFlags") { - return new MysteryEncounterFlags(v); + return new MysteryEncounterData(v); } return v; diff --git a/src/test/mystery-encounter/mystery-encounter-utils.test.ts b/src/test/mystery-encounter/mystery-encounter-utils.test.ts new file mode 100644 index 00000000000..4b514b13e01 --- /dev/null +++ b/src/test/mystery-encounter/mystery-encounter-utils.test.ts @@ -0,0 +1,366 @@ +import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest"; +import GameManager from "#app/test/utils/gameManager"; +import Phaser from "phaser"; +import { + getHighestLevelPlayerPokemon, getLowestLevelPlayerPokemon, + getRandomPlayerPokemon, getRandomSpeciesByStarterTier, getTextWithEncounterDialogueTokens, + koPlayerPokemon, queueEncounterMessage, showEncounterDialogue, showEncounterText, +} from "#app/data/mystery-encounters/mystery-encounter-utils"; +import {initSceneWithoutEncounterPhase} from "#test/utils/gameManagerUtils"; +import {Species} from "#enums/species"; +import BattleScene from "#app/battle-scene"; +import {StatusEffect} from "#app/data/status-effect"; +import MysteryEncounter from "#app/data/mystery-encounter"; +import {MessagePhase} from "#app/phases"; +import {getPokemonSpecies, speciesStarters} from "#app/data/pokemon-species"; +import {Type} from "#app/data/type"; + +describe("Mystery Encounter Utils", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + let scene: BattleScene; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + scene = game.scene; + initSceneWithoutEncounterPhase(game.scene, [Species.ARCEUS, Species.MANAPHY]); + // vi.spyOn(overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(256); + // vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(11); + }); + + describe("getRandomPlayerPokemon", () => { + it("gets a random pokemon from player party", () => { + // Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal) + scene.waveSeed = "random"; + Phaser.Math.RND.sow([ scene.waveSeed ]); + scene.rngCounter = 0; + + let result = getRandomPlayerPokemon(scene); + expect(result.species.speciesId).toBe(Species.MANAPHY); + + scene.waveSeed = "random2"; + Phaser.Math.RND.sow([ scene.waveSeed ]); + scene.rngCounter = 0; + + result = getRandomPlayerPokemon(scene); + expect(result.species.speciesId).toBe(Species.ARCEUS); + }); + + it("gets a fainted pokemon from player party if isAllowedInBattle is false", () => { + // Both pokemon fainted + scene.getParty().forEach(p => { + p.hp = 0; + p.trySetStatus(StatusEffect.FAINT); + p.updateInfo(); + }); + + // Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal) + scene.waveSeed = "random"; + Phaser.Math.RND.sow([ scene.waveSeed ]); + scene.rngCounter = 0; + + let result = getRandomPlayerPokemon(scene); + expect(result.species.speciesId).toBe(Species.MANAPHY); + + scene.waveSeed = "random2"; + Phaser.Math.RND.sow([ scene.waveSeed ]); + scene.rngCounter = 0; + + result = getRandomPlayerPokemon(scene); + expect(result.species.speciesId).toBe(Species.ARCEUS); + }); + + it("gets an unfainted pokemon from player party if isAllowedInBattle is true", () => { + // Only faint 1st pokemon + const party = scene.getParty(); + party[0].hp = 0; + party[0].trySetStatus(StatusEffect.FAINT); + party[0].updateInfo(); + + // Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal) + scene.waveSeed = "random"; + Phaser.Math.RND.sow([ scene.waveSeed ]); + scene.rngCounter = 0; + + let result = getRandomPlayerPokemon(scene, true); + expect(result.species.speciesId).toBe(Species.MANAPHY); + + scene.waveSeed = "random2"; + Phaser.Math.RND.sow([ scene.waveSeed ]); + scene.rngCounter = 0; + + result = getRandomPlayerPokemon(scene, true); + expect(result.species.speciesId).toBe(Species.MANAPHY); + }); + + it("returns last unfainted pokemon if doNotReturnLastAbleMon is false", () => { + // Only faint 1st pokemon + const party = scene.getParty(); + party[0].hp = 0; + party[0].trySetStatus(StatusEffect.FAINT); + party[0].updateInfo(); + + // Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal) + scene.waveSeed = "random"; + Phaser.Math.RND.sow([ scene.waveSeed ]); + scene.rngCounter = 0; + + let result = getRandomPlayerPokemon(scene, true, false); + expect(result.species.speciesId).toBe(Species.MANAPHY); + + scene.waveSeed = "random2"; + Phaser.Math.RND.sow([ scene.waveSeed ]); + scene.rngCounter = 0; + + result = getRandomPlayerPokemon(scene, true, false); + expect(result.species.speciesId).toBe(Species.MANAPHY); + }); + + it("never returns last unfainted pokemon if doNotReturnLastAbleMon is true", () => { + // Only faint 1st pokemon + const party = scene.getParty(); + party[0].hp = 0; + party[0].trySetStatus(StatusEffect.FAINT); + party[0].updateInfo(); + + // Seeds are calculated to return index 0 first, 1 second (if both pokemon are legal) + scene.waveSeed = "random"; + Phaser.Math.RND.sow([ scene.waveSeed ]); + scene.rngCounter = 0; + + let result = getRandomPlayerPokemon(scene, true, true); + expect(result.species.speciesId).toBe(Species.ARCEUS); + + scene.waveSeed = "random2"; + Phaser.Math.RND.sow([ scene.waveSeed ]); + scene.rngCounter = 0; + + result = getRandomPlayerPokemon(scene, true, true); + expect(result.species.speciesId).toBe(Species.ARCEUS); + }); + }); + + describe("getHighestLevelPlayerPokemon", () => { + it("gets highest level pokemon", () => { + const party = scene.getParty(); + party[0].level = 100; + + const result = getHighestLevelPlayerPokemon(scene); + expect(result.species.speciesId).toBe(Species.ARCEUS); + }); + + it("gets highest level pokemon at different index", () => { + const party = scene.getParty(); + party[1].level = 100; + + const result = getHighestLevelPlayerPokemon(scene); + expect(result.species.speciesId).toBe(Species.MANAPHY); + }); + + it("breaks ties by getting returning lower index", () => { + const party = scene.getParty(); + party[0].level = 100; + party[1].level = 100; + + const result = getHighestLevelPlayerPokemon(scene); + expect(result.species.speciesId).toBe(Species.ARCEUS); + }); + + it("returns highest level unfainted if unfainted is true", () => { + const party = scene.getParty(); + party[0].level = 100; + party[0].hp = 0; + party[0].trySetStatus(StatusEffect.FAINT); + party[0].updateInfo(); + party[1].level = 10; + + const result = getHighestLevelPlayerPokemon(scene, true); + expect(result.species.speciesId).toBe(Species.MANAPHY); + }); + }); + + describe("getLowestLevelPokemon", () => { + it("gets lowest level pokemon", () => { + const party = scene.getParty(); + party[0].level = 100; + + const result = getLowestLevelPlayerPokemon(scene); + expect(result.species.speciesId).toBe(Species.MANAPHY); + }); + + it("gets lowest level pokemon at different index", () => { + const party = scene.getParty(); + party[1].level = 100; + + const result = getLowestLevelPlayerPokemon(scene); + expect(result.species.speciesId).toBe(Species.ARCEUS); + }); + + it("breaks ties by getting returning lower index", () => { + const party = scene.getParty(); + party[0].level = 100; + party[1].level = 100; + + const result = getLowestLevelPlayerPokemon(scene); + expect(result.species.speciesId).toBe(Species.ARCEUS); + }); + + it("returns lowest level unfainted if unfainted is true", () => { + const party = scene.getParty(); + party[0].level = 10; + party[0].hp = 0; + party[0].trySetStatus(StatusEffect.FAINT); + party[0].updateInfo(); + party[1].level = 100; + + const result = getLowestLevelPlayerPokemon(scene, true); + expect(result.species.speciesId).toBe(Species.MANAPHY); + }); + }); + + describe("getRandomSpeciesByStarterTier", () => { + it("gets species for a starter tier", () => { + const result = getRandomSpeciesByStarterTier(5); + const pokeSpecies = getPokemonSpecies(result); + + expect(pokeSpecies.speciesId).toBe(result); + expect(speciesStarters[result]).toBe(5); + }); + + it("gets species for a starter tier range", () => { + const result = getRandomSpeciesByStarterTier([5, 8]); + const pokeSpecies = getPokemonSpecies(result); + + expect(pokeSpecies.speciesId).toBe(result); + expect(speciesStarters[result]).toBeGreaterThanOrEqual(5); + expect(speciesStarters[result]).toBeLessThanOrEqual(8); + }); + + it("excludes species from search", () => { + // Only 9 tiers are: Koraidon, Miraidon, Arceus, Rayquaza, Kyogre, Groudon, Zacian + const result = getRandomSpeciesByStarterTier(9, [Species.KORAIDON, Species.MIRAIDON, Species.ARCEUS, Species.RAYQUAZA, Species.KYOGRE, Species.GROUDON]); + const pokeSpecies = getPokemonSpecies(result); + expect(pokeSpecies.speciesId).toBe(Species.ZACIAN); + }); + + it("gets species of specified types", () => { + // Only 9 tiers are: Koraidon, Miraidon, Arceus, Rayquaza, Kyogre, Groudon, Zacian + const result = getRandomSpeciesByStarterTier(9, null, [Type.GROUND]); + const pokeSpecies = getPokemonSpecies(result); + expect(pokeSpecies.speciesId).toBe(Species.GROUDON); + }); + }); + + describe("koPlayerPokemon", () => { + it("KOs a pokemon", () => { + const party = scene.getParty(); + const arceus = party[0]; + arceus.hp = 100; + expect(arceus.isAllowedInBattle()).toBe(true); + + koPlayerPokemon(arceus); + expect(arceus.isAllowedInBattle()).toBe(false); + }); + }); + + describe("getTextWithEncounterDialogueTokens", () => { + it("injects dialogue tokens", () => { + scene.currentBattle.mysteryEncounter = new MysteryEncounter(null); + scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value"); + + const result = getTextWithEncounterDialogueTokens(scene, "mysteryEncounter:unit_test_dialogue"); + expect(result).toEqual("valuevalue @ec{testvalue} @ec{test1} value @ec{test\\} @ec{test\\} {test}"); + }); + + it("can perform nested dialogue token injection", () => { + scene.currentBattle.mysteryEncounter = new MysteryEncounter(null); + scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value"); + scene.currentBattle.mysteryEncounter.setDialogueToken("testvalue", "new"); + + const result = getTextWithEncounterDialogueTokens(scene, "mysteryEncounter:unit_test_dialogue"); + expect(result).toEqual("valuevalue new @ec{test1} value @ec{test\\} @ec{test\\} {test}"); + }); + }); + + describe("queueEncounterMessage", () => { + it("queues a message with encounter dialogue tokens", async () => { + scene.currentBattle.mysteryEncounter = new MysteryEncounter(null); + scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value"); + const spy = vi.spyOn(game.scene, "queueMessage"); + const phaseSpy = vi.spyOn(game.scene, "unshiftPhase"); + + queueEncounterMessage(scene, "mysteryEncounter:unit_test_dialogue"); + expect(spy).toHaveBeenCalledWith("valuevalue @ec{testvalue} @ec{test1} value @ec{test\\} @ec{test\\} {test}", null, true); + expect(phaseSpy).toHaveBeenCalledWith(expect.any(MessagePhase)); + }); + }); + + describe("showEncounterText", () => { + it("showText with dialogue tokens", async () => { + scene.currentBattle.mysteryEncounter = new MysteryEncounter(null); + scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value"); + const spy = vi.spyOn(game.scene.ui, "showText"); + + showEncounterText(scene, "mysteryEncounter:unit_test_dialogue"); + expect(spy).toHaveBeenCalledWith("valuevalue @ec{testvalue} @ec{test1} value @ec{test\\} @ec{test\\} {test}", null, expect.any(Function), 0, true); + }); + }); + + describe("showEncounterDialogue", () => { + it("showText with dialogue tokens", async () => { + scene.currentBattle.mysteryEncounter = new MysteryEncounter(null); + scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value"); + const spy = vi.spyOn(game.scene.ui, "showDialogue"); + + showEncounterDialogue(scene, "mysteryEncounter:unit_test_dialogue", "mysteryEncounter:unit_test_dialogue"); + expect(spy).toHaveBeenCalledWith("valuevalue @ec{testvalue} @ec{test1} value @ec{test\\} @ec{test\\} {test}", "valuevalue @ec{testvalue} @ec{test1} value @ec{test\\} @ec{test\\} {test}", null, undefined, 0, 0); + }); + }); + + describe("initBattleWithEnemyConfig", () => { + it("", () => { + + }); + }); + + describe("setCustomEncounterRewards", () => { + it("", () => { + + }); + }); + + describe("selectPokemonForOption", () => { + it("", () => { + + }); + }); + + describe("setEncounterExp", () => { + it("", () => { + + }); + }); + + describe("leaveEncounterWithoutBattle", () => { + it("", () => { + + }); + }); + + describe("handleMysteryEncounterVictory", () => { + it("", () => { + + }); + }); +}); + diff --git a/src/test/mystery-encounter/mystery-encounter.test.ts b/src/test/mystery-encounter/mystery-encounter.test.ts index b8f50070011..2645fbc68ce 100644 --- a/src/test/mystery-encounter/mystery-encounter.test.ts +++ b/src/test/mystery-encounter/mystery-encounter.test.ts @@ -1,9 +1,11 @@ -import {afterEach, beforeAll, beforeEach, describe, it, vi} from "vitest"; +import {afterEach, beforeAll, beforeEach, expect, describe, it, vi} from "vitest"; import * as overrides from "../../overrides"; import GameManager from "#app/test/utils/gameManager"; import Phaser from "phaser"; +import {Species} from "#enums/species"; +import {MysteryEncounterPhase} from "#app/phases/mystery-encounter-phase"; -describe("Mystery Encounter", () => { +describe("Mystery Encounters", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -19,17 +21,29 @@ describe("Mystery Encounter", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(64); - vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(3); + vi.spyOn(overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(256); + vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(11); }); - it("spawns a mystery encounter", async() => { - // await game.runToSummon([ - // Species.CHARIZARD, - // Species.VOLCARONA - // ]); - // expect(game.scene.getCurrentPhase().constructor.name).toBe(EncounterPhase.name); - }, 100000); + it("Spawns a mystery encounter", async() => { + await game.runToMysteryEncounter([ + Species.CHARIZARD, + Species.VOLCARONA + ]); + + await game.phaseInterceptor.to(MysteryEncounterPhase, false); + expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + }); + + it("", async() => { + await game.runToMysteryEncounter([ + Species.CHARIZARD, + Species.VOLCARONA + ]); + + await game.phaseInterceptor.to(MysteryEncounterPhase, false); + expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + }); it("spawns mysterious challengers encounter", async() => { }); diff --git a/src/test/phases/mystery-encounter-phase.test.ts b/src/test/phases/mystery-encounter-phase.test.ts new file mode 100644 index 00000000000..8d8bac79b5a --- /dev/null +++ b/src/test/phases/mystery-encounter-phase.test.ts @@ -0,0 +1,159 @@ +import {afterEach, beforeAll, beforeEach, expect, describe, it, vi} from "vitest"; +import * as overrides from "../../overrides"; +import GameManager from "#app/test/utils/gameManager"; +import Phaser from "phaser"; +import {Species} from "#enums/species"; +import {MysteryEncounterOptionSelectedPhase, MysteryEncounterPhase} from "#app/phases/mystery-encounter-phase"; +import {Mode} from "#app/ui/ui"; +import {Button} from "#enums/buttons"; +import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; +import {MysteryEncounterType} from "#enums/mystery-encounter-type"; +import {MysteryEncounterTier} from "#app/data/mystery-encounter"; + +describe("Mystery Encounter Phases", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + vi.spyOn(overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(256); + vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(11); + }); + + describe("MysteryEncounterPhase", () => { + it("Runs to MysteryEncounterPhase", async() => { + await game.runToMysteryEncounter([ + Species.CHARIZARD, + Species.VOLCARONA + ]); + + await game.phaseInterceptor.to(MysteryEncounterPhase, false); + expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterPhase.name); + }); + + it("Runs MysteryEncounterPhase", async() => { + vi.spyOn(overrides, "MYSTERY_ENCOUNTER_OVERRIDE", "get").mockReturnValue(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); + await game.runToMysteryEncounter([ + Species.CHARIZARD, + Species.VOLCARONA + ]); + + game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => { + // End phase early for test + game.phaseInterceptor.superEndPhase(); + }); + await game.phaseInterceptor.run(MysteryEncounterPhase); + + expect(game.scene.mysteryEncounterData.encounteredEvents.length).toBeGreaterThan(0); + expect(game.scene.mysteryEncounterData.encounteredEvents[0][0]).toEqual(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); + expect(game.scene.mysteryEncounterData.encounteredEvents[0][1]).toEqual(MysteryEncounterTier.UNCOMMON); + expect(game.scene.ui.getMode()).toBe(Mode.MYSTERY_ENCOUNTER); + }); + + it("Selects an option for MysteryEncounterPhase", async() => { + vi.spyOn(overrides, "MYSTERY_ENCOUNTER_OVERRIDE", "get").mockReturnValue(MysteryEncounterType.MYSTERIOUS_CHALLENGERS); + const dialogueSpy = vi.spyOn(game.scene.ui, "showDialogue"); + const messageSpy = vi.spyOn(game.scene.ui, "showText"); + await game.runToMysteryEncounter([ + Species.CHARIZARD, + Species.VOLCARONA + ]); + + game.onNextPrompt("MysteryEncounterPhase", Mode.MYSTERY_ENCOUNTER, () => { + // Select option 1 for encounter + const handler = game.scene.ui.getHandler() as MysteryEncounterUiHandler; + handler.unblockInput(); + handler.processInput(Button.ACTION); + }); + await game.phaseInterceptor.run(MysteryEncounterPhase); + + // After option selected + expect(game.scene.getCurrentPhase().constructor.name).toBe(MysteryEncounterOptionSelectedPhase.name); + expect(game.scene.ui.getMode()).toBe(Mode.MESSAGE); + expect(dialogueSpy).toHaveBeenCalledTimes(1); + expect(messageSpy).toHaveBeenCalledTimes(2); + expect(dialogueSpy).toHaveBeenCalledWith("What's this?", "???", null, expect.any(Function)); + expect(messageSpy).toHaveBeenCalledWith("Mysterious challengers have appeared!", null, expect.any(Function), 750, true); + expect(messageSpy).toHaveBeenCalledWith("The trainer steps forward...", null, expect.any(Function), 750, true); + }); + }); + + describe("MysteryEncounterOptionSelectedPhase", () => { + it("runs phase", () => { + + }); + + it("handles onOptionSelect execution", () => { + + }); + + it("hides intro visuals", () => { + + }); + + it("does not hide intro visuals if option disabled", () => { + + }); + }); + + describe("MysteryEncounterBattlePhase", () => { + it("runs phase", () => { + + }); + + it("handles TRAINER_BATTLE variant", () => { + + }); + + it("handles BOSS_BATTLE variant", () => { + + }); + + it("handles WILD_BATTLE variant", () => { + + }); + + it("handles double battle", () => { + + }); + }); + + describe("MysteryEncounterRewardsPhase", () => { + it("runs phase", () => { + + }); + + it("handles doEncounterRewards", () => { + + }); + + it("handles heal phase if enabled", () => { + + }); + }); + + describe("PostMysteryEncounterPhase", () => { + it("runs phase", () => { + + }); + + it("handles onPostOptionSelect execution", () => { + + }); + + it("runs to next EncounterPhase", () => { + + }); + }); +}); + diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 84d39024dc9..bfe13ee127c 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -33,6 +33,7 @@ import { Species } from "#enums/species"; import { Button } from "#enums/buttons"; import { BattlerIndex } from "#app/battle.js"; import TargetSelectUiHandler from "#app/ui/target-select-ui-handler.js"; +import BattleMessageUiHandler from "#app/ui/battle-message-ui-handler"; /** * Class to manage the game state and transitions between phases. @@ -137,6 +138,30 @@ export default class GameManager { await this.phaseInterceptor.run(EncounterPhase); } + /** + * Runs the game to a mystery encounter phase. + * @param species - Optional array of species for party. + * @returns A promise that resolves when the EncounterPhase ends. + */ + async runToMysteryEncounter(species?: Species[]) { + await this.runToTitle(); + + this.onNextPrompt("TitlePhase", Mode.TITLE, () => { + this.scene.gameMode = getGameMode(GameModes.CLASSIC); + const starters = generateStarter(this.scene, species); + const selectStarterPhase = new SelectStarterPhase(this.scene); + this.scene.pushPhase(new EncounterPhase(this.scene, false)); + selectStarterPhase.initBattle(starters); + }); + + this.onNextPrompt("EncounterPhase", Mode.MESSAGE, () => { + const handler = this.scene.ui.getHandler() as BattleMessageUiHandler; + handler.processInput(Button.ACTION); + }, null, true); + + await this.phaseInterceptor.run(EncounterPhase); + } + /** * Transitions to the start of a battle. * @param species - Optional array of species to start the battle with. diff --git a/src/test/utils/mocks/mocksContainer/mockContainer.ts b/src/test/utils/mocks/mocksContainer/mockContainer.ts index 2e2b9567796..4f8a58f7251 100644 --- a/src/test/utils/mocks/mocksContainer/mockContainer.ts +++ b/src/test/utils/mocks/mocksContainer/mockContainer.ts @@ -33,6 +33,10 @@ export default class MockContainer { // same as remove or destroy } + removeBetween(startIndex, endIndex, destroyChild) { + // Removes multiple children across an index range + } + addedToScene() { // This callback is invoked when this Game Object is added to a Scene. } diff --git a/src/test/utils/mocks/mocksContainer/mockSprite.ts b/src/test/utils/mocks/mocksContainer/mockSprite.ts index 2eb77f81302..ab61f88605b 100644 --- a/src/test/utils/mocks/mocksContainer/mockSprite.ts +++ b/src/test/utils/mocks/mocksContainer/mockSprite.ts @@ -31,6 +31,7 @@ export default class MockSprite { }; this.anims = { pause: () => null, + stop: () => null, }; } diff --git a/src/ui/mystery-encounter-ui-handler.ts b/src/ui/mystery-encounter-ui-handler.ts index 232d916be57..d8981f7da0d 100644 --- a/src/ui/mystery-encounter-ui-handler.ts +++ b/src/ui/mystery-encounter-ui-handler.ts @@ -103,7 +103,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { if (cursor === this.viewPartyIndex) { // Handle view party success = true; - this.clear(); + // this.clear(); this.scene.ui.setMode(Mode.PARTY, PartyUiMode.CHECK, -1, () => { this.scene.ui.setMode(Mode.MYSTERY_ENCOUNTER, true); setTimeout(() => { @@ -116,7 +116,7 @@ export default class MysteryEncounterUiHandler extends UiHandler { } else { const selected = this.filteredEncounterOptions[cursor]; if ((this.scene.getCurrentPhase() as MysteryEncounterPhase).handleOptionSelect(selected, cursor)) { - this.clear(); + // this.clear(); success = true; } else { ui.playError();