start adding unit tests
This commit is contained in:
parent
07ffe1ca64
commit
f803de6b23
|
@ -1,14 +1,14 @@
|
||||||
# 📝 Most immediate things to-do list
|
# 📝 Most immediate things to-do list
|
||||||
|
|
||||||
- ### High priority
|
- ### 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 to filter or change spawn rates in Challenge runs:
|
||||||
- ⚙️ Add a tag system so MEs don't show where they shouldn't and bricking Challenge runs:
|
|
||||||
- noChallenge (cant be spawned in challenge runs)
|
- noChallenge (cant be spawned in challenge runs)
|
||||||
- allChallenge (can spawn in all challenge modes)
|
- allChallenge (can spawn in all challenge modes)
|
||||||
- (typespecific)Challenge:
|
- (typespecific)Challenge:
|
||||||
- Example: fireOnly (can only spawn in fire related challenges)
|
- Example: fireOnly (can only spawn in fire related challenges)
|
||||||
|
|
||||||
- ### Medium priority
|
- ### Medium priority
|
||||||
|
- ⚙️ Update Chest visuals for Mysterious Chest (with animated chest)
|
||||||
|
|
||||||
- ### Low priority
|
- ### Low priority
|
||||||
- 🐛 Mysterious Challengers can spawn two trainers (or three) of the same type [Dev comment: not a bug]
|
- 🐛 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.
|
### 🌟 **Rarity** tier of the ME, common by default.
|
||||||
- ⚪ Common pool
|
- ⚪ Common pool
|
||||||
- 🔵 Rare pool
|
- 🔵 Uncommon pool
|
||||||
- 🟣 Epic pool
|
- 🟣 Rare pool
|
||||||
- 🟡 Legendary pool
|
- 🟡 Super Rare pool
|
||||||
|
|
||||||
### **Optional Requirements** for Mystery Encounters.
|
### **Optional Requirements** for Mystery Encounters.
|
||||||
- 🛠️ They give granular control over whether encounters will spawn in certain situations
|
- 🛠️ 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!):
|
# 📝 Known bugs (squash 'em all!):
|
||||||
- ## 🔴 __**Really bad ones**__
|
- ## 🔴 __**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**__
|
- ## 🟡 __**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
|
- 🐛 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
|
- 🐛 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**__
|
- ## 🟢 __**Non-game breaking**__
|
||||||
- Both of these bugs seem to have in common that they don't "forget" their last passed string:
|
- 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)
|
#### More requirements (with helper functions)
|
||||||
- Having X item
|
- Having X item
|
||||||
- Having Y amount of 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 can learn Y move
|
||||||
- A Pokémon X in player's party knows Y move
|
- A Pokémon X in player's party knows Y move
|
||||||
- A Pokémon X in player's party has Y ability
|
- A Pokémon X in player's party has Y ability
|
||||||
|
|
|
@ -93,7 +93,7 @@ import { UiTheme } from "#enums/ui-theme";
|
||||||
import { TimedEventManager } from "#app/timed-event-manager.js";
|
import { TimedEventManager } from "#app/timed-event-manager.js";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import MysteryEncounter, { MysteryEncounterTier, MysteryEncounterVariant } from "./data/mystery-encounter";
|
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 { MysteryEncounterFlags } from "#app/data/mystery-encounter-flags";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
|
||||||
|
@ -1098,22 +1098,22 @@ export default class BattleScene extends SceneBase {
|
||||||
|
|
||||||
// Check for mystery encounter
|
// Check for mystery encounter
|
||||||
// Can only occur in place of a standard wild battle, waves 10-180
|
// Can only occur in place of a standard wild battle, waves 10-180
|
||||||
// let testStartingWeight = 10;
|
// let testStartingWeight = 0;
|
||||||
// while (testStartingWeight < 30) {
|
// while (testStartingWeight < 20) {
|
||||||
// calculateMEAggregateStats(this, testStartingWeight);
|
// calculateMEAggregateStats(this, testStartingWeight);
|
||||||
// testStartingWeight += 2;
|
// testStartingWeight += 1;
|
||||||
// }
|
// }
|
||||||
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < 180 && newWaveIndex > 10) {
|
if (this.gameMode.hasMysteryEncounters && newBattleType === BattleType.WILD && !this.gameMode.isBoss(newWaveIndex) && newWaveIndex < 180 && newWaveIndex > 10) {
|
||||||
const roll = Utils.randSeedInt(256);
|
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
|
// 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.mysteryEncounterFlags?.encounterSpawnChance) ? this.mysteryEncounterFlags.encounterSpawnChance : BASE_MYSTERY_ENCOUNTER_WEIGHT;
|
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
|
// If total number of encounters is lower than expected for the run, slightly favor a new encounter spawn
|
||||||
// Do the reverse as well
|
// Do the reverse as well
|
||||||
// Reduces occurrence of runs with very few (<6) and a ton (>10) of encounters
|
// 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 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 favoredEncounterRate = sessionEncounterRate + currentRunDiffFromAvg * 5;
|
||||||
|
|
||||||
const successRate = isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE) ? favoredEncounterRate : Overrides.MYSTERY_ENCOUNTER_RATE_OVERRIDE;
|
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) {
|
if (roll < successRate) {
|
||||||
newBattleType = BattleType.MYSTERY_ENCOUNTER;
|
newBattleType = BattleType.MYSTERY_ENCOUNTER;
|
||||||
// Reset base spawn weight
|
// Reset base spawn weight
|
||||||
this.mysteryEncounterFlags.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_WEIGHT;
|
this.mysteryEncounterData.encounterSpawnChance = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
|
||||||
} else {
|
} 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) {
|
if (newBattleType === BattleType.MYSTERY_ENCOUNTER) {
|
||||||
// Disable double battle on mystery encounters (it may be re-enabled as part of encounter)
|
// Disable double battle on mystery encounters (it may be re-enabled as part of encounter)
|
||||||
this.currentBattle.double = false;
|
this.currentBattle.double = false;
|
||||||
|
this.executeWithSeedOffset(() => {
|
||||||
this.currentBattle.mysteryEncounter = this.getMysteryEncounter(mysteryEncounter);
|
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));
|
//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
|
// Check for queued encounters first
|
||||||
if (!encounter && this.mysteryEncounterFlags?.nextEncounterQueue?.length > 0) {
|
if (!encounter && this.mysteryEncounterData?.nextEncounterQueue?.length > 0) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (i < this.mysteryEncounterFlags.nextEncounterQueue.length && !!encounter) {
|
while (i < this.mysteryEncounterData.nextEncounterQueue.length && !!encounter) {
|
||||||
const candidate = this.mysteryEncounterFlags.nextEncounterQueue[i];
|
const candidate = this.mysteryEncounterData.nextEncounterQueue[i];
|
||||||
const forcedChance = candidate[1];
|
const forcedChance = candidate[1];
|
||||||
if (Utils.randSeedInt(100) < forcedChance) {
|
if (Utils.randSeedInt(100) < forcedChance) {
|
||||||
encounter = allMysteryEncounters[candidate[0]];
|
encounter = allMysteryEncounters[candidate[0]];
|
||||||
|
@ -2675,7 +2677,7 @@ export default class BattleScene extends SceneBase {
|
||||||
const tierWeights = [61, 40, 21, 6];
|
const tierWeights = [61, 40, 21, 6];
|
||||||
|
|
||||||
// Adjust tier weights by previously encountered events to lower odds of only common/uncommons in run
|
// 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];
|
const tier = val[1];
|
||||||
if (tier === MysteryEncounterTier.COMMON) {
|
if (tier === MysteryEncounterTier.COMMON) {
|
||||||
tierWeights[0] = tierWeights[0] - 6;
|
tierWeights[0] = tierWeights[0] - 6;
|
||||||
|
@ -2697,7 +2699,7 @@ export default class BattleScene extends SceneBase {
|
||||||
|
|
||||||
let availableEncounters = [];
|
let availableEncounters = [];
|
||||||
// New encounter will never be the same as the most recent encounter
|
// 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);
|
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
|
// If no valid encounters exist at tier, checks next tier down, continuing until there are some encounters available
|
||||||
while (availableEncounters.length === 0 && tier >= 0) {
|
while (availableEncounters.length === 0 && tier >= 0) {
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import {MysteryEncounterTier} from "#app/data/mystery-encounter";
|
import {MysteryEncounterTier} from "#app/data/mystery-encounter";
|
||||||
import {MysteryEncounterType} from "#enums/mystery-encounter-type";
|
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";
|
import {isNullOrUndefined} from "../utils";
|
||||||
|
|
||||||
export class MysteryEncounterFlags {
|
export class MysteryEncounterData {
|
||||||
encounteredEvents: [MysteryEncounterType, MysteryEncounterTier][] = [];
|
encounteredEvents: [MysteryEncounterType, MysteryEncounterTier][] = [];
|
||||||
encounterSpawnChance: number = BASE_MYSTERY_ENCOUNTER_WEIGHT;
|
encounterSpawnChance: number = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
|
||||||
nextEncounterQueue: [MysteryEncounterType, integer][] = [];
|
nextEncounterQueue: [MysteryEncounterType, integer][] = [];
|
||||||
|
|
||||||
constructor(flags: MysteryEncounterFlags) {
|
constructor(flags: MysteryEncounterData) {
|
||||||
if (!isNullOrUndefined(flags)) {
|
if (!isNullOrUndefined(flags)) {
|
||||||
Object.assign(this, flags);
|
Object.assign(this, flags);
|
||||||
}
|
}
|
|
@ -63,11 +63,11 @@ export class PreviousEncounterRequirement extends EncounterSceneRequirement {
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
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] {
|
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()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,10 +27,10 @@ import {BattlerTagType} from "#enums/battler-tag-type";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import {Biome} from "#enums/biome";
|
import {Biome} from "#enums/biome";
|
||||||
import {biomeLinks} from "#app/data/biomes";
|
import {biomeLinks} from "#app/data/biomes";
|
||||||
import {EncounterSceneRequirement} from "#app/data/mystery-encounter-requirements";
|
|
||||||
import {Mode} from "#app/ui/ui";
|
import {Mode} from "#app/ui/ui";
|
||||||
import {PartyOption, PartyUiMode} from "#app/ui/party-ui-handler";
|
import {PartyOption, PartyUiMode} from "#app/ui/party-ui-handler";
|
||||||
import {OptionSelectConfig, OptionSelectItem} from "#app/ui/abstact-option-select-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;
|
return chosenPokemon;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTokensFromScene(scene: BattleScene, reqs: EncounterSceneRequirement[]): Array<[RegExp, String]> {
|
// export function getTokensFromScene(scene: BattleScene, reqs: EncounterSceneRequirement[]): Array<[RegExp, String]> {
|
||||||
const arr = [];
|
// const arr = [];
|
||||||
if (scene) {
|
// if (scene) {
|
||||||
for (const req of reqs) {
|
// for (const req of reqs) {
|
||||||
req.getDialogueToken(scene);
|
// req.getDialogueToken(scene);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return arr;
|
// return arr;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ties are broken by whatever mon is closer to the front of the party
|
* 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;
|
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) {
|
export function koPlayerPokemon(pokemon: PlayerPokemon) {
|
||||||
pokemon.hp = 0;
|
pokemon.hp = 0;
|
||||||
pokemon.trySetStatus(StatusEffect.FAINT);
|
pokemon.trySetStatus(StatusEffect.FAINT);
|
||||||
|
@ -173,48 +215,6 @@ export function showEncounterDialogue(scene: BattleScene, textContentKey: Templa
|
||||||
scene.ui.showDialogue(text, speaker, null, callback, 0, 0);
|
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 {
|
export class EnemyPokemonConfig {
|
||||||
species: PokemonSpecies;
|
species: PokemonSpecies;
|
||||||
isBoss: boolean = false;
|
isBoss: boolean = false;
|
||||||
|
@ -594,10 +594,10 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase:
|
||||||
export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: number) {
|
export function calculateMEAggregateStats(scene: BattleScene, baseSpawnWeight: number) {
|
||||||
const numRuns = 1000;
|
const numRuns = 1000;
|
||||||
let run = 0;
|
let run = 0;
|
||||||
const targetEncountersPerRun = 15;
|
const targetEncountersPerRun = 15; // AVERAGE_ENCOUNTERS_PER_RUN_TARGET
|
||||||
|
|
||||||
const calculateNumEncounters = (): number[] => {
|
const calculateNumEncounters = (): number[] => {
|
||||||
let encounterRate = baseSpawnWeight;
|
let encounterRate = baseSpawnWeight; // BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT
|
||||||
const numEncounters = [0, 0, 0, 0];
|
const numEncounters = [0, 0, 0, 0];
|
||||||
let currentBiome = Biome.TOWN;
|
let currentBiome = Biome.TOWN;
|
||||||
let currentArena = scene.newArena(currentBiome);
|
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];
|
tierValue > commonThreshold ? ++numEncounters[0] : tierValue > uncommonThreshold ? ++numEncounters[1] : tierValue > rareThreshold ? ++numEncounters[2] : ++numEncounters[3];
|
||||||
} else {
|
} else {
|
||||||
encounterRate++;
|
encounterRate += WIGHT_INCREMENT_ON_SPAWN_MISS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,9 @@ import { Biome } from "#app/enums/biome";
|
||||||
import { SleepingSnorlaxEncounter } from "./sleeping-snorlax";
|
import { SleepingSnorlaxEncounter } from "./sleeping-snorlax";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
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 * <number of missed spawns>) / 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 AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 15;
|
||||||
|
|
||||||
export const allMysteryEncounters : {[encounterType:string]: MysteryEncounter} = {};
|
export const allMysteryEncounters : {[encounterType:string]: MysteryEncounter} = {};
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import {SimpleTranslationEntries} from "#app/interfaces/locales";
|
import {SimpleTranslationEntries} from "#app/interfaces/locales";
|
||||||
|
|
||||||
export const mysteryEncounter: SimpleTranslationEntries = {
|
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
|
// Mysterious Encounters -- Common Tier
|
||||||
|
|
||||||
|
|
|
@ -817,7 +817,7 @@ export class EncounterPhase extends BattlePhase {
|
||||||
if (mysteryEncounter.onInit) {
|
if (mysteryEncounter.onInit) {
|
||||||
mysteryEncounter.onInit(this.scene);
|
mysteryEncounter.onInit(this.scene);
|
||||||
}
|
}
|
||||||
mysteryEncounter.populateDialogueTokensFromRequirements();
|
mysteryEncounter.populateDialogueTokensFromRequirements(this.scene);
|
||||||
}, this.scene.currentBattle.waveIndex);
|
}, this.scene.currentBattle.waveIndex);
|
||||||
|
|
||||||
// Add intro visuals for mystery encounter
|
// Add intro visuals for mystery encounter
|
||||||
|
|
|
@ -40,7 +40,7 @@ export class MysteryEncounterPhase extends Phase {
|
||||||
|
|
||||||
// Sets flag that ME was encountered
|
// Sets flag that ME was encountered
|
||||||
// Can be used in later MEs to check for requirements to spawn, etc.
|
// 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
|
// Initiates encounter dialogue window and option select
|
||||||
this.scene.ui.setMode(Mode.MYSTERY_ENCOUNTER);
|
this.scene.ui.setMode(Mode.MYSTERY_ENCOUNTER);
|
||||||
|
@ -55,7 +55,7 @@ export class MysteryEncounterPhase extends Phase {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate dialogue tokens for option requirements
|
// Populate dialogue tokens for option requirements
|
||||||
this.scene.currentBattle.mysteryEncounter.populateDialogueTokensFromRequirements();
|
this.scene.currentBattle.mysteryEncounter.populateDialogueTokensFromRequirements(this.scene);
|
||||||
|
|
||||||
if (option.onPreOptionPhase) {
|
if (option.onPreOptionPhase) {
|
||||||
this.scene.executeWithSeedOffset(async () => {
|
this.scene.executeWithSeedOffset(async () => {
|
||||||
|
|
|
@ -40,7 +40,7 @@ import { GameDataType } from "#enums/game-data-type";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
import { Species } from "#enums/species";
|
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";
|
import MysteryEncounter from "../data/mystery-encounter";
|
||||||
|
|
||||||
export const defaultStarterSpecies: Species[] = [
|
export const defaultStarterSpecies: Species[] = [
|
||||||
|
@ -125,7 +125,7 @@ export interface SessionSaveData {
|
||||||
timestamp: integer;
|
timestamp: integer;
|
||||||
challenges: ChallengeData[];
|
challenges: ChallengeData[];
|
||||||
mysteryEncounter: MysteryEncounter;
|
mysteryEncounter: MysteryEncounter;
|
||||||
mysteryEncounterFlags: MysteryEncounterFlags;
|
mysteryEncounterFlags: MysteryEncounterData;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Unlocks {
|
interface Unlocks {
|
||||||
|
@ -842,7 +842,7 @@ export class GameData {
|
||||||
timestamp: new Date().getTime(),
|
timestamp: new Date().getTime(),
|
||||||
challenges: scene.gameMode.challenges.map(c => new ChallengeData(c)),
|
challenges: scene.gameMode.challenges.map(c => new ChallengeData(c)),
|
||||||
mysteryEncounter: scene.currentBattle.mysteryEncounter,
|
mysteryEncounter: scene.currentBattle.mysteryEncounter,
|
||||||
mysteryEncounterFlags: scene.mysteryEncounterFlags
|
mysteryEncounterFlags: scene.mysteryEncounterData
|
||||||
} as SessionSaveData;
|
} as SessionSaveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -933,7 +933,7 @@ export class GameData {
|
||||||
scene.score = sessionData.score;
|
scene.score = sessionData.score;
|
||||||
scene.updateScoreText();
|
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);
|
scene.newArena(sessionData.arena.biome);
|
||||||
|
|
||||||
|
@ -1159,7 +1159,7 @@ export class GameData {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (k === "mysteryEncounterFlags") {
|
if (k === "mysteryEncounterFlags") {
|
||||||
return new MysteryEncounterFlags(v);
|
return new MysteryEncounterData(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
|
|
|
@ -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("", () => {
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -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 * as overrides from "../../overrides";
|
||||||
import GameManager from "#app/test/utils/gameManager";
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
import Phaser from "phaser";
|
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 phaserGame: Phaser.Game;
|
||||||
let game: GameManager;
|
let game: GameManager;
|
||||||
|
|
||||||
|
@ -19,17 +21,29 @@ describe("Mystery Encounter", () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
vi.spyOn(overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(64);
|
vi.spyOn(overrides, "MYSTERY_ENCOUNTER_RATE_OVERRIDE", "get").mockReturnValue(256);
|
||||||
vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(3);
|
vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(11);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("spawns a mystery encounter", async() => {
|
it("Spawns a mystery encounter", async() => {
|
||||||
// await game.runToSummon([
|
await game.runToMysteryEncounter([
|
||||||
// Species.CHARIZARD,
|
Species.CHARIZARD,
|
||||||
// Species.VOLCARONA
|
Species.VOLCARONA
|
||||||
// ]);
|
]);
|
||||||
// expect(game.scene.getCurrentPhase().constructor.name).toBe(EncounterPhase.name);
|
|
||||||
}, 100000);
|
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() => {
|
it("spawns mysterious challengers encounter", async() => {
|
||||||
});
|
});
|
||||||
|
|
|
@ -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", () => {
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -33,6 +33,7 @@ import { Species } from "#enums/species";
|
||||||
import { Button } from "#enums/buttons";
|
import { Button } from "#enums/buttons";
|
||||||
import { BattlerIndex } from "#app/battle.js";
|
import { BattlerIndex } from "#app/battle.js";
|
||||||
import TargetSelectUiHandler from "#app/ui/target-select-ui-handler.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.
|
* Class to manage the game state and transitions between phases.
|
||||||
|
@ -137,6 +138,30 @@ export default class GameManager {
|
||||||
await this.phaseInterceptor.run(EncounterPhase);
|
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.
|
* Transitions to the start of a battle.
|
||||||
* @param species - Optional array of species to start the battle with.
|
* @param species - Optional array of species to start the battle with.
|
||||||
|
|
|
@ -33,6 +33,10 @@ export default class MockContainer {
|
||||||
// same as remove or destroy
|
// same as remove or destroy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeBetween(startIndex, endIndex, destroyChild) {
|
||||||
|
// Removes multiple children across an index range
|
||||||
|
}
|
||||||
|
|
||||||
addedToScene() {
|
addedToScene() {
|
||||||
// This callback is invoked when this Game Object is added to a Scene.
|
// This callback is invoked when this Game Object is added to a Scene.
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ export default class MockSprite {
|
||||||
};
|
};
|
||||||
this.anims = {
|
this.anims = {
|
||||||
pause: () => null,
|
pause: () => null,
|
||||||
|
stop: () => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,7 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
||||||
if (cursor === this.viewPartyIndex) {
|
if (cursor === this.viewPartyIndex) {
|
||||||
// Handle view party
|
// Handle view party
|
||||||
success = true;
|
success = true;
|
||||||
this.clear();
|
// this.clear();
|
||||||
this.scene.ui.setMode(Mode.PARTY, PartyUiMode.CHECK, -1, () => {
|
this.scene.ui.setMode(Mode.PARTY, PartyUiMode.CHECK, -1, () => {
|
||||||
this.scene.ui.setMode(Mode.MYSTERY_ENCOUNTER, true);
|
this.scene.ui.setMode(Mode.MYSTERY_ENCOUNTER, true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -116,7 +116,7 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
||||||
} else {
|
} else {
|
||||||
const selected = this.filteredEncounterOptions[cursor];
|
const selected = this.filteredEncounterOptions[cursor];
|
||||||
if ((this.scene.getCurrentPhase() as MysteryEncounterPhase).handleOptionSelect(selected, cursor)) {
|
if ((this.scene.getCurrentPhase() as MysteryEncounterPhase).handleOptionSelect(selected, cursor)) {
|
||||||
this.clear();
|
// this.clear();
|
||||||
success = true;
|
success = true;
|
||||||
} else {
|
} else {
|
||||||
ui.playError();
|
ui.playError();
|
||||||
|
|
Loading…
Reference in New Issue