Merge branch 'beta' into deep-sea-items
This commit is contained in:
commit
bb6ecb0afe
Binary file not shown.
|
@ -1 +1 @@
|
||||||
Subproject commit 4928231e22a06dce2b55d9b04cd2b283c2ee4afb
|
Subproject commit acad8499a4ca488a9871902de140f635235f309a
|
|
@ -363,28 +363,30 @@ export default class BattleScene extends SceneBase {
|
||||||
/**
|
/**
|
||||||
* Load the variant assets for the given sprite and stores them in {@linkcode variantColorCache}
|
* Load the variant assets for the given sprite and stores them in {@linkcode variantColorCache}
|
||||||
*/
|
*/
|
||||||
loadPokemonVariantAssets(spriteKey: string, fileRoot: string, variant?: Variant) {
|
public async loadPokemonVariantAssets(spriteKey: string, fileRoot: string, variant?: Variant): Promise<void> {
|
||||||
const useExpSprite = this.experimentalSprites && this.hasExpSprite(spriteKey);
|
const useExpSprite = this.experimentalSprites && this.hasExpSprite(spriteKey);
|
||||||
if (useExpSprite) {
|
if (useExpSprite) {
|
||||||
fileRoot = `exp/${fileRoot}`;
|
fileRoot = `exp/${fileRoot}`;
|
||||||
}
|
}
|
||||||
let variantConfig = variantData;
|
let variantConfig = variantData;
|
||||||
fileRoot.split("/").map(p => variantConfig ? variantConfig = variantConfig[p] : null);
|
fileRoot.split("/").map((p) => (variantConfig ? (variantConfig = variantConfig[p]) : null));
|
||||||
const variantSet = variantConfig as VariantSet;
|
const variantSet = variantConfig as VariantSet;
|
||||||
if (variantSet && (variant !== undefined && variantSet[variant] === 1)) {
|
|
||||||
const populateVariantColors = (key: string): Promise<void> => {
|
return new Promise<void>((resolve) => {
|
||||||
return new Promise(resolve => {
|
if (variantSet && variant !== undefined && variantSet[variant] === 1) {
|
||||||
if (variantColorCache.hasOwnProperty(key)) {
|
if (variantColorCache.hasOwnProperty(spriteKey)) {
|
||||||
return resolve();
|
return resolve();
|
||||||
}
|
}
|
||||||
this.cachedFetch(`./images/pokemon/variant/${fileRoot}.json`).then(res => res.json()).then(c => {
|
this.cachedFetch(`./images/pokemon/variant/${fileRoot}.json`)
|
||||||
variantColorCache[key] = c;
|
.then((res) => res.json())
|
||||||
|
.then((c) => {
|
||||||
|
variantColorCache[spriteKey] = c;
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
} else {
|
||||||
};
|
resolve();
|
||||||
populateVariantColors(spriteKey);
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async preload() {
|
async preload() {
|
||||||
|
@ -1191,6 +1193,9 @@ export default class BattleScene extends SceneBase {
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
this.clearPhaseQueue();
|
this.clearPhaseQueue();
|
||||||
|
|
||||||
|
this.ui.freeUIData();
|
||||||
|
this.uiContainer.remove(this.ui, true);
|
||||||
|
this.uiContainer.destroy();
|
||||||
this.children.removeAll(true);
|
this.children.removeAll(true);
|
||||||
this.game.domContainer.innerHTML = "";
|
this.game.domContainer.innerHTML = "";
|
||||||
this.launchBattle();
|
this.launchBattle();
|
||||||
|
@ -1865,7 +1870,7 @@ export default class BattleScene extends SceneBase {
|
||||||
|
|
||||||
generateRandomBiome(waveIndex: integer): Biome {
|
generateRandomBiome(waveIndex: integer): Biome {
|
||||||
const relWave = waveIndex % 250;
|
const relWave = waveIndex % 250;
|
||||||
const biomes = Utils.getEnumValues(Biome).slice(1, Utils.getEnumValues(Biome).filter(b => b >= 40).length * -1);
|
const biomes = Utils.getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END);
|
||||||
const maxDepth = biomeDepths[Biome.END][0] - 2;
|
const maxDepth = biomeDepths[Biome.END][0] - 2;
|
||||||
const depthWeights = new Array(maxDepth + 1).fill(null)
|
const depthWeights = new Array(maxDepth + 1).fill(null)
|
||||||
.map((_, i: integer) => ((1 - Math.min(Math.abs((i / (maxDepth - 1)) - (relWave / 250)) + 0.25, 1)) / 0.75) * 250);
|
.map((_, i: integer) => ((1 - Math.min(Math.abs((i / (maxDepth - 1)) - (relWave / 250)) + 0.25, 1)) / 0.75) * 250);
|
||||||
|
@ -1878,9 +1883,9 @@ export default class BattleScene extends SceneBase {
|
||||||
|
|
||||||
const randInt = Utils.randSeedInt(totalWeight);
|
const randInt = Utils.randSeedInt(totalWeight);
|
||||||
|
|
||||||
for (const biome of biomes) {
|
for (let i = 0; i < biomes.length; i++) {
|
||||||
if (randInt < biomeThresholds[biome]) {
|
if (randInt < biomeThresholds[i]) {
|
||||||
return biome;
|
return biomes[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/mod
|
||||||
import type { PokeballType } from "#enums/pokeball";
|
import type { PokeballType } from "#enums/pokeball";
|
||||||
import { trainerConfigs } from "#app/data/trainer-config";
|
import { trainerConfigs } from "#app/data/trainer-config";
|
||||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||||
import type { EnemyPokemon, PlayerPokemon, QueuedMove } from "#app/field/pokemon";
|
import type { EnemyPokemon, PlayerPokemon, TurnMove } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
import { BattleSpec } from "#enums/battle-spec";
|
import { BattleSpec } from "#enums/battle-spec";
|
||||||
|
@ -45,12 +45,12 @@ export enum BattlerIndex {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TurnCommand {
|
export interface TurnCommand {
|
||||||
command: Command;
|
command: Command;
|
||||||
cursor?: number;
|
cursor?: number;
|
||||||
move?: QueuedMove;
|
move?: TurnMove;
|
||||||
targets?: BattlerIndex[];
|
targets?: BattlerIndex[];
|
||||||
skip?: boolean;
|
skip?: boolean;
|
||||||
args?: any[];
|
args?: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FaintLogEntry {
|
export interface FaintLogEntry {
|
||||||
|
|
|
@ -612,7 +612,7 @@ export class InterruptedTag extends BattlerTag {
|
||||||
super.onAdd(pokemon);
|
super.onAdd(pokemon);
|
||||||
|
|
||||||
pokemon.getMoveQueue().shift();
|
pokemon.getMoveQueue().shift();
|
||||||
pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.OTHER });
|
pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.OTHER, targets: []});
|
||||||
}
|
}
|
||||||
|
|
||||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||||
|
|
|
@ -88,6 +88,11 @@ export enum ChallengeType {
|
||||||
* Modifies what weight AI pokemon have when generating movesets. UNIMPLEMENTED.
|
* Modifies what weight AI pokemon have when generating movesets. UNIMPLEMENTED.
|
||||||
*/
|
*/
|
||||||
MOVE_WEIGHT,
|
MOVE_WEIGHT,
|
||||||
|
/**
|
||||||
|
* Modifies what the pokemon stats for Flip Stat Mode.
|
||||||
|
*/
|
||||||
|
FLIP_STAT,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -405,6 +410,16 @@ export abstract class Challenge {
|
||||||
applyMoveWeight(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.IntegerHolder): boolean {
|
applyMoveWeight(pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, level: Utils.IntegerHolder): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An apply function for FlipStats. Derived classes should alter this.
|
||||||
|
* @param pokemon {@link Pokemon} What pokemon would learn the move.
|
||||||
|
* @param baseStats What are the stats to flip.
|
||||||
|
* @returns {@link boolean} Whether this function did anything.
|
||||||
|
*/
|
||||||
|
applyFlipStat(pokemon: Pokemon, baseStats: number[]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChallengeCondition = (data: GameData) => boolean;
|
type ChallengeCondition = (data: GameData) => boolean;
|
||||||
|
@ -705,6 +720,33 @@ export class InverseBattleChallenge extends Challenge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements a flip stat challenge.
|
||||||
|
*/
|
||||||
|
export class FlipStatChallenge extends Challenge {
|
||||||
|
constructor() {
|
||||||
|
super(Challenges.FLIP_STAT, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
override applyFlipStat(pokemon: Pokemon, baseStats: number[]) {
|
||||||
|
const origStats = Utils.deepCopy(baseStats);
|
||||||
|
baseStats[0] = origStats[5];
|
||||||
|
baseStats[1] = origStats[4];
|
||||||
|
baseStats[2] = origStats[3];
|
||||||
|
baseStats[3] = origStats[2];
|
||||||
|
baseStats[4] = origStats[1];
|
||||||
|
baseStats[5] = origStats[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static loadChallenge(source: FlipStatChallenge | any): FlipStatChallenge {
|
||||||
|
const newChallenge = new FlipStatChallenge();
|
||||||
|
newChallenge.value = source.value;
|
||||||
|
newChallenge.severity = source.severity;
|
||||||
|
return newChallenge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lowers the amount of starter points available.
|
* Lowers the amount of starter points available.
|
||||||
*/
|
*/
|
||||||
|
@ -890,6 +932,9 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
|
||||||
* @returns True if any challenge was successfully applied.
|
* @returns True if any challenge was successfully applied.
|
||||||
*/
|
*/
|
||||||
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.MOVE_WEIGHT, pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, weight: Utils.IntegerHolder): boolean;
|
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.MOVE_WEIGHT, pokemon: Pokemon, moveSource: MoveSourceType, move: Moves, weight: Utils.IntegerHolder): boolean;
|
||||||
|
|
||||||
|
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.FLIP_STAT, pokemon: Pokemon, baseStats: number[]): boolean;
|
||||||
|
|
||||||
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType, ...args: any[]): boolean {
|
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType, ...args: any[]): boolean {
|
||||||
let ret = false;
|
let ret = false;
|
||||||
gameMode.challenges.forEach(c => {
|
gameMode.challenges.forEach(c => {
|
||||||
|
@ -934,6 +979,9 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
|
||||||
case ChallengeType.MOVE_WEIGHT:
|
case ChallengeType.MOVE_WEIGHT:
|
||||||
ret ||= c.applyMoveWeight(args[0], args[1], args[2], args[3]);
|
ret ||= c.applyMoveWeight(args[0], args[1], args[2], args[3]);
|
||||||
break;
|
break;
|
||||||
|
case ChallengeType.FLIP_STAT:
|
||||||
|
ret ||= c.applyFlipStat(args[0], args[1]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -959,6 +1007,8 @@ export function copyChallenge(source: Challenge | any): Challenge {
|
||||||
return FreshStartChallenge.loadChallenge(source);
|
return FreshStartChallenge.loadChallenge(source);
|
||||||
case Challenges.INVERSE_BATTLE:
|
case Challenges.INVERSE_BATTLE:
|
||||||
return InverseBattleChallenge.loadChallenge(source);
|
return InverseBattleChallenge.loadChallenge(source);
|
||||||
|
case Challenges.FLIP_STAT:
|
||||||
|
return FlipStatChallenge.loadChallenge(source);
|
||||||
}
|
}
|
||||||
throw new Error("Unknown challenge copied");
|
throw new Error("Unknown challenge copied");
|
||||||
}
|
}
|
||||||
|
@ -971,5 +1021,6 @@ export function initChallenges() {
|
||||||
new SingleTypeChallenge(),
|
new SingleTypeChallenge(),
|
||||||
new FreshStartChallenge(),
|
new FreshStartChallenge(),
|
||||||
new InverseBattleChallenge(),
|
new InverseBattleChallenge(),
|
||||||
|
new FlipStatChallenge()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import type { PokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||||
import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
import PokemonSpecies, { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
||||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||||
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
||||||
|
import { Biome } from "#app/enums/biome";
|
||||||
|
|
||||||
export interface DailyRunConfig {
|
export interface DailyRunConfig {
|
||||||
seed: integer;
|
seed: integer;
|
||||||
|
@ -71,3 +72,76 @@ function getDailyRunStarter(starterSpeciesForm: PokemonSpeciesForm, startingLeve
|
||||||
pokemon.destroy();
|
pokemon.destroy();
|
||||||
return starter;
|
return starter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface BiomeWeights {
|
||||||
|
[key: integer]: integer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initially weighted by amount of exits each biome has
|
||||||
|
// Town and End are set to 0 however
|
||||||
|
// And some other biomes were balanced +1/-1 based on average size of the total daily.
|
||||||
|
const dailyBiomeWeights: BiomeWeights = {
|
||||||
|
[Biome.CAVE]: 3,
|
||||||
|
[Biome.LAKE]: 3,
|
||||||
|
[Biome.PLAINS]: 3,
|
||||||
|
[Biome.SNOWY_FOREST]: 3,
|
||||||
|
[Biome.SWAMP]: 3, // 2 -> 3
|
||||||
|
[Biome.TALL_GRASS]: 3, // 2 -> 3
|
||||||
|
|
||||||
|
[Biome.ABYSS]: 2, // 3 -> 2
|
||||||
|
[Biome.RUINS]: 2,
|
||||||
|
[Biome.BADLANDS]: 2,
|
||||||
|
[Biome.BEACH]: 2,
|
||||||
|
[Biome.CONSTRUCTION_SITE]: 2,
|
||||||
|
[Biome.DESERT]: 2,
|
||||||
|
[Biome.DOJO]: 2, // 3 -> 2
|
||||||
|
[Biome.FACTORY]: 2,
|
||||||
|
[Biome.FAIRY_CAVE]: 2,
|
||||||
|
[Biome.FOREST]: 2,
|
||||||
|
[Biome.GRASS]: 2, // 1 -> 2
|
||||||
|
[Biome.MEADOW]: 2,
|
||||||
|
[Biome.MOUNTAIN]: 2, // 3 -> 2
|
||||||
|
[Biome.SEA]: 2,
|
||||||
|
[Biome.SEABED]: 2,
|
||||||
|
[Biome.SLUM]: 2,
|
||||||
|
[Biome.TEMPLE]: 2, // 3 -> 2
|
||||||
|
[Biome.VOLCANO]: 2,
|
||||||
|
|
||||||
|
[Biome.GRAVEYARD]: 1,
|
||||||
|
[Biome.ICE_CAVE]: 1,
|
||||||
|
[Biome.ISLAND]: 1,
|
||||||
|
[Biome.JUNGLE]: 1,
|
||||||
|
[Biome.LABORATORY]: 1,
|
||||||
|
[Biome.METROPOLIS]: 1,
|
||||||
|
[Biome.POWER_PLANT]: 1,
|
||||||
|
[Biome.SPACE]: 1,
|
||||||
|
[Biome.WASTELAND]: 1,
|
||||||
|
|
||||||
|
[Biome.TOWN]: 0,
|
||||||
|
[Biome.END]: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getDailyStartingBiome(): Biome {
|
||||||
|
const biomes = Utils.getEnumValues(Biome).filter(b => b !== Biome.TOWN && b !== Biome.END);
|
||||||
|
|
||||||
|
let totalWeight = 0;
|
||||||
|
const biomeThresholds: integer[] = [];
|
||||||
|
for (const biome of biomes) {
|
||||||
|
// Keep track of the total weight
|
||||||
|
totalWeight += dailyBiomeWeights[biome];
|
||||||
|
|
||||||
|
// Keep track of each biomes cumulative weight
|
||||||
|
biomeThresholds.push(totalWeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
const randInt = Utils.randSeedInt(totalWeight);
|
||||||
|
|
||||||
|
for (let i = 0; i < biomes.length; i++) {
|
||||||
|
if (randInt < biomeThresholds[i]) {
|
||||||
|
return biomes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback in case something went wrong
|
||||||
|
return biomes[Utils.randSeedInt(biomes.length)];
|
||||||
|
}
|
||||||
|
|
904
src/data/move.ts
904
src/data/move.ts
File diff suppressed because it is too large
Load Diff
|
@ -516,8 +516,7 @@ export abstract class PokemonSpeciesForm {
|
||||||
globalScene.anims.get(spriteKey).frameRate = 10;
|
globalScene.anims.get(spriteKey).frameRate = 10;
|
||||||
}
|
}
|
||||||
const spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant).replace("variant/", "").replace(/_[1-3]$/, "");
|
const spritePath = this.getSpriteAtlasPath(female, formIndex, shiny, variant).replace("variant/", "").replace(/_[1-3]$/, "");
|
||||||
globalScene.loadPokemonVariantAssets(spriteKey, spritePath, variant);
|
globalScene.loadPokemonVariantAssets(spriteKey, spritePath, variant).then(() => resolve());
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
if (startLoad) {
|
if (startLoad) {
|
||||||
if (!globalScene.load.isLoading()) {
|
if (!globalScene.load.isLoading()) {
|
||||||
|
@ -948,19 +947,6 @@ export class PokemonForm extends PokemonSpeciesForm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const noStarterFormKeys: string[] = [
|
|
||||||
SpeciesFormKey.MEGA,
|
|
||||||
SpeciesFormKey.MEGA_X,
|
|
||||||
SpeciesFormKey.MEGA_Y,
|
|
||||||
SpeciesFormKey.PRIMAL,
|
|
||||||
SpeciesFormKey.ORIGIN,
|
|
||||||
SpeciesFormKey.THERIAN,
|
|
||||||
SpeciesFormKey.GIGANTAMAX,
|
|
||||||
SpeciesFormKey.GIGANTAMAX_RAPID,
|
|
||||||
SpeciesFormKey.GIGANTAMAX_SINGLE,
|
|
||||||
SpeciesFormKey.ETERNAMAX
|
|
||||||
].map(k => k.toString());
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to get the daily list of starters with Pokerus.
|
* Method to get the daily list of starters with Pokerus.
|
||||||
* @returns A list of starters with Pokerus
|
* @returns A list of starters with Pokerus
|
||||||
|
@ -1834,7 +1820,7 @@ export function initSpecies() {
|
||||||
new PokemonSpecies(Species.COFAGRIGUS, 5, false, false, false, "Coffin Pokémon", Type.GHOST, null, 1.7, 76.5, Abilities.MUMMY, Abilities.NONE, Abilities.NONE, 483, 58, 50, 145, 95, 105, 30, 90, 50, 169, GrowthRate.MEDIUM_FAST, 50, false),
|
new PokemonSpecies(Species.COFAGRIGUS, 5, false, false, false, "Coffin Pokémon", Type.GHOST, null, 1.7, 76.5, Abilities.MUMMY, Abilities.NONE, Abilities.NONE, 483, 58, 50, 145, 95, 105, 30, 90, 50, 169, GrowthRate.MEDIUM_FAST, 50, false),
|
||||||
new PokemonSpecies(Species.TIRTOUGA, 5, false, false, false, "Prototurtle Pokémon", Type.WATER, Type.ROCK, 0.7, 16.5, Abilities.SOLID_ROCK, Abilities.STURDY, Abilities.SWIFT_SWIM, 355, 54, 78, 103, 53, 45, 22, 45, 50, 71, GrowthRate.MEDIUM_FAST, 87.5, false),
|
new PokemonSpecies(Species.TIRTOUGA, 5, false, false, false, "Prototurtle Pokémon", Type.WATER, Type.ROCK, 0.7, 16.5, Abilities.SOLID_ROCK, Abilities.STURDY, Abilities.SWIFT_SWIM, 355, 54, 78, 103, 53, 45, 22, 45, 50, 71, GrowthRate.MEDIUM_FAST, 87.5, false),
|
||||||
new PokemonSpecies(Species.CARRACOSTA, 5, false, false, false, "Prototurtle Pokémon", Type.WATER, Type.ROCK, 1.2, 81, Abilities.SOLID_ROCK, Abilities.STURDY, Abilities.SWIFT_SWIM, 495, 74, 108, 133, 83, 65, 32, 45, 50, 173, GrowthRate.MEDIUM_FAST, 87.5, false),
|
new PokemonSpecies(Species.CARRACOSTA, 5, false, false, false, "Prototurtle Pokémon", Type.WATER, Type.ROCK, 1.2, 81, Abilities.SOLID_ROCK, Abilities.STURDY, Abilities.SWIFT_SWIM, 495, 74, 108, 133, 83, 65, 32, 45, 50, 173, GrowthRate.MEDIUM_FAST, 87.5, false),
|
||||||
new PokemonSpecies(Species.ARCHEN, 5, false, false, false, "First Bird Pokémon", Type.ROCK, Type.FLYING, 0.5, 9.5, Abilities.DEFEATIST, Abilities.NONE, Abilities.EMERGENCY_EXIT, 401, 55, 112, 45, 74, 45, 70, 45, 50, 71, GrowthRate.MEDIUM_FAST, 87.5, false), //Custom Hidden
|
new PokemonSpecies(Species.ARCHEN, 5, false, false, false, "First Bird Pokémon", Type.ROCK, Type.FLYING, 0.5, 9.5, Abilities.DEFEATIST, Abilities.NONE, Abilities.WIMP_OUT, 401, 55, 112, 45, 74, 45, 70, 45, 50, 71, GrowthRate.MEDIUM_FAST, 87.5, false), //Custom Hidden
|
||||||
new PokemonSpecies(Species.ARCHEOPS, 5, false, false, false, "First Bird Pokémon", Type.ROCK, Type.FLYING, 1.4, 32, Abilities.DEFEATIST, Abilities.NONE, Abilities.EMERGENCY_EXIT, 567, 75, 140, 65, 112, 65, 110, 45, 50, 177, GrowthRate.MEDIUM_FAST, 87.5, false), //Custom Hidden
|
new PokemonSpecies(Species.ARCHEOPS, 5, false, false, false, "First Bird Pokémon", Type.ROCK, Type.FLYING, 1.4, 32, Abilities.DEFEATIST, Abilities.NONE, Abilities.EMERGENCY_EXIT, 567, 75, 140, 65, 112, 65, 110, 45, 50, 177, GrowthRate.MEDIUM_FAST, 87.5, false), //Custom Hidden
|
||||||
new PokemonSpecies(Species.TRUBBISH, 5, false, false, false, "Trash Bag Pokémon", Type.POISON, null, 0.6, 31, Abilities.STENCH, Abilities.STICKY_HOLD, Abilities.AFTERMATH, 329, 50, 50, 62, 40, 62, 65, 190, 50, 66, GrowthRate.MEDIUM_FAST, 50, false),
|
new PokemonSpecies(Species.TRUBBISH, 5, false, false, false, "Trash Bag Pokémon", Type.POISON, null, 0.6, 31, Abilities.STENCH, Abilities.STICKY_HOLD, Abilities.AFTERMATH, 329, 50, 50, 62, 40, 62, 65, 190, 50, 66, GrowthRate.MEDIUM_FAST, 50, false),
|
||||||
new PokemonSpecies(Species.GARBODOR, 5, false, false, false, "Trash Heap Pokémon", Type.POISON, null, 1.9, 107.3, Abilities.STENCH, Abilities.WEAK_ARMOR, Abilities.AFTERMATH, 474, 80, 95, 82, 60, 82, 75, 60, 50, 166, GrowthRate.MEDIUM_FAST, 50, false, true,
|
new PokemonSpecies(Species.GARBODOR, 5, false, false, false, "Trash Heap Pokémon", Type.POISON, null, 1.9, 107.3, Abilities.STENCH, Abilities.WEAK_ARMOR, Abilities.AFTERMATH, 474, 80, 95, 82, 60, 82, 75, 60, 50, 166, GrowthRate.MEDIUM_FAST, 50, false, true,
|
||||||
|
|
|
@ -5,4 +5,5 @@ export enum Challenges {
|
||||||
LOWER_STARTER_POINTS,
|
LOWER_STARTER_POINTS,
|
||||||
FRESH_START,
|
FRESH_START,
|
||||||
INVERSE_BATTLE,
|
INVERSE_BATTLE,
|
||||||
|
FLIP_STAT,
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,11 +215,12 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const shinyPromises: Promise<void>[] = [];
|
||||||
this.spriteConfigs.forEach((config) => {
|
this.spriteConfigs.forEach((config) => {
|
||||||
if (config.isPokemon) {
|
if (config.isPokemon) {
|
||||||
globalScene.loadPokemonAtlas(config.spriteKey, config.fileRoot);
|
globalScene.loadPokemonAtlas(config.spriteKey, config.fileRoot);
|
||||||
if (config.isShiny) {
|
if (config.isShiny) {
|
||||||
globalScene.loadPokemonVariantAssets(config.spriteKey, config.fileRoot, config.variant);
|
shinyPromises.push(globalScene.loadPokemonVariantAssets(config.spriteKey, config.fileRoot, config.variant));
|
||||||
}
|
}
|
||||||
} else if (config.isItem) {
|
} else if (config.isItem) {
|
||||||
globalScene.loadAtlas("items", "");
|
globalScene.loadAtlas("items", "");
|
||||||
|
@ -254,7 +255,7 @@ export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Con
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
resolve();
|
Promise.all(shinyPromises).then(() => resolve());
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!globalScene.load.isLoading()) {
|
if (!globalScene.load.isLoading()) {
|
||||||
|
|
|
@ -1057,6 +1057,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
calculateBaseStats(): number[] {
|
calculateBaseStats(): number[] {
|
||||||
const baseStats = this.getSpeciesForm(true).baseStats.slice(0);
|
const baseStats = this.getSpeciesForm(true).baseStats.slice(0);
|
||||||
|
applyChallenges(globalScene.gameMode, ChallengeType.FLIP_STAT, this, baseStats);
|
||||||
// Shuckle Juice
|
// Shuckle Juice
|
||||||
globalScene.applyModifiers(PokemonBaseStatTotalModifier, this.isPlayer(), this, baseStats);
|
globalScene.applyModifiers(PokemonBaseStatTotalModifier, this.isPlayer(), this, baseStats);
|
||||||
// Old Gateau
|
// Old Gateau
|
||||||
|
@ -3298,7 +3299,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getMoveQueue(): QueuedMove[] {
|
getMoveQueue(): TurnMove[] {
|
||||||
return this.summonData.moveQueue;
|
return this.summonData.moveQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4810,17 +4811,19 @@ export class EnemyPokemon extends Pokemon {
|
||||||
* the Pokemon the move will target.
|
* the Pokemon the move will target.
|
||||||
* @returns this Pokemon's next move in the format {move, moveTargets}
|
* @returns this Pokemon's next move in the format {move, moveTargets}
|
||||||
*/
|
*/
|
||||||
getNextMove(): QueuedMove {
|
getNextMove(): TurnMove {
|
||||||
// If this Pokemon has a move already queued, return it.
|
// If this Pokemon has a move already queued, return it.
|
||||||
const queuedMove = this.getMoveQueue().length
|
const moveQueue = this.getMoveQueue();
|
||||||
? this.getMoveset().find(m => m?.moveId === this.getMoveQueue()[0].move)
|
if (moveQueue.length !== 0) {
|
||||||
: null;
|
const queuedMove = moveQueue[0];
|
||||||
if (queuedMove) {
|
if (queuedMove) {
|
||||||
if (queuedMove.isUsable(this, this.getMoveQueue()[0].ignorePP)) {
|
const moveIndex = this.getMoveset().findIndex(m => m?.moveId === queuedMove.move);
|
||||||
return { move: queuedMove.moveId, targets: this.getMoveQueue()[0].targets, ignorePP: this.getMoveQueue()[0].ignorePP };
|
if ((moveIndex > -1 && this.getMoveset()[moveIndex]!.isUsable(this, queuedMove.ignorePP)) || queuedMove.virtual) {
|
||||||
} else {
|
return queuedMove;
|
||||||
this.getMoveQueue().shift();
|
} else {
|
||||||
return this.getNextMove();
|
this.getMoveQueue().shift();
|
||||||
|
return this.getNextMove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5242,15 +5245,10 @@ export class EnemyPokemon extends Pokemon {
|
||||||
|
|
||||||
export interface TurnMove {
|
export interface TurnMove {
|
||||||
move: Moves;
|
move: Moves;
|
||||||
targets?: BattlerIndex[];
|
targets: BattlerIndex[];
|
||||||
result: MoveResult;
|
result?: MoveResult;
|
||||||
virtual?: boolean;
|
virtual?: boolean;
|
||||||
turn?: number;
|
turn?: number;
|
||||||
}
|
|
||||||
|
|
||||||
export interface QueuedMove {
|
|
||||||
move: Moves;
|
|
||||||
targets: BattlerIndex[];
|
|
||||||
ignorePP?: boolean;
|
ignorePP?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5266,7 +5264,7 @@ export interface AttackMoveResult {
|
||||||
export class PokemonSummonData {
|
export class PokemonSummonData {
|
||||||
/** [Atk, Def, SpAtk, SpDef, Spd, Acc, Eva] */
|
/** [Atk, Def, SpAtk, SpDef, Spd, Acc, Eva] */
|
||||||
public statStages: number[] = [ 0, 0, 0, 0, 0, 0, 0 ];
|
public statStages: number[] = [ 0, 0, 0, 0, 0, 0, 0 ];
|
||||||
public moveQueue: QueuedMove[] = [];
|
public moveQueue: TurnMove[] = [];
|
||||||
public tags: BattlerTag[] = [];
|
public tags: BattlerTag[] = [];
|
||||||
public abilitySuppressed: boolean = false;
|
public abilitySuppressed: boolean = false;
|
||||||
public abilitiesApplied: Abilities[] = [];
|
public abilitiesApplied: Abilities[] = [];
|
||||||
|
|
|
@ -428,7 +428,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prompts reroll of party member species if species already present in the enemy party
|
// Prompts reroll of party member species if species already present in the enemy party
|
||||||
if (this.checkDuplicateSpecies(ret, baseSpecies)) {
|
if (this.checkDuplicateSpecies(baseSpecies.speciesId)) {
|
||||||
console.log("Duplicate species detected, prompting reroll...");
|
console.log("Duplicate species detected, prompting reroll...");
|
||||||
retry = true;
|
retry = true;
|
||||||
}
|
}
|
||||||
|
@ -443,17 +443,23 @@ export default class Trainer extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the enemy trainer already has the Pokemon species in their party
|
* Checks if the enemy trainer already has the Pokemon species in their party
|
||||||
* @param {PokemonSpecies} species {@linkcode PokemonSpecies}
|
* @param baseSpecies - The base {@linkcode Species} of the current Pokemon
|
||||||
* @param {PokemonSpecies} baseSpecies {@linkcode PokemonSpecies} - baseSpecies of the Pokemon if species is forced to evolve
|
|
||||||
* @returns `true` if the species is already present in the party
|
* @returns `true` if the species is already present in the party
|
||||||
*/
|
*/
|
||||||
checkDuplicateSpecies(species: PokemonSpecies, baseSpecies: PokemonSpecies): boolean {
|
checkDuplicateSpecies(baseSpecies: Species): boolean {
|
||||||
const staticPartyPokemon = (signatureSpecies[TrainerType[this.config.trainerType]] ?? []).flat(1);
|
const staticSpecies = (signatureSpecies[TrainerType[this.config.trainerType]] ?? []).flat(1).map(s => {
|
||||||
|
let root = s;
|
||||||
const currentPartySpecies = globalScene.getEnemyParty().map(p => {
|
while (pokemonPrevolutions.hasOwnProperty(root)) {
|
||||||
return p.species.speciesId;
|
root = pokemonPrevolutions[root];
|
||||||
|
}
|
||||||
|
return root;
|
||||||
});
|
});
|
||||||
return currentPartySpecies.includes(species.speciesId) || staticPartyPokemon.includes(baseSpecies.speciesId);
|
|
||||||
|
const currentSpecies = globalScene.getEnemyParty().map(p => {
|
||||||
|
return p.species.getRootSpeciesId();
|
||||||
|
});
|
||||||
|
|
||||||
|
return currentSpecies.includes(baseSpecies) || staticSpecies.includes(baseSpecies);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPartyMemberMatchupScores(trainerSlot: TrainerSlot = TrainerSlot.NONE, forSwitch: boolean = false): [integer, integer][] {
|
getPartyMemberMatchupScores(trainerSlot: TrainerSlot = TrainerSlot.NONE, forSwitch: boolean = false): [integer, integer][] {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { Biome } from "#enums/biome";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { Challenges } from "./enums/challenges";
|
import { Challenges } from "./enums/challenges";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
|
import { getDailyStartingBiome } from "./data/daily-run";
|
||||||
|
|
||||||
export enum GameModes {
|
export enum GameModes {
|
||||||
CLASSIC,
|
CLASSIC,
|
||||||
|
@ -120,7 +121,7 @@ export class GameMode implements GameModeConfig {
|
||||||
getStartingBiome(): Biome {
|
getStartingBiome(): Biome {
|
||||||
switch (this.modeId) {
|
switch (this.modeId) {
|
||||||
case GameModes.DAILY:
|
case GameModes.DAILY:
|
||||||
return globalScene.generateRandomBiome(this.getWaveForDifficulty(1));
|
return getDailyStartingBiome();
|
||||||
default:
|
default:
|
||||||
return Overrides.STARTING_BIOME_OVERRIDE || Biome.TOWN;
|
return Overrides.STARTING_BIOME_OVERRIDE || Biome.TOWN;
|
||||||
}
|
}
|
||||||
|
|
|
@ -319,6 +319,7 @@ export class LoadingScene extends SceneBase {
|
||||||
this.loadSe("pb_move");
|
this.loadSe("pb_move");
|
||||||
this.loadSe("pb_catch");
|
this.loadSe("pb_catch");
|
||||||
this.loadSe("pb_lock");
|
this.loadSe("pb_lock");
|
||||||
|
this.loadSe("crit_throw");
|
||||||
|
|
||||||
this.loadSe("pb_tray_enter");
|
this.loadSe("pb_tray_enter");
|
||||||
this.loadSe("pb_tray_ball");
|
this.loadSe("pb_tray_ball");
|
||||||
|
|
|
@ -64,7 +64,7 @@ export class AttemptCapturePhase extends PokemonPhase {
|
||||||
this.pokeball.setOrigin(0.5, 0.625);
|
this.pokeball.setOrigin(0.5, 0.625);
|
||||||
globalScene.field.add(this.pokeball);
|
globalScene.field.add(this.pokeball);
|
||||||
|
|
||||||
globalScene.playSound("se/pb_throw", isCritical ? { rate: 0.2 } : undefined); // Crit catch throws are higher pitched
|
globalScene.playSound(isCritical ? "se/crit_throw" : "se/pb_throw");
|
||||||
globalScene.time.delayedCall(300, () => {
|
globalScene.time.delayedCall(300, () => {
|
||||||
globalScene.field.moveBelow(this.pokeball as Phaser.GameObjects.GameObject, pokemon);
|
globalScene.field.moveBelow(this.pokeball as Phaser.GameObjects.GameObject, pokemon);
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||||
import { Biome } from "#app/enums/biome";
|
import { Biome } from "#app/enums/biome";
|
||||||
import { Moves } from "#app/enums/moves";
|
import { Moves } from "#app/enums/moves";
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
import type { PlayerPokemon, TurnMove } from "#app/field/pokemon";
|
||||||
import { FieldPosition } from "#app/field/pokemon";
|
import { FieldPosition } from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { Command } from "#app/ui/command-ui-handler";
|
import { Command } from "#app/ui/command-ui-handler";
|
||||||
|
@ -86,19 +86,19 @@ export class CommandPhase extends FieldPhase {
|
||||||
const moveQueue = playerPokemon.getMoveQueue();
|
const moveQueue = playerPokemon.getMoveQueue();
|
||||||
|
|
||||||
while (moveQueue.length && moveQueue[0]
|
while (moveQueue.length && moveQueue[0]
|
||||||
&& moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move)
|
&& moveQueue[0].move && !moveQueue[0].virtual && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move)
|
||||||
|| !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable(playerPokemon, moveQueue[0].ignorePP))) { // TODO: is the bang correct?
|
|| !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable(playerPokemon, moveQueue[0].ignorePP))) { // TODO: is the bang correct?
|
||||||
moveQueue.shift();
|
moveQueue.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moveQueue.length) {
|
if (moveQueue.length > 0) {
|
||||||
const queuedMove = moveQueue[0];
|
const queuedMove = moveQueue[0];
|
||||||
if (!queuedMove.move) {
|
if (!queuedMove.move) {
|
||||||
this.handleCommand(Command.FIGHT, -1, false);
|
this.handleCommand(Command.FIGHT, -1);
|
||||||
} else {
|
} else {
|
||||||
const moveIndex = playerPokemon.getMoveset().findIndex(m => m?.moveId === queuedMove.move);
|
const moveIndex = playerPokemon.getMoveset().findIndex(m => m?.moveId === queuedMove.move);
|
||||||
if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex]!.isUsable(playerPokemon, queuedMove.ignorePP)) { // TODO: is the bang correct?
|
if ((moveIndex > -1 && playerPokemon.getMoveset()[moveIndex]!.isUsable(playerPokemon, queuedMove.ignorePP)) || queuedMove.virtual) { // TODO: is the bang correct?
|
||||||
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, { targets: queuedMove.targets, multiple: queuedMove.targets.length > 1 });
|
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, queuedMove);
|
||||||
} else {
|
} else {
|
||||||
globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||||
}
|
}
|
||||||
|
@ -120,12 +120,24 @@ export class CommandPhase extends FieldPhase {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case Command.FIGHT:
|
case Command.FIGHT:
|
||||||
let useStruggle = false;
|
let useStruggle = false;
|
||||||
|
const turnMove: TurnMove | undefined = (args.length === 2 ? (args[1] as TurnMove) : undefined);
|
||||||
if (cursor === -1 ||
|
if (cursor === -1 ||
|
||||||
playerPokemon.trySelectMove(cursor, args[0] as boolean) ||
|
playerPokemon.trySelectMove(cursor, args[0] as boolean) ||
|
||||||
(useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m?.isUsable(playerPokemon)).length)) {
|
(useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m?.isUsable(playerPokemon)).length)) {
|
||||||
const moveId = !useStruggle ? cursor > -1 ? playerPokemon.getMoveset()[cursor]!.moveId : Moves.NONE : Moves.STRUGGLE; // TODO: is the bang correct?
|
|
||||||
|
let moveId: Moves;
|
||||||
|
if (useStruggle) {
|
||||||
|
moveId = Moves.STRUGGLE;
|
||||||
|
} else if (turnMove !== undefined) {
|
||||||
|
moveId = turnMove.move;
|
||||||
|
} else if (cursor > -1) {
|
||||||
|
moveId = playerPokemon.getMoveset()[cursor]!.moveId;
|
||||||
|
} else {
|
||||||
|
moveId = Moves.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args };
|
const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args };
|
||||||
const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, moveId) : args[2];
|
const moveTargets: MoveTargetSet = turnMove === undefined ? getMoveTargets(playerPokemon, moveId) : { targets: turnMove.targets, multiple: turnMove.targets.length > 1 };
|
||||||
if (!moveId) {
|
if (!moveId) {
|
||||||
turnCommand.targets = [ this.fieldIndex ];
|
turnCommand.targets = [ this.fieldIndex ];
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,11 +296,6 @@ export class MovePhase extends BattlePhase {
|
||||||
globalScene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed));
|
globalScene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the battle's "last move" pointer, unless we're currently mimicking a move.
|
|
||||||
if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) {
|
|
||||||
globalScene.currentBattle.lastMove = this.move.moveId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the move is successful (meaning that its damage/effects can be attempted)
|
* Determine if the move is successful (meaning that its damage/effects can be attempted)
|
||||||
* by checking that all of the following are true:
|
* by checking that all of the following are true:
|
||||||
|
@ -324,6 +319,14 @@ export class MovePhase extends BattlePhase {
|
||||||
|
|
||||||
const success = passesConditions && !failedDueToWeather && !failedDueToTerrain;
|
const success = passesConditions && !failedDueToWeather && !failedDueToTerrain;
|
||||||
|
|
||||||
|
// Update the battle's "last move" pointer, unless we're currently mimicking a move.
|
||||||
|
if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) {
|
||||||
|
// The last move used is unaffected by moves that fail
|
||||||
|
if (success) {
|
||||||
|
globalScene.currentBattle.lastMove = this.move.moveId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the move has not failed, trigger ability-based user type changes and then execute it.
|
* If the move has not failed, trigger ability-based user type changes and then execute it.
|
||||||
*
|
*
|
||||||
|
@ -518,7 +521,7 @@ export class MovePhase extends BattlePhase {
|
||||||
frenzyMissFunc(this.pokemon, this.move.getMove());
|
frenzyMissFunc(this.pokemon, this.move.getMove());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL });
|
this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL, targets: this.targets });
|
||||||
|
|
||||||
this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
|
this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
|
||||||
this.pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE);
|
this.pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE);
|
||||||
|
|
|
@ -5,7 +5,7 @@ import i18next from "i18next";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
import type { Challenge } from "#app/data/challenge";
|
import type { Challenge } from "#app/data/challenge";
|
||||||
import { FreshStartChallenge, SingleGenerationChallenge, SingleTypeChallenge, InverseBattleChallenge } from "#app/data/challenge";
|
import { FlipStatChallenge, FreshStartChallenge, SingleGenerationChallenge, SingleTypeChallenge, InverseBattleChallenge } from "#app/data/challenge";
|
||||||
import type { ConditionFn } from "#app/@types/common";
|
import type { ConditionFn } from "#app/@types/common";
|
||||||
import { Stat, getShortenedStatKey } from "#app/enums/stat";
|
import { Stat, getShortenedStatKey } from "#app/enums/stat";
|
||||||
import { Challenges } from "#app/enums/challenges";
|
import { Challenges } from "#app/enums/challenges";
|
||||||
|
@ -280,6 +280,10 @@ export function getAchievementDescription(localizationKey: string): string {
|
||||||
return i18next.t("achv:FRESH_START.description", { context: genderStr });
|
return i18next.t("achv:FRESH_START.description", { context: genderStr });
|
||||||
case "INVERSE_BATTLE":
|
case "INVERSE_BATTLE":
|
||||||
return i18next.t("achv:INVERSE_BATTLE.description", { context: genderStr });
|
return i18next.t("achv:INVERSE_BATTLE.description", { context: genderStr });
|
||||||
|
case "FLIP_STATS":
|
||||||
|
return i18next.t("achv:FLIP_STATS.description", { context: genderStr });
|
||||||
|
case "FLIP_INVERSE":
|
||||||
|
return i18next.t("achv:FLIP_INVERSE.description", { context: genderStr });
|
||||||
case "BREEDERS_IN_SPACE":
|
case "BREEDERS_IN_SPACE":
|
||||||
return i18next.t("achv:BREEDERS_IN_SPACE.description", { context: genderStr });
|
return i18next.t("achv:BREEDERS_IN_SPACE.description", { context: genderStr });
|
||||||
default:
|
default:
|
||||||
|
@ -288,6 +292,7 @@ export function getAchievementDescription(localizationKey: string): string {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const achvs = {
|
export const achvs = {
|
||||||
_10K_MONEY: new MoneyAchv("10K_MONEY", "", 10000, "nugget", 10),
|
_10K_MONEY: new MoneyAchv("10K_MONEY", "", 10000, "nugget", 10),
|
||||||
_100K_MONEY: new MoneyAchv("100K_MONEY", "", 100000, "big_nugget", 25).setSecret(true),
|
_100K_MONEY: new MoneyAchv("100K_MONEY", "", 100000, "big_nugget", 25).setSecret(true),
|
||||||
|
@ -330,35 +335,37 @@ export const achvs = {
|
||||||
PERFECT_IVS: new Achv("PERFECT_IVS", "", "PERFECT_IVS.description", "blunder_policy", 100),
|
PERFECT_IVS: new Achv("PERFECT_IVS", "", "PERFECT_IVS.description", "blunder_policy", 100),
|
||||||
CLASSIC_VICTORY: new Achv("CLASSIC_VICTORY", "", "CLASSIC_VICTORY.description", "relic_crown", 150, (_) => globalScene.gameData.gameStats.sessionsWon === 0),
|
CLASSIC_VICTORY: new Achv("CLASSIC_VICTORY", "", "CLASSIC_VICTORY.description", "relic_crown", 150, (_) => globalScene.gameData.gameStats.sessionsWon === 0),
|
||||||
UNEVOLVED_CLASSIC_VICTORY: new Achv("UNEVOLVED_CLASSIC_VICTORY", "", "UNEVOLVED_CLASSIC_VICTORY.description", "eviolite", 175, (_) => globalScene.getPlayerParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)),
|
UNEVOLVED_CLASSIC_VICTORY: new Achv("UNEVOLVED_CLASSIC_VICTORY", "", "UNEVOLVED_CLASSIC_VICTORY.description", "eviolite", 175, (_) => globalScene.getPlayerParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)),
|
||||||
MONO_GEN_ONE_VICTORY: new ChallengeAchv("MONO_GEN_ONE", "", "MONO_GEN_ONE.description", "ribbon_gen1", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 1 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GEN_ONE_VICTORY: new ChallengeAchv("MONO_GEN_ONE", "", "MONO_GEN_ONE.description", "ribbon_gen1", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 1 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_GEN_TWO_VICTORY: new ChallengeAchv("MONO_GEN_TWO", "", "MONO_GEN_TWO.description", "ribbon_gen2", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 2 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GEN_TWO_VICTORY: new ChallengeAchv("MONO_GEN_TWO", "", "MONO_GEN_TWO.description", "ribbon_gen2", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 2 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_GEN_THREE_VICTORY: new ChallengeAchv("MONO_GEN_THREE", "", "MONO_GEN_THREE.description", "ribbon_gen3", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 3 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GEN_THREE_VICTORY: new ChallengeAchv("MONO_GEN_THREE", "", "MONO_GEN_THREE.description", "ribbon_gen3", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 3 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_GEN_FOUR_VICTORY: new ChallengeAchv("MONO_GEN_FOUR", "", "MONO_GEN_FOUR.description", "ribbon_gen4", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 4 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GEN_FOUR_VICTORY: new ChallengeAchv("MONO_GEN_FOUR", "", "MONO_GEN_FOUR.description", "ribbon_gen4", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 4 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_GEN_FIVE_VICTORY: new ChallengeAchv("MONO_GEN_FIVE", "", "MONO_GEN_FIVE.description", "ribbon_gen5", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 5 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GEN_FIVE_VICTORY: new ChallengeAchv("MONO_GEN_FIVE", "", "MONO_GEN_FIVE.description", "ribbon_gen5", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 5 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_GEN_SIX_VICTORY: new ChallengeAchv("MONO_GEN_SIX", "", "MONO_GEN_SIX.description", "ribbon_gen6", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 6 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GEN_SIX_VICTORY: new ChallengeAchv("MONO_GEN_SIX", "", "MONO_GEN_SIX.description", "ribbon_gen6", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 6 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_GEN_SEVEN_VICTORY: new ChallengeAchv("MONO_GEN_SEVEN", "", "MONO_GEN_SEVEN.description", "ribbon_gen7", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 7 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GEN_SEVEN_VICTORY: new ChallengeAchv("MONO_GEN_SEVEN", "", "MONO_GEN_SEVEN.description", "ribbon_gen7", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 7 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_GEN_EIGHT_VICTORY: new ChallengeAchv("MONO_GEN_EIGHT", "", "MONO_GEN_EIGHT.description", "ribbon_gen8", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 8 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GEN_EIGHT_VICTORY: new ChallengeAchv("MONO_GEN_EIGHT", "", "MONO_GEN_EIGHT.description", "ribbon_gen8", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 8 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_GEN_NINE_VICTORY: new ChallengeAchv("MONO_GEN_NINE", "", "MONO_GEN_NINE.description", "ribbon_gen9", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 9 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GEN_NINE_VICTORY: new ChallengeAchv("MONO_GEN_NINE", "", "MONO_GEN_NINE.description", "ribbon_gen9", 100, (c) => c instanceof SingleGenerationChallenge && c.value === 9 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_NORMAL: new ChallengeAchv("MONO_NORMAL", "", "MONO_NORMAL.description", "silk_scarf", 100, (c) => c instanceof SingleTypeChallenge && c.value === 1 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_NORMAL: new ChallengeAchv("MONO_NORMAL", "", "MONO_NORMAL.description", "silk_scarf", 100, (c) => c instanceof SingleTypeChallenge && c.value === 1 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_FIGHTING: new ChallengeAchv("MONO_FIGHTING", "", "MONO_FIGHTING.description", "black_belt", 100, (c) => c instanceof SingleTypeChallenge && c.value === 2 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_FIGHTING: new ChallengeAchv("MONO_FIGHTING", "", "MONO_FIGHTING.description", "black_belt", 100, (c) => c instanceof SingleTypeChallenge && c.value === 2 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_FLYING: new ChallengeAchv("MONO_FLYING", "", "MONO_FLYING.description", "sharp_beak", 100, (c) => c instanceof SingleTypeChallenge && c.value === 3 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_FLYING: new ChallengeAchv("MONO_FLYING", "", "MONO_FLYING.description", "sharp_beak", 100, (c) => c instanceof SingleTypeChallenge && c.value === 3 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_POISON: new ChallengeAchv("MONO_POISON", "", "MONO_POISON.description", "poison_barb", 100, (c) => c instanceof SingleTypeChallenge && c.value === 4 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_POISON: new ChallengeAchv("MONO_POISON", "", "MONO_POISON.description", "poison_barb", 100, (c) => c instanceof SingleTypeChallenge && c.value === 4 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_GROUND: new ChallengeAchv("MONO_GROUND", "", "MONO_GROUND.description", "soft_sand", 100, (c) => c instanceof SingleTypeChallenge && c.value === 5 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GROUND: new ChallengeAchv("MONO_GROUND", "", "MONO_GROUND.description", "soft_sand", 100, (c) => c instanceof SingleTypeChallenge && c.value === 5 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_ROCK: new ChallengeAchv("MONO_ROCK", "", "MONO_ROCK.description", "hard_stone", 100, (c) => c instanceof SingleTypeChallenge && c.value === 6 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_ROCK: new ChallengeAchv("MONO_ROCK", "", "MONO_ROCK.description", "hard_stone", 100, (c) => c instanceof SingleTypeChallenge && c.value === 6 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_BUG: new ChallengeAchv("MONO_BUG", "", "MONO_BUG.description", "silver_powder", 100, (c) => c instanceof SingleTypeChallenge && c.value === 7 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_BUG: new ChallengeAchv("MONO_BUG", "", "MONO_BUG.description", "silver_powder", 100, (c) => c instanceof SingleTypeChallenge && c.value === 7 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_GHOST: new ChallengeAchv("MONO_GHOST", "", "MONO_GHOST.description", "spell_tag", 100, (c) => c instanceof SingleTypeChallenge && c.value === 8 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GHOST: new ChallengeAchv("MONO_GHOST", "", "MONO_GHOST.description", "spell_tag", 100, (c) => c instanceof SingleTypeChallenge && c.value === 8 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_STEEL: new ChallengeAchv("MONO_STEEL", "", "MONO_STEEL.description", "metal_coat", 100, (c) => c instanceof SingleTypeChallenge && c.value === 9 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_STEEL: new ChallengeAchv("MONO_STEEL", "", "MONO_STEEL.description", "metal_coat", 100, (c) => c instanceof SingleTypeChallenge && c.value === 9 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_FIRE: new ChallengeAchv("MONO_FIRE", "", "MONO_FIRE.description", "charcoal", 100, (c) => c instanceof SingleTypeChallenge && c.value === 10 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_FIRE: new ChallengeAchv("MONO_FIRE", "", "MONO_FIRE.description", "charcoal", 100, (c) => c instanceof SingleTypeChallenge && c.value === 10 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_WATER: new ChallengeAchv("MONO_WATER", "", "MONO_WATER.description", "mystic_water", 100, (c) => c instanceof SingleTypeChallenge && c.value === 11 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_WATER: new ChallengeAchv("MONO_WATER", "", "MONO_WATER.description", "mystic_water", 100, (c) => c instanceof SingleTypeChallenge && c.value === 11 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_GRASS: new ChallengeAchv("MONO_GRASS", "", "MONO_GRASS.description", "miracle_seed", 100, (c) => c instanceof SingleTypeChallenge && c.value === 12 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GRASS: new ChallengeAchv("MONO_GRASS", "", "MONO_GRASS.description", "miracle_seed", 100, (c) => c instanceof SingleTypeChallenge && c.value === 12 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_ELECTRIC: new ChallengeAchv("MONO_ELECTRIC", "", "MONO_ELECTRIC.description", "magnet", 100, (c) => c instanceof SingleTypeChallenge && c.value === 13 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_ELECTRIC: new ChallengeAchv("MONO_ELECTRIC", "", "MONO_ELECTRIC.description", "magnet", 100, (c) => c instanceof SingleTypeChallenge && c.value === 13 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_PSYCHIC: new ChallengeAchv("MONO_PSYCHIC", "", "MONO_PSYCHIC.description", "twisted_spoon", 100, (c) => c instanceof SingleTypeChallenge && c.value === 14 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_PSYCHIC: new ChallengeAchv("MONO_PSYCHIC", "", "MONO_PSYCHIC.description", "twisted_spoon", 100, (c) => c instanceof SingleTypeChallenge && c.value === 14 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_ICE: new ChallengeAchv("MONO_ICE", "", "MONO_ICE.description", "never_melt_ice", 100, (c) => c instanceof SingleTypeChallenge && c.value === 15 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_ICE: new ChallengeAchv("MONO_ICE", "", "MONO_ICE.description", "never_melt_ice", 100, (c) => c instanceof SingleTypeChallenge && c.value === 15 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_DRAGON: new ChallengeAchv("MONO_DRAGON", "", "MONO_DRAGON.description", "dragon_fang", 100, (c) => c instanceof SingleTypeChallenge && c.value === 16 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_DRAGON: new ChallengeAchv("MONO_DRAGON", "", "MONO_DRAGON.description", "dragon_fang", 100, (c) => c instanceof SingleTypeChallenge && c.value === 16 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_DARK: new ChallengeAchv("MONO_DARK", "", "MONO_DARK.description", "black_glasses", 100, (c) => c instanceof SingleTypeChallenge && c.value === 17 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_DARK: new ChallengeAchv("MONO_DARK", "", "MONO_DARK.description", "black_glasses", 100, (c) => c instanceof SingleTypeChallenge && c.value === 17 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
MONO_FAIRY: new ChallengeAchv("MONO_FAIRY", "", "MONO_FAIRY.description", "fairy_feather", 100, (c) => c instanceof SingleTypeChallenge && c.value === 18 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_FAIRY: new ChallengeAchv("MONO_FAIRY", "", "MONO_FAIRY.description", "fairy_feather", 100, (c) => c instanceof SingleTypeChallenge && c.value === 18 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
FRESH_START: new ChallengeAchv("FRESH_START", "", "FRESH_START.description", "reviver_seed", 100, (c) => c instanceof FreshStartChallenge && c.value > 0 && !globalScene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
FRESH_START: new ChallengeAchv("FRESH_START", "", "FRESH_START.description", "reviver_seed", 100, (c) => c instanceof FreshStartChallenge && c.value > 0 && !globalScene.gameMode.challenges.some(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
INVERSE_BATTLE: new ChallengeAchv("INVERSE_BATTLE", "", "INVERSE_BATTLE.description", "inverse", 100, c => c instanceof InverseBattleChallenge && c.value > 0),
|
INVERSE_BATTLE: new ChallengeAchv("INVERSE_BATTLE", "", "INVERSE_BATTLE.description", "inverse", 100, (c) => c instanceof InverseBattleChallenge && c.value > 0),
|
||||||
|
FLIP_STATS: new ChallengeAchv("FLIP_STATS", "", "FLIP_STATS.description", "dubious_disc", 100, (c) => c instanceof FlipStatChallenge && c.value > 0),
|
||||||
|
FLIP_INVERSE: new ChallengeAchv("FLIP_INVERSE", "", "FLIP_INVERSE.description", "cracked_pot", 100, (c) => c instanceof FlipStatChallenge && c.value > 0 && globalScene.gameMode.challenges.every(c => [ Challenges.INVERSE_BATTLE, Challenges.FLIP_STAT ].includes(c.id) && c.value > 0)),
|
||||||
BREEDERS_IN_SPACE: new Achv("BREEDERS_IN_SPACE", "", "BREEDERS_IN_SPACE.description", "moon_stone", 50).setSecret(),
|
BREEDERS_IN_SPACE: new Achv("BREEDERS_IN_SPACE", "", "BREEDERS_IN_SPACE.description", "moon_stone", 50).setSecret(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import type { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||||
import type Pokemon from "#app/field/pokemon";
|
import type Pokemon from "#app/field/pokemon";
|
||||||
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||||
import { allSpecies, getPokemonSpecies, noStarterFormKeys } from "#app/data/pokemon-species";
|
import { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { speciesStarterCosts } from "#app/data/balance/starters";
|
import { speciesStarterCosts } from "#app/data/balance/starters";
|
||||||
import * as Utils from "#app/utils";
|
import * as Utils from "#app/utils";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
|
@ -1619,9 +1619,6 @@ export class GameData {
|
||||||
const dexEntry = this.dexData[species.speciesId];
|
const dexEntry = this.dexData[species.speciesId];
|
||||||
const caughtAttr = dexEntry.caughtAttr;
|
const caughtAttr = dexEntry.caughtAttr;
|
||||||
const formIndex = pokemon.formIndex;
|
const formIndex = pokemon.formIndex;
|
||||||
if (noStarterFormKeys.includes(pokemon.getFormKey())) {
|
|
||||||
pokemon.formIndex = 0;
|
|
||||||
}
|
|
||||||
const dexAttr = pokemon.getDexAttr();
|
const dexAttr = pokemon.getDexAttr();
|
||||||
pokemon.formIndex = formIndex;
|
pokemon.formIndex = formIndex;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { Stat } from "#app/enums/stat";
|
||||||
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
|
import { CommandPhase } from "#app/phases/command-phase";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Assist", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
// Manual moveset overrides are required for the player pokemon in these tests
|
||||||
|
// because the normal moveset override doesn't allow for accurate testing of moveset changes
|
||||||
|
game.override
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.battleType("double")
|
||||||
|
.disableCrits()
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyLevel(100)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.SPLASH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should only use an ally's moves", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.SWORDS_DANCE);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS, Species.SHUCKLE ]);
|
||||||
|
|
||||||
|
const [ feebas, shuckle ] = game.scene.getPlayerField();
|
||||||
|
// These are all moves Assist cannot call; Sketch will be used to test that it can call other moves properly
|
||||||
|
game.move.changeMoveset(feebas, [ Moves.ASSIST, Moves.SKETCH, Moves.PROTECT, Moves.DRAGON_TAIL ]);
|
||||||
|
game.move.changeMoveset(shuckle, [ Moves.ASSIST, Moves.SKETCH, Moves.PROTECT, Moves.DRAGON_TAIL ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.ASSIST, 0);
|
||||||
|
game.move.select(Moves.SKETCH, 1);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER ]);
|
||||||
|
// Player_2 uses Sketch, copies Swords Dance, Player_1 uses Assist, uses Player_2's Sketched Swords Dance
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(2); // Stat raised from Assist -> Swords Dance
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail if there are no allies", async () => {
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
const feebas = game.scene.getPlayerPokemon()!;
|
||||||
|
game.move.changeMoveset(feebas, [ Moves.ASSIST, Moves.SKETCH, Moves.PROTECT, Moves.DRAGON_TAIL ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.ASSIST, 0);
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail if ally has no usable moves and user has usable moves", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.SWORDS_DANCE);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS, Species.SHUCKLE ]);
|
||||||
|
|
||||||
|
const [ feebas, shuckle ] = game.scene.getPlayerField();
|
||||||
|
game.move.changeMoveset(feebas, [ Moves.ASSIST, Moves.SKETCH, Moves.PROTECT, Moves.DRAGON_TAIL ]);
|
||||||
|
game.move.changeMoveset(shuckle, [ Moves.ASSIST, Moves.SKETCH, Moves.PROTECT, Moves.DRAGON_TAIL ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SKETCH, 0);
|
||||||
|
game.move.select(Moves.PROTECT, 1);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2 ]);
|
||||||
|
// Player uses Sketch to copy Swords Dance, Player_2 stalls a turn. Player will attempt Assist and should have no usable moves
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.move.select(Moves.ASSIST, 0);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase);
|
||||||
|
game.move.select(Moves.PROTECT, 1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply secondary effects of a move", async () => {
|
||||||
|
game.override.moveset([ Moves.ASSIST, Moves.WOOD_HAMMER, Moves.WOOD_HAMMER, Moves.WOOD_HAMMER ]);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS, Species.SHUCKLE ]);
|
||||||
|
|
||||||
|
const [ feebas, shuckle ] = game.scene.getPlayerField();
|
||||||
|
game.move.changeMoveset(feebas, [ Moves.ASSIST, Moves.SKETCH, Moves.PROTECT, Moves.DRAGON_TAIL ]);
|
||||||
|
game.move.changeMoveset(shuckle, [ Moves.ASSIST, Moves.SKETCH, Moves.PROTECT, Moves.DRAGON_TAIL ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.ASSIST, 0);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase);
|
||||||
|
game.move.select(Moves.ASSIST, 1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.isFullHp()).toBeFalsy(); // should receive recoil damage from Wood Hammer
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { allMoves, RandomMoveAttr } from "#app/data/move";
|
||||||
|
import { Stat } from "#app/enums/stat";
|
||||||
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Copycat", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
const randomMoveAttr = allMoves[Moves.METRONOME].getAttrs(RandomMoveAttr)[0];
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.COPYCAT, Moves.SPIKY_SHIELD, Moves.SWORDS_DANCE, Moves.SPLASH ])
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.battleType("single")
|
||||||
|
.disableCrits()
|
||||||
|
.starterSpecies(Species.FEEBAS)
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.SPLASH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should copy the last move successfully executed", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.SUCKER_PUNCH);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
game.move.select(Moves.SWORDS_DANCE);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
game.move.select(Moves.COPYCAT); // Last successful move should be Swords Dance
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail when the last move used is not a valid Copycat move", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.PROTECT); // Protect is not a valid move for Copycat to copy
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
game.move.select(Moves.SPIKY_SHIELD); // Spiky Shield is not a valid move for Copycat to copy
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
game.move.select(Moves.COPYCAT);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should copy the called move when the last move successfully calls another", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.SPLASH, Moves.METRONOME ])
|
||||||
|
.enemyMoveset(Moves.COPYCAT);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(Moves.SWORDS_DANCE);
|
||||||
|
|
||||||
|
game.move.select(Moves.METRONOME);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); // Player moves first, so enemy can copy Swords Dance
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply secondary effects of a move", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.ACID_SPRAY); // Secondary effect lowers SpDef by 2 stages
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
game.move.select(Moves.COPYCAT);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.SPDEF)).toBe(-2);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,113 @@
|
||||||
|
import { RechargingTag, SemiInvulnerableTag } from "#app/data/battler-tags";
|
||||||
|
import { allMoves, RandomMoveAttr } from "#app/data/move";
|
||||||
|
import { Abilities } from "#app/enums/abilities";
|
||||||
|
import { Stat } from "#app/enums/stat";
|
||||||
|
import { CommandPhase } from "#app/phases/command-phase";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Metronome", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
const randomMoveAttr = allMoves[Moves.METRONOME].getAttrs(RandomMoveAttr)[0];
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.METRONOME, Moves.SPLASH ])
|
||||||
|
.battleType("single")
|
||||||
|
.startingLevel(100)
|
||||||
|
.starterSpecies(Species.REGIELEKI)
|
||||||
|
.enemyLevel(100)
|
||||||
|
.enemySpecies(Species.SHUCKLE)
|
||||||
|
.enemyMoveset(Moves.SPLASH)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have one semi-invulnerable turn and deal damage on the second turn when a semi-invulnerable move is called", async () => {
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(Moves.DIVE);
|
||||||
|
|
||||||
|
game.move.select(Moves.METRONOME);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(player.getTag(SemiInvulnerableTag)).toBeTruthy();
|
||||||
|
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(player.getTag(SemiInvulnerableTag)).toBeFalsy();
|
||||||
|
expect(enemy.isFullHp()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply secondary effects of a move", async () => {
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(Moves.WOOD_HAMMER);
|
||||||
|
|
||||||
|
game.move.select(Moves.METRONOME);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(player.isFullHp()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should recharge after using recharge move", async () => {
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(Moves.HYPER_BEAM);
|
||||||
|
vi.spyOn(allMoves[Moves.HYPER_BEAM], "accuracy", "get").mockReturnValue(100);
|
||||||
|
|
||||||
|
game.move.select(Moves.METRONOME);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(player.getTag(RechargingTag)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should only target ally for Aromatic Mist", async () => {
|
||||||
|
game.override.battleType("double");
|
||||||
|
await game.classicMode.startBattle([ Species.REGIELEKI, Species.RATTATA ]);
|
||||||
|
const [ leftPlayer, rightPlayer ] = game.scene.getPlayerField();
|
||||||
|
const [ leftOpp, rightOpp ] = game.scene.getEnemyField();
|
||||||
|
vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(Moves.AROMATIC_MIST);
|
||||||
|
|
||||||
|
game.move.select(Moves.METRONOME, 0);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase);
|
||||||
|
game.move.select(Moves.SPLASH, 1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(rightPlayer.getStatStage(Stat.SPDEF)).toBe(1);
|
||||||
|
expect(leftPlayer.getStatStage(Stat.SPDEF)).toBe(0);
|
||||||
|
expect(leftOpp.getStatStage(Stat.SPDEF)).toBe(0);
|
||||||
|
expect(rightOpp.getStatStage(Stat.SPDEF)).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should cause opponent to flee, and not crash for Roar", async () => {
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
vi.spyOn(randomMoveAttr, "getMoveOverride").mockReturnValue(Moves.ROAR);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.METRONOME);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
const isVisible = enemyPokemon.visible;
|
||||||
|
const hasFled = enemyPokemon.switchOutStatus;
|
||||||
|
expect(!isVisible && hasFled).toBe(true);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("CommandPhase");
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { Stat } from "#app/enums/stat";
|
||||||
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Mirror Move", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.MIRROR_MOVE, Moves.SPLASH ])
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.battleType("single")
|
||||||
|
.disableCrits()
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.SPLASH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use the last move that the target used on the user", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("double")
|
||||||
|
.enemyMoveset([ Moves.TACKLE, Moves.GROWL ]);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.MIRROR_MOVE, 0, BattlerIndex.ENEMY); // target's last move is Tackle, enemy should receive damage from Mirror Move copying Tackle
|
||||||
|
game.move.select(Moves.SPLASH, 1);
|
||||||
|
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
|
||||||
|
await game.forceEnemyMove(Moves.GROWL, BattlerIndex.PLAYER_2);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER ]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getEnemyField()[0].isFullHp()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply secondary effects of a move", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.ACID_SPRAY);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.MIRROR_MOVE);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.SPDEF)).toBe(-2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be able to copy status moves", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.GROWL);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.MIRROR_MOVE);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail if the target has not used any moves", async () => {
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.MIRROR_MOVE);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { Stat } from "#app/enums/stat";
|
||||||
|
import { StatusEffect } from "#app/enums/status-effect";
|
||||||
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Sleep Talk", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.SPLASH, Moves.SLEEP_TALK ])
|
||||||
|
.statusEffect(StatusEffect.SLEEP)
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.battleType("single")
|
||||||
|
.disableCrits()
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.SPLASH)
|
||||||
|
.enemyLevel(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail when the user is not asleep", async () => {
|
||||||
|
game.override.statusEffect(StatusEffect.NONE);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SLEEP_TALK);
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail if the user has no valid moves", async () => {
|
||||||
|
game.override.moveset([ Moves.SLEEP_TALK, Moves.DIG, Moves.METRONOME, Moves.SOLAR_BEAM ]);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SLEEP_TALK);
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call a random valid move if the user is asleep", async () => {
|
||||||
|
game.override.moveset([ Moves.SLEEP_TALK, Moves.DIG, Moves.FLY, Moves.SWORDS_DANCE ]); // Dig and Fly are invalid moves, Swords Dance should always be called
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SLEEP_TALK);
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply secondary effects of a move", async () => {
|
||||||
|
game.override.moveset([ Moves.SLEEP_TALK, Moves.DIG, Moves.FLY, Moves.WOOD_HAMMER ]); // Dig and Fly are invalid moves, Wood Hammer should always be called
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
game.move.select(Moves.SLEEP_TALK);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.isFullHp()).toBeFalsy(); // Wood Hammer recoil effect should be applied
|
||||||
|
});
|
||||||
|
});
|
|
@ -125,7 +125,7 @@ describe("Moves - Spit Up", () => {
|
||||||
game.move.select(Moves.SPIT_UP);
|
game.move.select(Moves.SPIT_UP);
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to(TurnInitPhase);
|
||||||
|
|
||||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.FAIL });
|
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.FAIL, targets: [ game.scene.getEnemyPokemon()!.getBattlerIndex() ]});
|
||||||
|
|
||||||
expect(spitUp.calculateBattlePower).not.toHaveBeenCalled();
|
expect(spitUp.calculateBattlePower).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
@ -148,7 +148,7 @@ describe("Moves - Spit Up", () => {
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to(TurnInitPhase);
|
||||||
|
|
||||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS });
|
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS, targets: [ game.scene.getEnemyPokemon()!.getBattlerIndex() ]});
|
||||||
|
|
||||||
expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce();
|
expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce();
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ describe("Moves - Spit Up", () => {
|
||||||
game.move.select(Moves.SPIT_UP);
|
game.move.select(Moves.SPIT_UP);
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to(TurnInitPhase);
|
||||||
|
|
||||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS });
|
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS, targets: [ game.scene.getEnemyPokemon()!.getBattlerIndex() ]});
|
||||||
|
|
||||||
expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce();
|
expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce();
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ describe("Moves - Stockpile", () => {
|
||||||
expect(user.getStatStage(Stat.SPDEF)).toBe(3);
|
expect(user.getStatStage(Stat.SPDEF)).toBe(3);
|
||||||
expect(stockpilingTag).toBeDefined();
|
expect(stockpilingTag).toBeDefined();
|
||||||
expect(stockpilingTag.stockpiledCount).toBe(3);
|
expect(stockpilingTag.stockpiledCount).toBe(3);
|
||||||
expect(user.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ result: MoveResult.FAIL, move: Moves.STOCKPILE });
|
expect(user.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ result: MoveResult.FAIL, move: Moves.STOCKPILE, targets: [ user.getBattlerIndex() ]});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -135,7 +135,7 @@ describe("Moves - Swallow", () => {
|
||||||
game.move.select(Moves.SWALLOW);
|
game.move.select(Moves.SWALLOW);
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to(TurnInitPhase);
|
||||||
|
|
||||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.FAIL });
|
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.FAIL, targets: [ pokemon.getBattlerIndex() ]});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("restores stat stage boosts granted by stacks", () => {
|
describe("restores stat stage boosts granted by stacks", () => {
|
||||||
|
@ -156,7 +156,7 @@ describe("Moves - Swallow", () => {
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to(TurnInitPhase);
|
||||||
|
|
||||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.SUCCESS });
|
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.SUCCESS, targets: [ pokemon.getBattlerIndex() ]});
|
||||||
|
|
||||||
expect(pokemon.getStatStage(Stat.DEF)).toBe(0);
|
expect(pokemon.getStatStage(Stat.DEF)).toBe(0);
|
||||||
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(0);
|
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(0);
|
||||||
|
@ -183,7 +183,7 @@ describe("Moves - Swallow", () => {
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to(TurnInitPhase);
|
||||||
|
|
||||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.SUCCESS });
|
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.SUCCESS, targets: [ pokemon.getBattlerIndex() ]});
|
||||||
|
|
||||||
expect(pokemon.getStatStage(Stat.DEF)).toBe(1);
|
expect(pokemon.getStatStage(Stat.DEF)).toBe(1);
|
||||||
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(-2);
|
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(-2);
|
||||||
|
|
|
@ -364,6 +364,8 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
|
||||||
success = this.setCursor(0);
|
success = this.setCursor(0);
|
||||||
} else if (this.rowCursor < this.shopOptionsRows.length + 1) {
|
} else if (this.rowCursor < this.shopOptionsRows.length + 1) {
|
||||||
success = this.setRowCursor(this.rowCursor + 1);
|
success = this.setRowCursor(this.rowCursor + 1);
|
||||||
|
} else {
|
||||||
|
success = this.setRowCursor(0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Button.DOWN:
|
case Button.DOWN:
|
||||||
|
@ -371,13 +373,15 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
|
||||||
success = this.setRowCursor(this.rowCursor - 1);
|
success = this.setRowCursor(this.rowCursor - 1);
|
||||||
} else if (this.lockRarityButtonContainer.visible && this.cursor === 0) {
|
} else if (this.lockRarityButtonContainer.visible && this.cursor === 0) {
|
||||||
success = this.setCursor(3);
|
success = this.setCursor(3);
|
||||||
|
} else {
|
||||||
|
success = this.setRowCursor(this.shopOptionsRows.length + 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Button.LEFT:
|
case Button.LEFT:
|
||||||
if (!this.rowCursor) {
|
if (!this.rowCursor) {
|
||||||
switch (this.cursor) {
|
switch (this.cursor) {
|
||||||
case 0:
|
case 0:
|
||||||
success = false;
|
success = this.setCursor(2);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
if (this.lockRarityButtonContainer.visible) {
|
if (this.lockRarityButtonContainer.visible) {
|
||||||
|
@ -395,11 +399,21 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 3:
|
||||||
|
if (this.lockRarityButtonContainer.visible) {
|
||||||
|
success = this.setCursor(2);
|
||||||
|
} else {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (this.cursor) {
|
} else if (this.cursor) {
|
||||||
success = this.setCursor(this.cursor - 1);
|
success = this.setCursor(this.cursor - 1);
|
||||||
} else if (this.rowCursor === 1 && this.rerollButtonContainer.visible) {
|
} else {
|
||||||
success = this.setRowCursor(0);
|
if (this.rowCursor === 1 && this.options.length === 0) {
|
||||||
|
success = false;
|
||||||
|
} else {
|
||||||
|
success = this.setCursor(this.getRowItems(this.rowCursor) - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Button.RIGHT:
|
case Button.RIGHT:
|
||||||
|
@ -416,7 +430,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
|
||||||
success = this.setCursor(2);
|
success = this.setCursor(2);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
success = false;
|
success = this.setCursor(0);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
if (this.transferButtonContainer.visible) {
|
if (this.transferButtonContainer.visible) {
|
||||||
|
@ -428,8 +442,12 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
|
||||||
}
|
}
|
||||||
} else if (this.cursor < this.getRowItems(this.rowCursor) - 1) {
|
} else if (this.cursor < this.getRowItems(this.rowCursor) - 1) {
|
||||||
success = this.setCursor(this.cursor + 1);
|
success = this.setCursor(this.cursor + 1);
|
||||||
} else if (this.rowCursor === 1 && this.transferButtonContainer.visible) {
|
} else {
|
||||||
success = this.setRowCursor(0);
|
if (this.rowCursor === 1 && this.options.length === 0) {
|
||||||
|
success = this.setRowCursor(0);
|
||||||
|
} else {
|
||||||
|
success = this.setCursor(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -519,6 +537,14 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
|
||||||
newCursor = 2;
|
newCursor = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Allows to find lock rarity button when looping from the top
|
||||||
|
if (rowCursor === 0 && lastRowCursor > 1 && newCursor === 0 && this.lockRarityButtonContainer.visible) {
|
||||||
|
newCursor = 3;
|
||||||
|
}
|
||||||
|
// Allows to loop to top when lock rarity button is shown
|
||||||
|
if (rowCursor === this.shopOptionsRows.length + 1 && lastRowCursor === 0 && this.cursor === 3) {
|
||||||
|
newCursor = 0;
|
||||||
|
}
|
||||||
this.cursor = -1;
|
this.cursor = -1;
|
||||||
this.setCursor(newCursor);
|
this.setCursor(newCursor);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -157,6 +157,12 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler {
|
||||||
success = (this.cursor === 0) ? this.setCursor(this.cursor) : this.setCursor(this.cursor - 1, cursorPosition);
|
success = (this.cursor === 0) ? this.setCursor(this.cursor) : this.setCursor(this.cursor - 1, cursorPosition);
|
||||||
} else if (this.scrollCursor) {
|
} else if (this.scrollCursor) {
|
||||||
success = this.setScrollCursor(this.scrollCursor - 1, cursorPosition);
|
success = this.setScrollCursor(this.scrollCursor - 1, cursorPosition);
|
||||||
|
} else if ((this.cursor === 0) && (this.scrollCursor === 0)) {
|
||||||
|
this.setScrollCursor(SESSION_SLOTS_COUNT - SLOTS_ON_SCREEN);
|
||||||
|
// Revert to avoid an extra session slot sticking out
|
||||||
|
this.revertSessionSlot(SESSION_SLOTS_COUNT - SLOTS_ON_SCREEN);
|
||||||
|
this.setCursor(SLOTS_ON_SCREEN - 1);
|
||||||
|
success = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Button.DOWN:
|
case Button.DOWN:
|
||||||
|
@ -164,6 +170,11 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler {
|
||||||
success = this.setCursor(this.cursor + 1, cursorPosition);
|
success = this.setCursor(this.cursor + 1, cursorPosition);
|
||||||
} else if (this.scrollCursor < SESSION_SLOTS_COUNT - SLOTS_ON_SCREEN) {
|
} else if (this.scrollCursor < SESSION_SLOTS_COUNT - SLOTS_ON_SCREEN) {
|
||||||
success = this.setScrollCursor(this.scrollCursor + 1, cursorPosition);
|
success = this.setScrollCursor(this.scrollCursor + 1, cursorPosition);
|
||||||
|
} else if ((this.cursor === SLOTS_ON_SCREEN - 1) && (this.scrollCursor === SESSION_SLOTS_COUNT - SLOTS_ON_SCREEN)) {
|
||||||
|
this.setScrollCursor(0);
|
||||||
|
this.revertSessionSlot(SLOTS_ON_SCREEN - 1);
|
||||||
|
this.setCursor(0);
|
||||||
|
success = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Button.RIGHT:
|
case Button.RIGHT:
|
||||||
|
|
|
@ -89,6 +89,13 @@ export class NavigationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes menus from the manager in preparation for reset
|
||||||
|
*/
|
||||||
|
public clearNavigationMenus() {
|
||||||
|
this.navigationMenus.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class NavigationMenu extends Phaser.GameObjects.Container {
|
export default class NavigationMenu extends Phaser.GameObjects.Container {
|
||||||
|
|
|
@ -2698,6 +2698,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
||||||
this.updateScroll();
|
this.updateScroll();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
override destroy(): void {
|
||||||
|
// Without this the reference gets hung up and no startercontainers get GCd
|
||||||
|
this.starterContainers = [];
|
||||||
|
}
|
||||||
|
|
||||||
updateScroll = () => {
|
updateScroll = () => {
|
||||||
const maxColumns = 9;
|
const maxColumns = 9;
|
||||||
const maxRows = 9;
|
const maxRows = 9;
|
||||||
|
|
|
@ -62,4 +62,9 @@ export default abstract class UiHandler {
|
||||||
clear() {
|
clear() {
|
||||||
this.active = false;
|
this.active = false;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* To be implemented by individual handlers when necessary to free memory
|
||||||
|
* Called when {@linkcode BattleScene} is reset
|
||||||
|
*/
|
||||||
|
destroy(): void {}
|
||||||
}
|
}
|
||||||
|
|
11
src/ui/ui.ts
11
src/ui/ui.ts
|
@ -53,6 +53,7 @@ import TestDialogueUiHandler from "#app/ui/test-dialogue-ui-handler";
|
||||||
import AutoCompleteUiHandler from "./autocomplete-ui-handler";
|
import AutoCompleteUiHandler from "./autocomplete-ui-handler";
|
||||||
import { Device } from "#enums/devices";
|
import { Device } from "#enums/devices";
|
||||||
import MysteryEncounterUiHandler from "./mystery-encounter-ui-handler";
|
import MysteryEncounterUiHandler from "./mystery-encounter-ui-handler";
|
||||||
|
import { NavigationManager } from "./settings/navigationMenu";
|
||||||
|
|
||||||
export enum Mode {
|
export enum Mode {
|
||||||
MESSAGE,
|
MESSAGE,
|
||||||
|
@ -614,4 +615,14 @@ export default class UI extends Phaser.GameObjects.Container {
|
||||||
return globalScene.inputMethod;
|
return globalScene.inputMethod;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to free memory held by UI handlers
|
||||||
|
* and clears menus from {@linkcode NavigationManager} to prepare for reset
|
||||||
|
*/
|
||||||
|
public freeUIData(): void {
|
||||||
|
this.handlers.forEach(h => h.destroy());
|
||||||
|
this.handlers = [];
|
||||||
|
NavigationManager.getInstance().clearNavigationMenus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue