mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2024-11-25 16:26:25 +00:00
Merge branch 'beta' into new-team
This commit is contained in:
commit
f12730880b
Binary file not shown.
41
public/images/trainer/expert_pokemon_breeder.json
Normal file
41
public/images/trainer/expert_pokemon_breeder.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"textures": [
|
||||
{
|
||||
"image": "expert_pokemon_breeder.png",
|
||||
"format": "RGBA8888",
|
||||
"size": {
|
||||
"w": 39,
|
||||
"h": 75
|
||||
},
|
||||
"scale": 1,
|
||||
"frames": [
|
||||
{
|
||||
"filename": "0001.png",
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"sourceSize": {
|
||||
"w": 80,
|
||||
"h": 80
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 21,
|
||||
"y": 3,
|
||||
"w": 39,
|
||||
"h": 75
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 39,
|
||||
"h": 75
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"app": "https://www.codeandweb.com/texturepacker",
|
||||
"version": "3.0",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:cb681265d8dca038a518ab14076fd140:18ff41b1ef6967682643a11695926e58:c59ea3971195f5a395b75223a77d9068$"
|
||||
}
|
||||
}
|
BIN
public/images/trainer/expert_pokemon_breeder.png
Normal file
BIN
public/images/trainer/expert_pokemon_breeder.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1006 B |
@ -16,6 +16,14 @@ import { TrainerType } from "#enums/trainer-type";
|
||||
import i18next from "#app/plugins/i18n";
|
||||
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
import { CustomModifierSettings } from "#app/modifier/modifier-type";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
|
||||
export enum ClassicFixedBossWaves {
|
||||
// TODO: other fixed wave battles should be added here
|
||||
EVIL_BOSS_1 = 115,
|
||||
EVIL_BOSS_2 = 165,
|
||||
}
|
||||
|
||||
export enum BattleType {
|
||||
WILD,
|
||||
@ -419,6 +427,7 @@ export class FixedBattleConfig {
|
||||
public getTrainer: GetTrainerFunc;
|
||||
public getEnemyParty: GetEnemyPartyFunc;
|
||||
public seedOffsetWaveIndex: number;
|
||||
public customModifierRewardSettings?: CustomModifierSettings;
|
||||
|
||||
setBattleType(battleType: BattleType): FixedBattleConfig {
|
||||
this.battleType = battleType;
|
||||
@ -444,6 +453,11 @@ export class FixedBattleConfig {
|
||||
this.seedOffsetWaveIndex = seedOffsetWaveIndex;
|
||||
return this;
|
||||
}
|
||||
|
||||
setCustomModifierRewards(customModifierRewardSettings: CustomModifierSettings) {
|
||||
this.customModifierRewardSettings = customModifierRewardSettings;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -503,11 +517,13 @@ export const classicFixedBattles: FixedBattleConfigs = {
|
||||
[8]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
||||
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
|
||||
[25]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
||||
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_2, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
|
||||
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_2, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
|
||||
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], allowLuckUpgrades: false }),
|
||||
[35]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
||||
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)),
|
||||
[55]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
||||
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_3, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
|
||||
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_3, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
|
||||
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], allowLuckUpgrades: false }),
|
||||
[62]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
|
||||
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)),
|
||||
[64]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
|
||||
@ -515,17 +531,21 @@ export const classicFixedBattles: FixedBattleConfigs = {
|
||||
[66]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
|
||||
.setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ] ], true)),
|
||||
[95]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
||||
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_4, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
|
||||
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_4, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
|
||||
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }),
|
||||
[112]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
|
||||
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_GRUNT, TrainerType.MAGMA_GRUNT, TrainerType.AQUA_GRUNT, TrainerType.GALACTIC_GRUNT, TrainerType.PLASMA_GRUNT, TrainerType.FLARE_GRUNT, TrainerType.AETHER_GRUNT, TrainerType.SKULL_GRUNT, TrainerType.MACRO_GRUNT, TrainerType.STAR_GRUNT ], true)),
|
||||
[114]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
|
||||
.setGetTrainerFunc(getRandomTrainerFunc([[ TrainerType.ARCHER, TrainerType.ARIANA, TrainerType.PROTON, TrainerType.PETREL ], [ TrainerType.TABITHA, TrainerType.COURTNEY ], [ TrainerType.MATT, TrainerType.SHELLY ], [ TrainerType.JUPITER, TrainerType.MARS, TrainerType.SATURN ], [ TrainerType.ZINZOLIN, TrainerType.ROOD ], [ TrainerType.XEROSIC, TrainerType.BRYONY ], TrainerType.FABA, TrainerType.PLUMERIA, TrainerType.OLEANA, [ TrainerType.GIACOMO, TrainerType.MELA, TrainerType.ATTICUS, TrainerType.ORTEGA, TrainerType.ERI ] ], true, 1)),
|
||||
[115]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
|
||||
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.MAXIE, TrainerType.ARCHIE, TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE, TrainerType.LUSAMINE, TrainerType.GUZMA, TrainerType.ROSE, TrainerType.PENNY ])),
|
||||
[ClassicFixedBossWaves.EVIL_BOSS_1]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
|
||||
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_1, TrainerType.MAXIE, TrainerType.ARCHIE, TrainerType.CYRUS, TrainerType.GHETSIS, TrainerType.LYSANDRE, TrainerType.LUSAMINE, TrainerType.GUZMA, TrainerType.ROSE, TrainerType.PENNY ]))
|
||||
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }),
|
||||
[145]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
||||
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_5, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT)),
|
||||
[165]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
|
||||
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_2, TrainerType.MAXIE_2, TrainerType.ARCHIE_2, TrainerType.CYRUS_2, TrainerType.GHETSIS_2, TrainerType.LYSANDRE_2, TrainerType.LUSAMINE_2, TrainerType.GUZMA_2, TrainerType.ROSE_2, TrainerType.PENNY_2 ])),
|
||||
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_5, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
|
||||
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }),
|
||||
[ClassicFixedBossWaves.EVIL_BOSS_2]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(35)
|
||||
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.ROCKET_BOSS_GIOVANNI_2, TrainerType.MAXIE_2, TrainerType.ARCHIE_2, TrainerType.CYRUS_2, TrainerType.GHETSIS_2, TrainerType.LYSANDRE_2, TrainerType.LUSAMINE_2, TrainerType.GUZMA_2, TrainerType.ROSE_2, TrainerType.PENNY_2 ]))
|
||||
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA], allowLuckUpgrades: false }),
|
||||
[182]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
||||
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.LORELEI, TrainerType.WILL, TrainerType.SIDNEY, TrainerType.AARON, TrainerType.SHAUNTAL, TrainerType.MALVA, [ TrainerType.HALA, TrainerType.MOLAYNE ], TrainerType.MARNIE_ELITE, TrainerType.RIKA, TrainerType.CRISPIN ])),
|
||||
[184]: new FixedBattleConfig().setBattleType(BattleType.TRAINER).setSeedOffsetWave(182)
|
||||
@ -538,4 +558,5 @@ export const classicFixedBattles: FixedBattleConfigs = {
|
||||
.setGetTrainerFunc(getRandomTrainerFunc([ TrainerType.BLUE, [ TrainerType.RED, TrainerType.LANCE_CHAMPION ], [ TrainerType.STEVEN, TrainerType.WALLACE ], TrainerType.CYNTHIA, [ TrainerType.ALDER, TrainerType.IRIS ], TrainerType.DIANTHA, TrainerType.HAU, TrainerType.LEON, [ TrainerType.GEETA, TrainerType.NEMONA ], TrainerType.KIERAN ])),
|
||||
[195]: new FixedBattleConfig().setBattleType(BattleType.TRAINER)
|
||||
.setGetTrainerFunc(scene => new Trainer(scene, TrainerType.RIVAL_6, scene.gameData.gender === PlayerGender.MALE ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT))
|
||||
.setCustomModifierRewards({ guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], allowLuckUpgrades: false })
|
||||
};
|
||||
|
@ -1 +1,5 @@
|
||||
export const PLAYER_PARTY_MAX_SIZE = 6;
|
||||
/** The maximum size of the player's party */
|
||||
export const PLAYER_PARTY_MAX_SIZE: number = 6;
|
||||
|
||||
/** Whether to use seasonal splash messages in general */
|
||||
export const USE_SEASONAL_SPLASH_MESSAGES: boolean = false;
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
transitionMysteryEncounterIntroVisuals,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import {
|
||||
getRandomPartyMemberFunc,
|
||||
trainerConfigs,
|
||||
TrainerPartyCompoundTemplate,
|
||||
TrainerPartyTemplate,
|
||||
@ -17,14 +18,12 @@ import {
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import * as Utils from "#app/utils";
|
||||
import { isNullOrUndefined, randSeedInt, randSeedShuffle } from "#app/utils";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { Species } from "#enums/species";
|
||||
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { getEncounterText, showEncounterDialogue } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||
import { Moves } from "#enums/moves";
|
||||
@ -584,16 +583,6 @@ function getTrainerConfigForWave(waveIndex: number) {
|
||||
return config;
|
||||
}
|
||||
|
||||
function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void) {
|
||||
return (scene: BattleScene, level: number, strength: PartyMemberStrength) => {
|
||||
let species = Utils.randSeedItem(speciesPool);
|
||||
if (!ignoreEvolution) {
|
||||
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength);
|
||||
}
|
||||
return scene.addEnemyPokemon(getPokemonSpecies(species), level, trainerSlot, undefined, undefined, postProcess);
|
||||
};
|
||||
}
|
||||
|
||||
function doBugTypeMoveTutor(scene: BattleScene): Promise<void> {
|
||||
return new Promise<void>(async resolve => {
|
||||
const moveOptions = scene.currentBattle.mysteryEncounter!.misc.moveTutorOptions;
|
||||
|
@ -133,7 +133,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
||||
}
|
||||
|
||||
const oricorioData = new PokemonData(enemyPokemon);
|
||||
const oricorio = scene.addEnemyPokemon(species, scene.currentBattle.enemyLevels![0], TrainerSlot.NONE, false, oricorioData);
|
||||
const oricorio = scene.addEnemyPokemon(species, level, TrainerSlot.NONE, false, oricorioData);
|
||||
|
||||
// Adds a real Pokemon sprite to the field (required for the animation)
|
||||
scene.getEnemyParty().forEach(enemyPokemon => {
|
||||
|
@ -0,0 +1,549 @@
|
||||
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { trainerConfigs } from "#app/data/trainer-config";
|
||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { randSeedShuffle } from "#app/utils";
|
||||
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||
import { Biome } from "#enums/biome";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import i18next from "i18next";
|
||||
import { Species } from "#enums/species";
|
||||
import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Type } from "#app/data/type";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { PlayerPokemon } from "#app/field/pokemon";
|
||||
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { IEggOptions } from "#app/data/egg";
|
||||
import { EggSourceType } from "#enums/egg-source-types";
|
||||
import { EggTier } from "#enums/egg-type";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { achvs } from "#app/system/achv";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
const namespace = "mysteryEncounter:expertPokemonBreeder";
|
||||
|
||||
const trainerNameKey = "trainerNames:expert_pokemon_breeder";
|
||||
|
||||
const FIRST_STAGE_EVOLUTION_WAVE = 30;
|
||||
const SECOND_STAGE_EVOLUTION_WAVE = 45;
|
||||
const FINAL_STAGE_EVOLUTION_WAVE = 60;
|
||||
|
||||
const FRIENDSHIP_ADDED = 20;
|
||||
|
||||
class BreederSpeciesEvolution {
|
||||
species: Species;
|
||||
evolution: number;
|
||||
|
||||
constructor(species: Species, evolution: number) {
|
||||
this.species = species;
|
||||
this.evolution = evolution;
|
||||
}
|
||||
}
|
||||
|
||||
const POOL_1_POKEMON: (Species | BreederSpeciesEvolution)[][] = [
|
||||
[Species.MUNCHLAX, new BreederSpeciesEvolution(Species.SNORLAX, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.HAPPINY, new BreederSpeciesEvolution(Species.CHANSEY, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.BLISSEY, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.MAGBY, new BreederSpeciesEvolution(Species.MAGMAR, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.MAGMORTAR, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.ELEKID, new BreederSpeciesEvolution(Species.ELECTABUZZ, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ELECTIVIRE, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.RIOLU, new BreederSpeciesEvolution(Species.LUCARIO, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.BUDEW, new BreederSpeciesEvolution(Species.ROSELIA, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ROSERADE, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.TOXEL, new BreederSpeciesEvolution(Species.TOXTRICITY, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.MIME_JR, new BreederSpeciesEvolution(Species.GALAR_MR_MIME, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.MR_RIME, FINAL_STAGE_EVOLUTION_WAVE)]
|
||||
];
|
||||
|
||||
const POOL_2_POKEMON: (Species | BreederSpeciesEvolution)[][] = [
|
||||
[Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.RAICHU, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.PICHU, new BreederSpeciesEvolution(Species.PIKACHU, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.ALOLA_RAICHU, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.JYNX],
|
||||
[Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONLEE, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONCHAN, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.TYROGUE, new BreederSpeciesEvolution(Species.HITMONTOP, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.IGGLYBUFF, new BreederSpeciesEvolution(Species.JIGGLYPUFF, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.WIGGLYTUFF, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.AZURILL, new BreederSpeciesEvolution(Species.MARILL, FIRST_STAGE_EVOLUTION_WAVE), new BreederSpeciesEvolution(Species.AZUMARILL, FINAL_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.WYNAUT, new BreederSpeciesEvolution(Species.WOBBUFFET, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.CHINGLING, new BreederSpeciesEvolution(Species.CHIMECHO, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.BONSLY, new BreederSpeciesEvolution(Species.SUDOWOODO, SECOND_STAGE_EVOLUTION_WAVE)],
|
||||
[Species.MANTYKE, new BreederSpeciesEvolution(Species.MANTINE, SECOND_STAGE_EVOLUTION_WAVE)]
|
||||
];
|
||||
|
||||
/**
|
||||
* The Expert Pokémon Breeder encounter.
|
||||
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3818 | GitHub Issue #3818}
|
||||
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||
*/
|
||||
export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
||||
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER)
|
||||
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
||||
.withScenePartySizeRequirement(4, 6, true) // Must have at least 4 legal pokemon in party
|
||||
.withIntroSpriteConfigs([]) // These are set in onInit()
|
||||
.withIntroDialogue([
|
||||
{
|
||||
text: `${namespace}.intro`,
|
||||
},
|
||||
{
|
||||
speaker: trainerNameKey,
|
||||
text: `${namespace}.intro_dialogue`,
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
const waveIndex = scene.currentBattle.waveIndex;
|
||||
// Calculates what trainers are available for battle in the encounter
|
||||
|
||||
// If player is in space biome, uses special "Space" version of the trainer
|
||||
encounter.enemyPartyConfigs = [
|
||||
getPartyConfig(scene)
|
||||
];
|
||||
|
||||
const cleffaSpecies = waveIndex < FIRST_STAGE_EVOLUTION_WAVE ? Species.CLEFFA : waveIndex < FINAL_STAGE_EVOLUTION_WAVE ? Species.CLEFAIRY : Species.CLEFABLE;
|
||||
encounter.spriteConfigs = [
|
||||
{
|
||||
spriteKey: cleffaSpecies.toString(),
|
||||
fileRoot: "pokemon",
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
x: 14,
|
||||
y: -2,
|
||||
yShadow: -2
|
||||
},
|
||||
{
|
||||
spriteKey: "expert_pokemon_breeder",
|
||||
fileRoot: "trainer",
|
||||
hasShadow: true,
|
||||
x: -14,
|
||||
y: 4,
|
||||
yShadow: 2
|
||||
},
|
||||
];
|
||||
|
||||
// Determine the 3 pokemon the player can battle with
|
||||
let partyCopy = scene.getParty().slice(0);
|
||||
partyCopy = partyCopy
|
||||
.filter(p => p.isAllowedInBattle())
|
||||
.sort((a, b) => a.friendship - b.friendship);
|
||||
|
||||
const pokemon1 = partyCopy[0];
|
||||
const pokemon2 = partyCopy[1];
|
||||
const pokemon3 = partyCopy[2];
|
||||
encounter.setDialogueToken("pokemon1Name", pokemon1.getNameToRender());
|
||||
encounter.setDialogueToken("pokemon2Name", pokemon2.getNameToRender());
|
||||
encounter.setDialogueToken("pokemon3Name", pokemon3.getNameToRender());
|
||||
|
||||
// Dialogue and egg calcs for Pokemon 1
|
||||
const [pokemon1CommonEggs, pokemon1RareEggs] = calculateEggRewardsForPokemon(pokemon1);
|
||||
let pokemon1Tooltip = getEncounterText(scene, `${namespace}.option.1.tooltip_base`)!;
|
||||
if (pokemon1RareEggs > 0) {
|
||||
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon1RareEggs, rarity: i18next.t("egg:greatTier") });
|
||||
pokemon1Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
|
||||
encounter.setDialogueToken("pokemon1RareEggs", eggsText);
|
||||
}
|
||||
if (pokemon1CommonEggs > 0) {
|
||||
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon1CommonEggs, rarity: i18next.t("egg:defaultTier") });
|
||||
pokemon1Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
|
||||
encounter.setDialogueToken("pokemon1CommonEggs", eggsText);
|
||||
}
|
||||
encounter.options[0].dialogue!.buttonTooltip = pokemon1Tooltip;
|
||||
|
||||
// Dialogue and egg calcs for Pokemon 2
|
||||
const [pokemon2CommonEggs, pokemon2RareEggs] = calculateEggRewardsForPokemon(pokemon2);
|
||||
let pokemon2Tooltip = getEncounterText(scene, `${namespace}.option.2.tooltip_base`)!;
|
||||
if (pokemon2RareEggs > 0) {
|
||||
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon2RareEggs, rarity: i18next.t("egg:greatTier") });
|
||||
pokemon2Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
|
||||
encounter.setDialogueToken("pokemon2RareEggs", eggsText);
|
||||
}
|
||||
if (pokemon2CommonEggs > 0) {
|
||||
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon2CommonEggs, rarity: i18next.t("egg:defaultTier") });
|
||||
pokemon2Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
|
||||
encounter.setDialogueToken("pokemon1CommonEggs", eggsText);
|
||||
}
|
||||
encounter.options[1].dialogue!.buttonTooltip = pokemon2Tooltip;
|
||||
|
||||
// Dialogue and egg calcs for Pokemon 3
|
||||
const [pokemon3CommonEggs, pokemon3RareEggs] = calculateEggRewardsForPokemon(pokemon3);
|
||||
let pokemon3Tooltip = getEncounterText(scene, `${namespace}.option.3.tooltip_base`)!;
|
||||
if (pokemon3RareEggs > 0) {
|
||||
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon3RareEggs, rarity: i18next.t("egg:greatTier") });
|
||||
pokemon3Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
|
||||
encounter.setDialogueToken("pokemon3RareEggs", eggsText);
|
||||
}
|
||||
if (pokemon3CommonEggs > 0) {
|
||||
const eggsText = i18next.t(`${namespace}.numEggs`, { count: pokemon3CommonEggs, rarity: i18next.t("egg:defaultTier") });
|
||||
pokemon3Tooltip += i18next.t(`${namespace}.eggs_tooltip`, { eggs: eggsText });
|
||||
encounter.setDialogueToken("pokemon3CommonEggs", eggsText);
|
||||
}
|
||||
encounter.options[2].dialogue!.buttonTooltip = pokemon3Tooltip;
|
||||
|
||||
encounter.misc = {
|
||||
pokemon1,
|
||||
pokemon1CommonEggs,
|
||||
pokemon1RareEggs,
|
||||
pokemon2,
|
||||
pokemon2CommonEggs,
|
||||
pokemon2RareEggs,
|
||||
pokemon3,
|
||||
pokemon3CommonEggs,
|
||||
pokemon3RareEggs
|
||||
};
|
||||
|
||||
return true;
|
||||
})
|
||||
.withTitle(`${namespace}.title`)
|
||||
.withDescription(`${namespace}.description`)
|
||||
.withQuery(`${namespace}.query`)
|
||||
.withOption(
|
||||
MysteryEncounterOptionBuilder
|
||||
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||
.withDialogue({
|
||||
buttonLabel: `${namespace}.option.1.label`,
|
||||
selected: [
|
||||
{
|
||||
speaker: trainerNameKey,
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
// Spawn battle with first pokemon
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
|
||||
const { pokemon1, pokemon1CommonEggs, pokemon1RareEggs } = encounter.misc;
|
||||
encounter.setDialogueToken("chosenPokemon", pokemon1.getNameToRender());
|
||||
const eggOptions = getEggOptions(scene, pokemon1CommonEggs, pokemon1RareEggs);
|
||||
setEncounterRewards(scene, { fillRemaining: true }, eggOptions);
|
||||
|
||||
// Remove all Pokemon from the party except the chosen Pokemon
|
||||
removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon1);
|
||||
|
||||
// Configure outro dialogue for egg rewards
|
||||
encounter.dialogue.outro = [
|
||||
{
|
||||
speaker: trainerNameKey,
|
||||
text: `${namespace}.outro`,
|
||||
},
|
||||
];
|
||||
if (encounter.dialogueTokens.hasOwnProperty("pokemon1CommonEggs")) {
|
||||
encounter.dialogue.outro.push({
|
||||
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon1CommonEggs"] }),
|
||||
});
|
||||
}
|
||||
if (encounter.dialogueTokens.hasOwnProperty("pokemon1RareEggs")) {
|
||||
encounter.dialogue.outro.push({
|
||||
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon1RareEggs"] }),
|
||||
});
|
||||
}
|
||||
|
||||
initBattleWithEnemyConfig(scene, config);
|
||||
})
|
||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||
// Give achievement if in Space biome
|
||||
checkAchievement(scene);
|
||||
// Give 20 friendship to the chosen pokemon
|
||||
scene.currentBattle.mysteryEncounter!.misc.pokemon1.addFriendship(FRIENDSHIP_ADDED);
|
||||
await restorePartyAndHeldItems(scene);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
.withOption(
|
||||
MysteryEncounterOptionBuilder
|
||||
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||
.withDialogue({
|
||||
buttonLabel: `${namespace}.option.2.label`,
|
||||
selected: [
|
||||
{
|
||||
speaker: trainerNameKey,
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
// Spawn battle with second pokemon
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
|
||||
const { pokemon2, pokemon2CommonEggs, pokemon2RareEggs } = encounter.misc;
|
||||
encounter.setDialogueToken("chosenPokemon", pokemon2.getNameToRender());
|
||||
const eggOptions = getEggOptions(scene, pokemon2CommonEggs, pokemon2RareEggs);
|
||||
setEncounterRewards(scene, { fillRemaining: true }, eggOptions);
|
||||
|
||||
// Remove all Pokemon from the party except the chosen Pokemon
|
||||
removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon2);
|
||||
|
||||
// Configure outro dialogue for egg rewards
|
||||
encounter.dialogue.outro = [
|
||||
{
|
||||
speaker: trainerNameKey,
|
||||
text: `${namespace}.outro`,
|
||||
},
|
||||
];
|
||||
if (encounter.dialogueTokens.hasOwnProperty("pokemon2CommonEggs")) {
|
||||
encounter.dialogue.outro.push({
|
||||
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon2CommonEggs"] }),
|
||||
});
|
||||
}
|
||||
if (encounter.dialogueTokens.hasOwnProperty("pokemon2RareEggs")) {
|
||||
encounter.dialogue.outro.push({
|
||||
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon2RareEggs"] }),
|
||||
});
|
||||
}
|
||||
|
||||
initBattleWithEnemyConfig(scene, config);
|
||||
})
|
||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||
// Give achievement if in Space biome
|
||||
checkAchievement(scene);
|
||||
// Give 20 friendship to the chosen pokemon
|
||||
scene.currentBattle.mysteryEncounter!.misc.pokemon2.addFriendship(FRIENDSHIP_ADDED);
|
||||
await restorePartyAndHeldItems(scene);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
.withOption(
|
||||
MysteryEncounterOptionBuilder
|
||||
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||
.withDialogue({
|
||||
buttonLabel: `${namespace}.option.3.label`,
|
||||
selected: [
|
||||
{
|
||||
speaker: trainerNameKey,
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
// Spawn battle with third pokemon
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
|
||||
const { pokemon3, pokemon3CommonEggs, pokemon3RareEggs } = encounter.misc;
|
||||
encounter.setDialogueToken("chosenPokemon", pokemon3.getNameToRender());
|
||||
const eggOptions = getEggOptions(scene, pokemon3CommonEggs, pokemon3RareEggs);
|
||||
setEncounterRewards(scene, { fillRemaining: true }, eggOptions);
|
||||
|
||||
// Remove all Pokemon from the party except the chosen Pokemon
|
||||
removePokemonFromPartyAndStoreHeldItems(scene, encounter, pokemon3);
|
||||
|
||||
// Configure outro dialogue for egg rewards
|
||||
encounter.dialogue.outro = [
|
||||
{
|
||||
speaker: trainerNameKey,
|
||||
text: `${namespace}.outro`,
|
||||
},
|
||||
];
|
||||
if (encounter.dialogueTokens.hasOwnProperty("pokemon3CommonEggs")) {
|
||||
encounter.dialogue.outro.push({
|
||||
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon3CommonEggs"] }),
|
||||
});
|
||||
}
|
||||
if (encounter.dialogueTokens.hasOwnProperty("pokemon3RareEggs")) {
|
||||
encounter.dialogue.outro.push({
|
||||
text: i18next.t(`${namespace}.gained_eggs`, { numEggs: encounter.dialogueTokens["pokemon3RareEggs"] }),
|
||||
});
|
||||
}
|
||||
|
||||
initBattleWithEnemyConfig(scene, config);
|
||||
})
|
||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||
// Give achievement if in Space biome
|
||||
checkAchievement(scene);
|
||||
// Give 20 friendship to the chosen pokemon
|
||||
scene.currentBattle.mysteryEncounter!.misc.pokemon3.addFriendship(FRIENDSHIP_ADDED);
|
||||
await restorePartyAndHeldItems(scene);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
.withOutroDialogue([
|
||||
{
|
||||
text: `${namespace}.outro`,
|
||||
},
|
||||
])
|
||||
.build();
|
||||
|
||||
function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
|
||||
// Bug type superfan trainer config
|
||||
const waveIndex = scene.currentBattle.waveIndex;
|
||||
const breederConfig = trainerConfigs[TrainerType.EXPERT_POKEMON_BREEDER].clone();
|
||||
breederConfig.name = i18next.t(trainerNameKey);
|
||||
|
||||
// First mon is *always* this special cleffa
|
||||
const cleffaSpecies = waveIndex < FIRST_STAGE_EVOLUTION_WAVE ? Species.CLEFFA : waveIndex < FINAL_STAGE_EVOLUTION_WAVE ? Species.CLEFAIRY : Species.CLEFABLE;
|
||||
const baseConfig: EnemyPartyConfig = {
|
||||
trainerType: TrainerType.EXPERT_POKEMON_BREEDER,
|
||||
pokemonConfigs: [
|
||||
{
|
||||
nickname: i18next.t(`${namespace}.cleffa_1_nickname`),
|
||||
species: getPokemonSpecies(cleffaSpecies),
|
||||
isBoss: false,
|
||||
abilityIndex: 1, // Magic Guard
|
||||
shiny: false,
|
||||
nature: Nature.ADAMANT,
|
||||
moveSet: [Moves.METEOR_MASH, Moves.FIRE_PUNCH, Moves.ICE_PUNCH, Moves.THUNDER_PUNCH],
|
||||
ivs: [31, 31, 31, 31, 31, 31],
|
||||
modifierConfigs: [
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.TERA_SHARD, [Type.STEEL]) as PokemonHeldItemModifierType,
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.ATK]) as PokemonHeldItemModifierType,
|
||||
stackCount: 1 + Math.floor(waveIndex / 20), // +1 Protein every 20 waves
|
||||
},
|
||||
{
|
||||
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.SPD]) as PokemonHeldItemModifierType,
|
||||
stackCount: 1 + Math.floor(waveIndex / 40), // +1 Carbos every 40 waves
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
if (scene.arena.biomeType === Biome.SPACE) {
|
||||
// All 3 members always Cleffa line, but different configs
|
||||
baseConfig.pokemonConfigs!.push({
|
||||
nickname: i18next.t(`${namespace}.cleffa_2_nickname`),
|
||||
species: getPokemonSpecies(cleffaSpecies),
|
||||
isBoss: false,
|
||||
abilityIndex: 1, // Magic Guard
|
||||
shiny: true,
|
||||
variant: 1,
|
||||
nature: Nature.MODEST,
|
||||
moveSet: [Moves.MOONBLAST, Moves.MYSTICAL_FIRE, Moves.ICE_BEAM, Moves.THUNDERBOLT],
|
||||
ivs: [31, 31, 31, 31, 31, 31]
|
||||
},
|
||||
{
|
||||
nickname: i18next.t(`${namespace}.cleffa_3_nickname`, { speciesName: getPokemonSpecies(cleffaSpecies).getName() }),
|
||||
species: getPokemonSpecies(cleffaSpecies),
|
||||
isBoss: false,
|
||||
abilityIndex: 2, // Friend Guard / Unaware
|
||||
shiny: true,
|
||||
variant: 2,
|
||||
nature: Nature.BOLD,
|
||||
moveSet: [Moves.TRI_ATTACK, Moves.STORED_POWER, Moves.TAKE_HEART, Moves.MOONLIGHT],
|
||||
ivs: [31, 31, 31, 31, 31, 31]
|
||||
});
|
||||
} else {
|
||||
// Second member from pool 1
|
||||
const pool1Species = getSpeciesFromPool(POOL_1_POKEMON, waveIndex);
|
||||
// Third member from pool 2
|
||||
const pool2Species = getSpeciesFromPool(POOL_2_POKEMON, waveIndex);
|
||||
|
||||
baseConfig.pokemonConfigs!.push({
|
||||
species: getPokemonSpecies(pool1Species),
|
||||
isBoss: false,
|
||||
ivs: [31, 31, 31, 31, 31, 31]
|
||||
},
|
||||
{
|
||||
species: getPokemonSpecies(pool2Species),
|
||||
isBoss: false,
|
||||
ivs: [31, 31, 31, 31, 31, 31]
|
||||
});
|
||||
}
|
||||
|
||||
return baseConfig;
|
||||
}
|
||||
|
||||
function getSpeciesFromPool(speciesPool: (Species | BreederSpeciesEvolution)[][], waveIndex: number): Species {
|
||||
const poolCopy = speciesPool.slice(0);
|
||||
randSeedShuffle(poolCopy);
|
||||
const speciesEvolutions = poolCopy.pop()!.slice(0);
|
||||
let speciesObject = speciesEvolutions.pop()!;
|
||||
while (speciesObject instanceof BreederSpeciesEvolution && speciesObject.evolution > waveIndex) {
|
||||
speciesObject = speciesEvolutions.pop()!;
|
||||
}
|
||||
return speciesObject instanceof BreederSpeciesEvolution ? speciesObject.species : speciesObject;
|
||||
}
|
||||
|
||||
function calculateEggRewardsForPokemon(pokemon: PlayerPokemon): [number, number] {
|
||||
const bst = pokemon.calculateBaseStats().reduce((a, b) => a + b, 0);
|
||||
// 1 point for every 20 points below 680 BST the pokemon is, (max 18, min 1)
|
||||
const pointsFromBst = Math.min(Math.max(Math.floor((680 - bst) / 20), 1), 18);
|
||||
|
||||
const rootSpecies = pokemon.species.getRootSpeciesId(true);
|
||||
let pointsFromStarterTier = 0;
|
||||
// 2 points for every 1 below 7 that the pokemon's starter tier is (max 12, min 0)
|
||||
if (speciesStarters.hasOwnProperty(rootSpecies)) {
|
||||
const starterTier = speciesStarters[rootSpecies];
|
||||
pointsFromStarterTier = Math.min(Math.max(Math.floor(7 - starterTier) * 2, 0), 12);
|
||||
}
|
||||
|
||||
// Maximum of 30 points
|
||||
const totalPoints = Math.min(pointsFromStarterTier + pointsFromBst, 30);
|
||||
|
||||
// 1 Rare egg for every 6 points
|
||||
const numRares = Math.floor(totalPoints / 6);
|
||||
// 1 Common egg for every point leftover
|
||||
const numCommons = totalPoints % 6;
|
||||
|
||||
return [numCommons, numRares];
|
||||
}
|
||||
|
||||
function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number) {
|
||||
const eggDescription = i18next.t(`${namespace}.title`) + ":\n" + i18next.t(trainerNameKey);
|
||||
const eggOptions: IEggOptions[] = [];
|
||||
|
||||
if (commonEggs > 0) {
|
||||
for (let i = 0; i < commonEggs; i++) {
|
||||
eggOptions.push({
|
||||
scene,
|
||||
pulled: false,
|
||||
sourceType: EggSourceType.EVENT,
|
||||
eggDescriptor: eggDescription,
|
||||
tier: EggTier.COMMON
|
||||
});
|
||||
}
|
||||
}
|
||||
if (rareEggs > 0) {
|
||||
for (let i = 0; i < rareEggs; i++) {
|
||||
eggOptions.push({
|
||||
scene,
|
||||
pulled: false,
|
||||
sourceType: EggSourceType.EVENT,
|
||||
eggDescriptor: eggDescription,
|
||||
tier: EggTier.GREAT
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return eggOptions;
|
||||
}
|
||||
|
||||
function removePokemonFromPartyAndStoreHeldItems(scene: BattleScene, encounter: MysteryEncounter, chosenPokemon: PlayerPokemon) {
|
||||
const party = scene.getParty();
|
||||
const chosenIndex = party.indexOf(chosenPokemon);
|
||||
party[chosenIndex] = party[0];
|
||||
party[0] = chosenPokemon;
|
||||
encounter.misc.originalParty = scene.getParty().slice(1);
|
||||
encounter.misc.originalPartyHeldItems = encounter.misc.originalParty
|
||||
.map(p => p.getHeldItems());
|
||||
scene["party"] = [
|
||||
chosenPokemon
|
||||
];
|
||||
}
|
||||
|
||||
function checkAchievement(scene: BattleScene) {
|
||||
if (scene.arena.biomeType === Biome.SPACE) {
|
||||
scene.validateAchv(achvs.BREEDERS_IN_SPACE);
|
||||
}
|
||||
}
|
||||
|
||||
async function restorePartyAndHeldItems(scene: BattleScene) {
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
// Restore original party
|
||||
scene.getParty().push(...encounter.misc.originalParty);
|
||||
|
||||
// Restore held items
|
||||
const originalHeldItems = encounter.misc.originalPartyHeldItems;
|
||||
originalHeldItems.forEach(pokemonHeldItemsList => {
|
||||
pokemonHeldItemsList.forEach(heldItem => {
|
||||
scene.addModifier(heldItem, true, false, false, true);
|
||||
});
|
||||
});
|
||||
await scene.updateModifiers(true);
|
||||
}
|
@ -31,6 +31,7 @@ import { BugTypeSuperfanEncounter } from "#app/data/mystery-encounters/encounter
|
||||
import { FunAndGamesEncounter } from "#app/data/mystery-encounters/encounters/fun-and-games-encounter";
|
||||
import { UncommonBreedEncounter } from "#app/data/mystery-encounters/encounters/uncommon-breed-encounter";
|
||||
import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter";
|
||||
import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter";
|
||||
|
||||
/**
|
||||
* Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT
|
||||
@ -184,7 +185,8 @@ const humanTransitableBiomeEncounters: MysteryEncounterType[] = [
|
||||
MysteryEncounterType.SHADY_VITAMIN_DEALER,
|
||||
MysteryEncounterType.THE_POKEMON_SALESMAN,
|
||||
MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE,
|
||||
MysteryEncounterType.THE_WINSTRATE_CHALLENGE
|
||||
MysteryEncounterType.THE_WINSTRATE_CHALLENGE,
|
||||
MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER
|
||||
];
|
||||
|
||||
const civilizationBiomeEncounters: MysteryEncounterType[] = [
|
||||
@ -238,7 +240,6 @@ export const mysteryEncountersByBiome = new Map<Biome, MysteryEncounterType[]>([
|
||||
MysteryEncounterType.SAFARI_ZONE,
|
||||
MysteryEncounterType.ABSOLUTE_AVARICE
|
||||
]],
|
||||
|
||||
[Biome.SEA, [
|
||||
MysteryEncounterType.LOST_AT_SEA
|
||||
]],
|
||||
@ -275,7 +276,9 @@ export const mysteryEncountersByBiome = new Map<Biome, MysteryEncounterType[]>([
|
||||
[Biome.ABYSS, [
|
||||
MysteryEncounterType.DANCING_LESSONS
|
||||
]],
|
||||
[Biome.SPACE, []],
|
||||
[Biome.SPACE, [
|
||||
MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER
|
||||
]],
|
||||
[Biome.CONSTRUCTION_SITE, []],
|
||||
[Biome.JUNGLE, [
|
||||
MysteryEncounterType.SAFARI_ZONE
|
||||
@ -319,6 +322,7 @@ export function initMysteryEncounters() {
|
||||
allMysteryEncounters[MysteryEncounterType.FUN_AND_GAMES] = FunAndGamesEncounter;
|
||||
allMysteryEncounters[MysteryEncounterType.UNCOMMON_BREED] = UncommonBreedEncounter;
|
||||
allMysteryEncounters[MysteryEncounterType.GLOBAL_TRADE_SYSTEM] = GlobalTradeSystemEncounter;
|
||||
allMysteryEncounters[MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER] = TheExpertPokemonBreederEncounter;
|
||||
|
||||
// Add extreme encounters to biome map
|
||||
extremeBiomeEncounters.forEach(encounter => {
|
||||
|
@ -36,6 +36,7 @@ import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||
import { GameOverPhase } from "#app/phases/game-over-phase";
|
||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||
import { PartyExpPhase } from "#app/phases/party-exp-phase";
|
||||
import { Variant } from "#app/data/variant";
|
||||
|
||||
/**
|
||||
* Animates exclamation sprite over trainer's head at start of encounter
|
||||
@ -67,6 +68,7 @@ export function doTrainerExclamation(scene: BattleScene) {
|
||||
export interface EnemyPokemonConfig {
|
||||
species: PokemonSpecies;
|
||||
isBoss: boolean;
|
||||
nickname?: string;
|
||||
bossSegments?: number;
|
||||
bossSegmentModifier?: number; // Additive to the determined segment number
|
||||
mysteryEncounterPokemonData?: MysteryEncounterPokemonData;
|
||||
@ -79,6 +81,8 @@ export interface EnemyPokemonConfig {
|
||||
nature?: Nature;
|
||||
ivs?: [number, number, number, number, number, number];
|
||||
shiny?: boolean;
|
||||
/** Is only checked if Pokemon is shiny */
|
||||
variant?: Variant;
|
||||
/** Can set just the status, or pass a timer on the status turns */
|
||||
status?: StatusEffect | [StatusEffect, number];
|
||||
mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void;
|
||||
@ -220,6 +224,11 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
||||
if (partyConfig?.pokemonConfigs && e < partyConfig.pokemonConfigs.length) {
|
||||
const config = partyConfig.pokemonConfigs[e];
|
||||
|
||||
// Set form
|
||||
if (!isNullOrUndefined(config.nickname)) {
|
||||
enemyPokemon.nickname = btoa(unescape(encodeURIComponent(config.nickname!)));
|
||||
}
|
||||
|
||||
// Generate new id, reset status and HP in case using data source
|
||||
if (config.dataSource) {
|
||||
enemyPokemon.id = Utils.randSeedInt(4294967296);
|
||||
@ -235,6 +244,11 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
||||
enemyPokemon.shiny = config.shiny!;
|
||||
}
|
||||
|
||||
// Set Variant
|
||||
if (enemyPokemon.shiny && !isNullOrUndefined(config.variant)) {
|
||||
enemyPokemon.variant = config.variant!;
|
||||
}
|
||||
|
||||
// Set custom mystery encounter data fields (such as sprite scale, custom abilities, types, etc.)
|
||||
if (!isNullOrUndefined(config.mysteryEncounterPokemonData)) {
|
||||
enemyPokemon.mysteryEncounterPokemonData = config.mysteryEncounterPokemonData!;
|
||||
@ -315,6 +329,9 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
||||
// Requires re-priming summon data to update everything properly
|
||||
enemyPokemon.primeSummonData(enemyPokemon.summonData);
|
||||
|
||||
if (enemyPokemon.isShiny() && !enemyPokemon["shinySparkle"]) {
|
||||
enemyPokemon.initShinySparkle();
|
||||
}
|
||||
enemyPokemon.initBattleInfo();
|
||||
enemyPokemon.getBattleInfo().initInfo(enemyPokemon);
|
||||
enemyPokemon.generateName();
|
||||
|
@ -1,46 +1,136 @@
|
||||
import i18next from "i18next";
|
||||
import { USE_SEASONAL_SPLASH_MESSAGES } from "#app/constants";
|
||||
|
||||
export function getBattleCountSplashMessage(): string {
|
||||
return `{COUNT} ${i18next.t("splashMessages:battlesWon")}`;
|
||||
//#region Interfaces/Types
|
||||
|
||||
type Month = "01" | "02" | "03" | "04" | "05" | "06" | "07" | "08" | "09" | "10" | "11" | "12";
|
||||
type Day =
|
||||
| Month
|
||||
| "13"
|
||||
| "14"
|
||||
| "15"
|
||||
| "16"
|
||||
| "17"
|
||||
| "18"
|
||||
| "19"
|
||||
| "20"
|
||||
| "21"
|
||||
| "22"
|
||||
| "23"
|
||||
| "24"
|
||||
| "25"
|
||||
| "26"
|
||||
| "27"
|
||||
| "28"
|
||||
| "29"
|
||||
| "30"
|
||||
| "31";
|
||||
|
||||
/**
|
||||
* Represents a season with its {@linkcode name},
|
||||
* {@linkcode start} day+month, {@linkcode end} day+month
|
||||
* and {@linkcode messages}.
|
||||
*/
|
||||
interface Season {
|
||||
/** The name of the season (internal use only) */
|
||||
name: string;
|
||||
/** The start day and month of the season. Format `MM-DD` */
|
||||
start: `${Month}-${Day}`;
|
||||
/** The end day and month of the season. Format `MM-DD` */
|
||||
end: `${Month}-${Day}`;
|
||||
/** Collection of the messages to display (without the `i18next.t()` call!) */
|
||||
messages: string[];
|
||||
}
|
||||
|
||||
//#region Constants
|
||||
|
||||
/** The weight multiplier for the battles-won splash message */
|
||||
const BATTLES_WON_WEIGHT_MULTIPLIER = 10;
|
||||
/** The weight multiplier for the seasonal splash messages */
|
||||
const SEASONAL_WEIGHT_MULTIPLIER = 10;
|
||||
|
||||
//#region Common Messages
|
||||
|
||||
const commonSplashMessages = [
|
||||
...Array(BATTLES_WON_WEIGHT_MULTIPLIER).fill("battlesWon"),
|
||||
"joinTheDiscord",
|
||||
"infiniteLevels",
|
||||
"everythingStacks",
|
||||
"optionalSaveScumming",
|
||||
"biomes",
|
||||
"openSource",
|
||||
"playWithSpeed",
|
||||
"liveBugTesting",
|
||||
"heavyInfluence",
|
||||
"pokemonRiskAndPokemonRain",
|
||||
"nowWithMoreSalt",
|
||||
"infiniteFusionAtHome",
|
||||
"brokenEggMoves",
|
||||
"magnificent",
|
||||
"mubstitute",
|
||||
"thatsCrazy",
|
||||
"oranceJuice",
|
||||
"questionableBalancing",
|
||||
"coolShaders",
|
||||
"aiFree",
|
||||
"suddenDifficultySpikes",
|
||||
"basedOnAnUnfinishedFlashGame",
|
||||
"moreAddictiveThanIntended",
|
||||
"mostlyConsistentSeeds",
|
||||
"achievementPointsDontDoAnything",
|
||||
"youDoNotStartAtLevel",
|
||||
"dontTalkAboutTheManaphyEggIncident",
|
||||
"alsoTryPokengine",
|
||||
"alsoTryEmeraldRogue",
|
||||
"alsoTryRadicalRed",
|
||||
"eeveeExpo",
|
||||
"ynoproject",
|
||||
"breedersInSpace",
|
||||
];
|
||||
|
||||
//#region Seasonal Messages
|
||||
|
||||
const seasonalSplashMessages: Season[] = [
|
||||
{
|
||||
name: "Halloween",
|
||||
start: "09-15",
|
||||
end: "10-31",
|
||||
messages: ["halloween.pumpkaboosAbout", "halloween.mayContainSpiders", "halloween.spookyScaryDuskulls"],
|
||||
},
|
||||
{
|
||||
name: "XMAS",
|
||||
start: "12-01",
|
||||
end: "12-26",
|
||||
messages: ["xmas.happyHolidays", "xmas.delibirdSeason"],
|
||||
},
|
||||
{
|
||||
name: "New Year's",
|
||||
start: "01-01",
|
||||
end: "01-31",
|
||||
messages: ["newYears.happyNewYear"],
|
||||
},
|
||||
];
|
||||
|
||||
//#endregion
|
||||
|
||||
export function getSplashMessages(): string[] {
|
||||
const splashMessages = Array(10).fill(getBattleCountSplashMessage());
|
||||
splashMessages.push(
|
||||
i18next.t("splashMessages:joinTheDiscord"),
|
||||
i18next.t("splashMessages:infiniteLevels"),
|
||||
i18next.t("splashMessages:everythingStacks"),
|
||||
i18next.t("splashMessages:optionalSaveScumming"),
|
||||
i18next.t("splashMessages:biomes"),
|
||||
i18next.t("splashMessages:openSource"),
|
||||
i18next.t("splashMessages:playWithSpeed"),
|
||||
i18next.t("splashMessages:liveBugTesting"),
|
||||
i18next.t("splashMessages:heavyInfluence"),
|
||||
i18next.t("splashMessages:pokemonRiskAndPokemonRain"),
|
||||
i18next.t("splashMessages:nowWithMoreSalt"),
|
||||
i18next.t("splashMessages:infiniteFusionAtHome"),
|
||||
i18next.t("splashMessages:brokenEggMoves"),
|
||||
i18next.t("splashMessages:magnificent"),
|
||||
i18next.t("splashMessages:mubstitute"),
|
||||
i18next.t("splashMessages:thatsCrazy"),
|
||||
i18next.t("splashMessages:oranceJuice"),
|
||||
i18next.t("splashMessages:questionableBalancing"),
|
||||
i18next.t("splashMessages:coolShaders"),
|
||||
i18next.t("splashMessages:aiFree"),
|
||||
i18next.t("splashMessages:suddenDifficultySpikes"),
|
||||
i18next.t("splashMessages:basedOnAnUnfinishedFlashGame"),
|
||||
i18next.t("splashMessages:moreAddictiveThanIntended"),
|
||||
i18next.t("splashMessages:mostlyConsistentSeeds"),
|
||||
i18next.t("splashMessages:achievementPointsDontDoAnything"),
|
||||
i18next.t("splashMessages:youDoNotStartAtLevel"),
|
||||
i18next.t("splashMessages:dontTalkAboutTheManaphyEggIncident"),
|
||||
i18next.t("splashMessages:alsoTryPokengine"),
|
||||
i18next.t("splashMessages:alsoTryEmeraldRogue"),
|
||||
i18next.t("splashMessages:alsoTryRadicalRed"),
|
||||
i18next.t("splashMessages:eeveeExpo"),
|
||||
i18next.t("splashMessages:ynoproject"),
|
||||
i18next.t("splashMessages:breedersInSpace"),
|
||||
);
|
||||
const splashMessages: string[] = [...commonSplashMessages];
|
||||
console.log("use seasonal splash messages", USE_SEASONAL_SPLASH_MESSAGES);
|
||||
if (USE_SEASONAL_SPLASH_MESSAGES) {
|
||||
// add seasonal splash messages if the season is active
|
||||
for (const { name, start, end, messages } of seasonalSplashMessages) {
|
||||
const now = new Date();
|
||||
const startDate = new Date(`${start}-${now.getFullYear()}`);
|
||||
const endDate = new Date(`${end}-${now.getFullYear()}`);
|
||||
|
||||
return splashMessages;
|
||||
if (now >= startDate && now <= endDate) {
|
||||
console.log(`Adding ${messages.length} ${name} splash messages (weight: x${SEASONAL_WEIGHT_MULTIPLIER})`);
|
||||
messages.forEach((message) => {
|
||||
const weightedMessage = Array(SEASONAL_WEIGHT_MULTIPLIER).fill(message);
|
||||
splashMessages.push(...weightedMessage);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return splashMessages.map((message) => `splashMessages:${message}`);
|
||||
}
|
||||
|
@ -1143,8 +1143,16 @@ function getGymLeaderPartyTemplate(scene: BattleScene) {
|
||||
return getWavePartyTemplate(scene, trainerPartyTemplates.GYM_LEADER_1, trainerPartyTemplates.GYM_LEADER_2, trainerPartyTemplates.GYM_LEADER_3, trainerPartyTemplates.GYM_LEADER_4, trainerPartyTemplates.GYM_LEADER_5);
|
||||
}
|
||||
|
||||
function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void): PartyMemberFunc {
|
||||
return (scene: BattleScene, level: integer, strength: PartyMemberStrength) => {
|
||||
/**
|
||||
* Randomly selects one of the `Species` from `speciesPool`, determines its evolution, level, and strength.
|
||||
* Then adds Pokemon to scene.
|
||||
* @param speciesPool
|
||||
* @param trainerSlot
|
||||
* @param ignoreEvolution
|
||||
* @param postProcess
|
||||
*/
|
||||
export function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void) {
|
||||
return (scene: BattleScene, level: number, strength: PartyMemberStrength) => {
|
||||
let species = Utils.randSeedItem(speciesPool);
|
||||
if (!ignoreEvolution) {
|
||||
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength, scene.currentBattle.waveIndex);
|
||||
@ -2430,6 +2438,8 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
.setMoneyMultiplier(2)
|
||||
.setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(2, PartyMemberStrength.STRONG))),
|
||||
[TrainerType.BUG_TYPE_SUPERFAN]: new TrainerConfig(++t).setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.ACE_TRAINER)
|
||||
.setPartyTemplates(new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE))
|
||||
.setPartyTemplates(new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE)),
|
||||
[TrainerType.EXPERT_POKEMON_BREEDER]: new TrainerConfig(++t).setMoneyMultiplier(3).setEncounterBgm(TrainerType.ACE_TRAINER)
|
||||
.setPartyTemplates(new TrainerPartyTemplate(3, PartyMemberStrength.STRONG))
|
||||
};
|
||||
|
||||
|
@ -28,5 +28,6 @@ export enum MysteryEncounterType {
|
||||
BUG_TYPE_SUPERFAN,
|
||||
FUN_AND_GAMES,
|
||||
UNCOMMON_BREED,
|
||||
GLOBAL_TRADE_SYSTEM
|
||||
GLOBAL_TRADE_SYSTEM,
|
||||
THE_EXPERT_POKEMON_BREEDER
|
||||
}
|
||||
|
@ -115,6 +115,7 @@ export enum TrainerType {
|
||||
VICKY,
|
||||
VITO,
|
||||
BUG_TYPE_SUPERFAN,
|
||||
EXPERT_POKEMON_BREEDER,
|
||||
|
||||
BROCK = 200,
|
||||
MISTY,
|
||||
|
@ -1,19 +1,19 @@
|
||||
import BattleScene from "../battle-scene";
|
||||
import { BiomePoolTier, PokemonPools, BiomeTierTrainerPools, biomePokemonPools, biomeTrainerPools } from "../data/biomes";
|
||||
import { biomePokemonPools, BiomePoolTier, BiomeTierTrainerPools, biomeTrainerPools, PokemonPools } from "../data/biomes";
|
||||
import { Constructor } from "#app/utils";
|
||||
import * as Utils from "../utils";
|
||||
import PokemonSpecies, { getPokemonSpecies } from "../data/pokemon-species";
|
||||
import { Weather, WeatherType, getTerrainClearMessage, getTerrainStartMessage, getWeatherClearMessage, getWeatherStartMessage } from "../data/weather";
|
||||
import { getTerrainClearMessage, getTerrainStartMessage, getWeatherClearMessage, getWeatherStartMessage, Weather, WeatherType } from "../data/weather";
|
||||
import { CommonAnim } from "../data/battle-anims";
|
||||
import { Type } from "../data/type";
|
||||
import Move from "../data/move";
|
||||
import { ArenaTag, ArenaTagSide, ArenaTrapTag, getArenaTag } from "../data/arena-tag";
|
||||
import { BattlerIndex } from "../battle";
|
||||
import { Terrain, TerrainType } from "../data/terrain";
|
||||
import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability";
|
||||
import { applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs, PostTerrainChangeAbAttr, PostWeatherChangeAbAttr } from "../data/ability";
|
||||
import Pokemon from "./pokemon";
|
||||
import Overrides from "#app/overrides";
|
||||
import { WeatherChangedEvent, TerrainChangedEvent, TagAddedEvent, TagRemovedEvent } from "../events/arena";
|
||||
import { TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "../events/arena";
|
||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||
import { Biome } from "#enums/biome";
|
||||
import { Moves } from "#enums/moves";
|
||||
|
@ -3812,6 +3812,25 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const rootForm = getPokemonSpecies(this.species.getRootSpeciesId());
|
||||
return rootForm.getAbility(abilityIndex) === rootForm.getAbility(currentAbilityIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to check if the player already owns the starter data of the Pokemon's
|
||||
* current ability
|
||||
* @param ownedAbilityAttrs the owned abilityAttr of this Pokemon's root form
|
||||
* @returns true if the player already has it, false otherwise
|
||||
*/
|
||||
checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs: number): boolean {
|
||||
if ((ownedAbilityAttrs & 1) > 0 && this.hasSameAbilityInRootForm(0)) {
|
||||
return true;
|
||||
}
|
||||
if ((ownedAbilityAttrs & 2) > 0 && this.hasSameAbilityInRootForm(1)) {
|
||||
return true;
|
||||
}
|
||||
if ((ownedAbilityAttrs & 4) > 0 && this.hasSameAbilityInRootForm(2)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export default interface Pokemon {
|
||||
|
@ -268,7 +268,6 @@ export class GameMode implements GameModeConfig {
|
||||
isFixedBattle(waveIndex: integer): boolean {
|
||||
const dummyConfig = new FixedBattleConfig();
|
||||
return this.battleConfig.hasOwnProperty(waveIndex) || applyChallenges(this, ChallengeType.FIXED_BATTLES, waveIndex, dummyConfig);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"battlesWon": "Kämpfe gewonnen!",
|
||||
"battlesWon": "{{count, number}} Kämpfe gewonnen!",
|
||||
"joinTheDiscord": "Tritt dem Discord bei!",
|
||||
"infiniteLevels": "Unendliche Level!",
|
||||
"everythingStacks": "Alles stapelt sich!",
|
||||
|
@ -283,5 +283,9 @@
|
||||
"INVERSE_BATTLE": {
|
||||
"name": "Mirror rorriM",
|
||||
"description": "Complete the Inverse Battle challenge.\n.egnellahc elttaB esrevnI eht etelpmoC"
|
||||
},
|
||||
"BREEDERS_IN_SPACE": {
|
||||
"name": "Breeders in Space!",
|
||||
"description": "Beat the Expert Pokémon Breeder in the Space Biome."
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ import bugTypeSuperfan from "#app/locales/en/mystery-encounters/bug-type-superfa
|
||||
import funAndGames from "#app/locales/en/mystery-encounters/fun-and-games-dialogue.json";
|
||||
import uncommonBreed from "#app/locales/en/mystery-encounters/uncommon-breed-dialogue.json";
|
||||
import globalTradeSystem from "#app/locales/en/mystery-encounters/global-trade-system-dialogue.json";
|
||||
import expertPokemonBreeder from "#app/locales/en/mystery-encounters/the-expert-pokemon-breeder-dialogue.json";
|
||||
|
||||
/**
|
||||
* Dialogue/Text token injection patterns that can be used:
|
||||
@ -183,7 +184,8 @@ export const enConfig = {
|
||||
bugTypeSuperfan,
|
||||
funAndGames,
|
||||
uncommonBreed,
|
||||
globalTradeSystem
|
||||
globalTradeSystem,
|
||||
expertPokemonBreeder
|
||||
},
|
||||
mysteryEncounterMessages
|
||||
};
|
||||
|
@ -21,7 +21,7 @@
|
||||
"label": "Sales Assistant",
|
||||
"tooltip": "(-) Your {{option3PrimaryName}} uses {{option3PrimaryMove}}\n(+) Earn @[MONEY]{Money}",
|
||||
"disabled_tooltip": "Your Pokémon need to know certain moves for this job",
|
||||
"selected": "Your {{option3PrimaryName}} spends the day using {{option3PrimaryMove}} to attract customers to the business!"
|
||||
"selected": "Your {{option3PrimaryName}} spends the day using {{option3PrimaryMove}} to draw customers to the business!"
|
||||
}
|
||||
},
|
||||
"job_complete_good": "Thanks for the assistance!\nYour {{selectedPokemon}} was incredibly helpful!$Here's your check for the day.",
|
||||
|
@ -0,0 +1,30 @@
|
||||
{
|
||||
"intro": "It's a trainer carrying tons of Pokémon Eggs!",
|
||||
"intro_dialogue": "Hey there, trainer!$It looks like some of your\npartner Pokémon are feeling a little down.$Why not have a battle with me to cheer them up?",
|
||||
"title": "The Expert Pokémon Breeder",
|
||||
"description": "You've been challenged to a battle where @[TOOLTIP_TITLE]{you can only use a single Pokémon}. It might be tough, but it would surely deepen the bond you have with the Pokémon you choose!\nThe breeder will also give you some @[TOOLTIP_TITLE]{Pokémon Eggs} if you win!",
|
||||
"query": "Who will you battle with?",
|
||||
"cleffa_1_nickname": "Ace",
|
||||
"cleffa_2_nickname": "Clefablest",
|
||||
"cleffa_3_nickname": "{{speciesName}} the Great",
|
||||
"option": {
|
||||
"1": {
|
||||
"label": "{{pokemon1Name}}",
|
||||
"tooltip_base": "(-) Tough Battle\n(+) Gain Friendship with {{pokemon1Name}}"
|
||||
},
|
||||
"2": {
|
||||
"label": "{{pokemon2Name}}",
|
||||
"tooltip_base": "(-) Tough Battle\n(+) Gain Friendship with {{pokemon2Name}}"
|
||||
},
|
||||
"3": {
|
||||
"label": "{{pokemon3Name}}",
|
||||
"tooltip_base": "(-) Tough Battle\n(+) Gain Friendship with {{pokemon3Name}}"
|
||||
},
|
||||
"selected": "Let's do this!"
|
||||
},
|
||||
"outro": "Look how happy your {{chosenPokemon}} is now!$Here, you can have these as well.",
|
||||
"gained_eggs": "@s{item_fanfare}You received {{numEggs}}!",
|
||||
"eggs_tooltip": "\n(+) Earn {{eggs}}",
|
||||
"numEggs_one": "{{count}} {{rarity}} Egg",
|
||||
"numEggs_other": "{{count}} {{rarity}} Eggs"
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"battlesWon": "Battles Won!",
|
||||
"battlesWon": "{{count, number}} Battles Won!",
|
||||
"joinTheDiscord": "Join the Discord!",
|
||||
"infiniteLevels": "Infinite Levels!",
|
||||
"everythingStacks": "Everything Stacks!",
|
||||
@ -32,5 +32,17 @@
|
||||
"alsoTryRadicalRed": "Also Try Radical Red!",
|
||||
"eeveeExpo": "Eevee Expo!",
|
||||
"ynoproject": "YNOproject!",
|
||||
"breedersInSpace": "Breeders in space!"
|
||||
"breedersInSpace": "Breeders in space!",
|
||||
"halloween": {
|
||||
"pumpkaboosAbout": "Pumpkaboos about!",
|
||||
"mayContainSpiders": "May contain spiders!",
|
||||
"spookyScaryDuskulls": "Spooky, Scary Duskulls!"
|
||||
},
|
||||
"xmas": {
|
||||
"happyHolidays": "Happy Holidays!",
|
||||
"delibirdSeason": "Delibird Season!"
|
||||
},
|
||||
"newYears": {
|
||||
"happyNewYear": "Happy New Year!"
|
||||
}
|
||||
}
|
@ -178,5 +178,6 @@
|
||||
"vivi": "Vivi",
|
||||
"vicky": "Vicky",
|
||||
"vito": "Vito",
|
||||
"bug_type_superfan": "Bug-Type Superfan"
|
||||
"bug_type_superfan": "Bug-Type Superfan",
|
||||
"expert_pokemon_breeder": "Expert Pokémon Breeder"
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"battlesWon": "¡Batallas ganadas!",
|
||||
"battlesWon": "¡{{count, number}} Batallas ganadas!",
|
||||
"joinTheDiscord": "¡Únete al Discord!",
|
||||
"infiniteLevels": "¡Niveles infinitos!",
|
||||
"everythingStacks": "¡Todo se acumula!",
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"battlesWon": "combats gagnés !",
|
||||
"battlesWon": "{{count, number}} combats gagnés !",
|
||||
"joinTheDiscord": "Rejoins le Discord !",
|
||||
"infiniteLevels": "Niveaux infinis !",
|
||||
"everythingStacks": "Tout se cumule !",
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"battlesWon": "Battaglie Vinte!",
|
||||
"battlesWon": "{{count, number}} Battaglie Vinte!",
|
||||
"joinTheDiscord": "Entra nel Discord!",
|
||||
"infiniteLevels": "Livelli Infiniti!",
|
||||
"everythingStacks": "Tutto si impila!",
|
||||
|
@ -1,10 +1,10 @@
|
||||
{
|
||||
"encounter": "It appears the time has finally come once again.\nYou know why you have come here, do you not?\n$You were drawn here, because you have been here before.\nCountless times.\n$Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle.\n$Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain.\n$Until now you have yet to succeed, but I sense a different presence in you this time.\n\n$You are the only one here, though it is as if there is… another.\n$Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia?\n$We begin.",
|
||||
"encounter_female": "It appears the time has finally come once again.\nYou know why you have come here, do you not?\n$You were drawn here, because you have been here before.\nCountless times.\n$Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle.\n$Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain.\n$Until now you have yet to succeed, but I sense a different presence in you this time.\n\n$You are the only one here, though it is as if there is… another.\n$Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia?\n$We begin.",
|
||||
"firstStageWin": "I see. The presence I felt was indeed real.\nIt appears I no longer need to hold back.\n$Do not disappoint me.",
|
||||
"secondStageWin": "…Magnificent.",
|
||||
"key_ordinal_one": "st",
|
||||
"key_ordinal_two": "nd",
|
||||
"key_ordinal_few": "rd",
|
||||
"key_ordinal_other": "th"
|
||||
"encounter": "又しても 時が満ちた 様 である。\nこちらへ 至る 理由は 存知するな。\n$汝は この場所へ 引かれた……\nこの何度となく 至った場所。\n$けれども 数えられぬとも 限らぬ……\n正確に宣ふ(のたまう)と 現在の 循環は {{cycleCount}}回目 である。\n$各循環に 心… 意識… 両方も 元の有様に 戻る。\nなれども 故吾の 残影は 汝の 中に 存する。\n$未だに 成功せぬ ままでも\n異なる 存在を 感ずる。\n$御座在る者は 一人。\nなれども 感ずるは… もう 他人。\n$到頭 汝から いかめしい 挑戦は 我が目にかかるか?\n千歳 万歳 相まってた挑戦……\n$始もう。",
|
||||
"encounter_female": "又しても 時が満ちた 様 である。\nこちらへ 至る 理由は 存知するな。\n$汝は この場所へ 引かれた……\nこの何度となく 至った場所。\n$けれども 数えられぬとも 限らぬ……\n正確に宣ふ(のたまう)と 現在の 循環は {{cycleCount}}回目 である。\n$各循環に 心… 意識… 両方も 元の有様に 戻る。\nなれども 故吾の 残影は 汝の 中に 存する。\n$未だに 成功せぬ ままでも\n異なる 存在を 感ずる。\n$御座在る者は 一人。\nなれども 感ずるは… もう 他人。\n$到頭 汝から いかめしい 挑戦は 我が目にかかるか?\n千歳 万歳 相まってた挑戦……\n$始もう。",
|
||||
"firstStageWin": "成る程。 感じた 存在は 正身(むざね) であった。\n自分を括る 必要 有らぬ 様である。\n$失望させぬが良い。",
|
||||
"secondStageWin": "……お見事でございます。",
|
||||
"key_ordinal_one": "",
|
||||
"key_ordinal_two": "",
|
||||
"key_ordinal_few": "",
|
||||
"key_ordinal_other": ""
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"ending": "@c{shock}You're back?@d{32} Does that mean…@d{96} you won?!\n@c{smile_ehalf}I should have known you had it in you.\n$@c{smile_eclosed}Of course… I always had that feeling.\n@c{smile}It's over now, right? You ended the loop.\n$@c{smile_ehalf}You fulfilled your dream too, didn't you?\nYou didn't lose even once.\n$I'll be the only one to remember what you did.\n@c{angry_mopen}I'll try not to forget!\n$@c{smile_wave_wink}Just kidding!@d{64} @c{smile}I'd never forget.@d{32}\nYour legend will live on in our hearts.\n$@c{smile_wave}Anyway,@d{64} it's getting late…@d{96} I think?\nIt's hard to tell in this place.\n$Let's go home. @c{smile_wave_wink}Maybe tomorrow, we can have another battle, for old time's sake?",
|
||||
"ending_female": "@c{smile}Oh? You won?@d{96} @c{smile_eclosed}I guess I should've known.\nBut, you're back now.\n$@c{smile}It's over.@d{64} You ended the loop.\n$@c{serious_smile_fists}You fulfilled your dream too, didn't you?\nYou didn't lose even once.\n$@c{neutral}I'm the only one who'll remember what you did.@d{96}\nI guess that's okay, isn't it?\n$@c{serious_smile_fists}Your legend will always live on in our hearts.\n$@c{smile_eclosed}Anyway, I've had about enough of this place, haven't you? Let's head home.\n$@c{serious_smile_fists}Maybe when we get back, we can have another battle?\nIf you're up to it.",
|
||||
"ending_endless": "Congratulations on reaching the current end!\nMore content is coming soon.",
|
||||
"ending_name": "Devs"
|
||||
"ending": "@c{shock}帰ってきた?@d{32} それなら…@d{96} 勝った っていうこと だろう?!\n@c{smile_ehalf}絶対 やれると思う べきだったな。\n$@c{smile_eclosed}もちろん、ずっと そんな気 がしたんだな。\n@c{smile}ついに 終わった だろう? ループを 断ち切った。\n$@c{smile_ehalf}キミの夢も 叶ったよな?\n一回も 負けなかった。\n$キミの しつくした事を 覚えるのは おれだけだ。\n@c{angry_mopen}忘れない ように するが…\n$@c{smile_wave_wink}なんつって!@d{64} @c{smile}一生 忘れない。@d{32}\nキミの伝説は いつまでも みんなの 心の中に 残っているから。\n$@c{smile_wave}とにかく、@d{64} そろそろ 遅くなる……@d{96} かな?\nこの場所で よく 分からない。\n$さあ、帰ろう。\n@c{smile_wave_wink}明日、昔のよしみで バトルでも しないか?",
|
||||
"ending_female": "@c{smile}えぇ? 勝ちゃった?@d{96} @c{smile_eclosed}勝てるのが 分かる べきだったね。\nやっぱり 帰ってきた…\n$@c{smile}ついに終わった。@d{64} ループを 断ち切った。\n$@c{serious_smile_fists} アナタの夢も 叶ったよね?\n一回も 負けなかった!\n$@c{neutral}アタシだけが アナタが できた事 を覚えていく、ね。@d{96}\nでもね、たぶん 大丈夫なの かなぁ?\n$@c{serious_smile_fists}アナタの伝説は みんなの 心の中に\nずっと 残っているからね……\n$@c{smile_eclosed}じゃあ、こんなトコは もう飽きた だろう?\n帰ろうよ。\n$@c{serious_smile_fists}ふるさとに 着いたら、 また バトルしよう?\nやる気 あればね!",
|
||||
"ending_endless": "現在のエンドまで やって来て おめでとう!\n更に多くの コンテンツは 近日公開予定です。",
|
||||
"ending_name": "開発者"
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@
|
||||
"newGame": "はじめから",
|
||||
"settings": "設定",
|
||||
"selectGameMode": "ゲームモードを 選んでください。",
|
||||
"logInOrCreateAccount": "始めるには、ログイン、または 登録して ください。\nメールアドレスは 必要が ありません!",
|
||||
"logInOrCreateAccount": "始めるには、ログイン、または 登録して ください。\nメールアドレスは 必要 ありません!",
|
||||
"username": "ユーザー名",
|
||||
"password": "パスワード",
|
||||
"login": "ログイン",
|
||||
@ -14,7 +14,7 @@
|
||||
"register": "登録",
|
||||
"emptyUsername": "ユーザー名を 空にする ことは できません",
|
||||
"invalidLoginUsername": "入力されたユーザー名は無効です",
|
||||
"invalidRegisterUsername": "ユーザー名には 英文字、 数字、 アンダースコアのみを 含くむ必要が あります",
|
||||
"invalidRegisterUsername": "ユーザー名には 英文字、 数字、 アンダースコアのみを 含くむことが 必要です",
|
||||
"invalidLoginPassword": "入力したパスワードは無効です",
|
||||
"invalidRegisterPassword": "パスワードは 6文字以上 でなければなりません",
|
||||
"usernameAlreadyUsed": "入力したユーザー名は すでに 使用されています",
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"battlesWon": "Battles Won!",
|
||||
"battlesWon": "勝ったバトル:{{count, number}}回!",
|
||||
"joinTheDiscord": "Join the Discord!",
|
||||
"infiniteLevels": "Infinite Levels!",
|
||||
"everythingStacks": "Everything Stacks!",
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"battlesWon": "전투에서 승리하세요!",
|
||||
"battlesWon": "{{count, number}} 전투에서 승리하세요!",
|
||||
"joinTheDiscord": "디스코드에 가입하세요!",
|
||||
"infiniteLevels": "무한한 레벨!",
|
||||
"everythingStacks": "모든 것이 누적됩니다!",
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"battlesWon": "Batalhas Ganhas!",
|
||||
"battlesWon": "{{count, number}} Batalhas Ganhas!",
|
||||
"joinTheDiscord": "Junte-se ao Discord!",
|
||||
"infiniteLevels": "Níveis Infinitos!",
|
||||
"everythingStacks": "Tudo Acumula!",
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"battlesWon": "场胜利!",
|
||||
"battlesWon": "{{count, number}} 场胜利!",
|
||||
"joinTheDiscord": "加入Discord!",
|
||||
"infiniteLevels": "等级无限!",
|
||||
"everythingStacks": "道具全部叠加!",
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"battlesWon": "勝利場數!",
|
||||
"battlesWon": "{{count, number}} 勝利場數!",
|
||||
"joinTheDiscord": "加入Discord!",
|
||||
"infiniteLevels": "無限等級!",
|
||||
"everythingStacks": "所有效果都能疊加!",
|
||||
|
@ -1761,7 +1761,7 @@ const modifierPool: ModifierPool = {
|
||||
new WeightedModifierType(modifierTypes.ABILITY_CHARM, skipInClassicAfterWave(189, 6)),
|
||||
new WeightedModifierType(modifierTypes.FOCUS_BAND, 5),
|
||||
new WeightedModifierType(modifierTypes.KINGS_ROCK, 3),
|
||||
new WeightedModifierType(modifierTypes.LOCK_CAPSULE, skipInLastClassicWaveOrDefault(3)),
|
||||
new WeightedModifierType(modifierTypes.LOCK_CAPSULE, (party: Pokemon[]) => party[0].scene.gameMode.isClassic ? 0 : 3),
|
||||
new WeightedModifierType(modifierTypes.SUPER_EXP_CHARM, skipInLastClassicWaveOrDefault(8)),
|
||||
new WeightedModifierType(modifierTypes.RARE_FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24),
|
||||
new WeightedModifierType(modifierTypes.MEGA_BRACELET, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 9, 36),
|
||||
|
@ -1,6 +1,6 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { BattlerIndex, BattleType } from "#app/battle";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { BattlerIndex, BattleType, ClassicFixedBossWaves } from "#app/battle";
|
||||
import { CustomModifierSettings, modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { BattleEndPhase } from "./battle-end-phase";
|
||||
import { NewBattlePhase } from "./new-battle-phase";
|
||||
import { PokemonPhase } from "./pokemon-phase";
|
||||
@ -42,8 +42,12 @@ export class VictoryPhase extends PokemonPhase {
|
||||
}
|
||||
if (this.scene.gameMode.isEndless || !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) {
|
||||
this.scene.pushPhase(new EggLapsePhase(this.scene));
|
||||
if (this.scene.gameMode.isClassic && this.scene.currentBattle.waveIndex === ClassicFixedBossWaves.EVIL_BOSS_2) {
|
||||
// Should get Lock Capsule on 165 before shop phase so it can be used in the rewards shop
|
||||
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.LOCK_CAPSULE));
|
||||
}
|
||||
if (this.scene.currentBattle.waveIndex % 10) {
|
||||
this.scene.pushPhase(new SelectModifierPhase(this.scene));
|
||||
this.scene.pushPhase(new SelectModifierPhase(this.scene, undefined, undefined, this.getFixedBattleCustomModifiers()));
|
||||
} else if (this.scene.gameMode.isDaily) {
|
||||
this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.EXP_CHARM));
|
||||
if (this.scene.currentBattle.waveIndex > 10 && !this.scene.gameMode.isWaveFinal(this.scene.currentBattle.waveIndex)) {
|
||||
@ -76,4 +80,18 @@ export class VictoryPhase extends PokemonPhase {
|
||||
|
||||
this.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* If this wave is a fixed battle with special custom modifier rewards,
|
||||
* will pass those settings to the upcoming {@linkcode SelectModifierPhase}`.
|
||||
*/
|
||||
getFixedBattleCustomModifiers(): CustomModifierSettings | undefined {
|
||||
const gameMode = this.scene.gameMode;
|
||||
const waveIndex = this.scene.currentBattle.waveIndex;
|
||||
if (gameMode.isFixedBattle(waveIndex)) {
|
||||
return gameMode.getFixedBattle(waveIndex).customModifierRewardSettings;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
@ -279,6 +279,8 @@ export function getAchievementDescription(localizationKey: string): string {
|
||||
return i18next.t("achv:FRESH_START.description", { context: genderStr });
|
||||
case "INVERSE_BATTLE":
|
||||
return i18next.t("achv:INVERSE_BATTLE.description", { context: genderStr });
|
||||
case "BREEDERS_IN_SPACE":
|
||||
return i18next.t("achv:BREEDERS_IN_SPACE.description", { context: genderStr });
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
@ -356,6 +358,7 @@ export const achvs = {
|
||||
MONO_FAIRY: new ChallengeAchv("MONO_FAIRY", "", "MONO_FAIRY.description", "fairy_feather", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 18 && !scene.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, scene) => c instanceof FreshStartChallenge && c.value > 0 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||
INVERSE_BATTLE: new ChallengeAchv("INVERSE_BATTLE", "", "INVERSE_BATTLE.description", "inverse", 100, c => c instanceof InverseBattleChallenge && c.value > 0),
|
||||
BREEDERS_IN_SPACE: new Achv("BREEDERS_IN_SPACE", "", "BREEDERS_IN_SPACE.description", "moon_stone", 100).setSecret(),
|
||||
};
|
||||
|
||||
export function initAchievements() {
|
||||
|
66
src/test/data/splash_messages.test.ts
Normal file
66
src/test/data/splash_messages.test.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { getSplashMessages } from "#app/data/splash-messages";
|
||||
import { describe, expect, it, vi, afterEach, beforeEach } from "vitest";
|
||||
import * as Constants from "#app/constants";
|
||||
|
||||
describe("Data - Splash Messages", () => {
|
||||
it("should contain at least 15 splash messages", () => {
|
||||
expect(getSplashMessages().length).toBeGreaterThanOrEqual(15);
|
||||
});
|
||||
|
||||
// make sure to adjust this test if the weight it changed!
|
||||
it("should add contain 10 `battlesWon` splash messages", () => {
|
||||
const battlesWonMessages = getSplashMessages().filter((message) => message === "splashMessages:battlesWon");
|
||||
expect(battlesWonMessages).toHaveLength(10);
|
||||
});
|
||||
|
||||
describe("Seasonal", () => {
|
||||
beforeEach(() => {
|
||||
vi.spyOn(Constants, "USE_SEASONAL_SPLASH_MESSAGES", "get").mockReturnValue(true);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers(); // reset system time
|
||||
});
|
||||
|
||||
it("should contain halloween messages from Sep 15 to Oct 31", () => {
|
||||
testSeason(new Date("2024-09-15"), new Date("2024-10-31"), "halloween");
|
||||
});
|
||||
|
||||
it("should contain xmas messages from Dec 1 to Dec 26", () => {
|
||||
testSeason(new Date("2024-12-01"), new Date("2024-12-26"), "xmas");
|
||||
});
|
||||
|
||||
it("should contain new years messages frm Jan 1 to Jan 31", () => {
|
||||
testSeason(new Date("2024-01-01"), new Date("2024-01-31"), "newYears");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Helpoer method to test seasonal messages
|
||||
* @param startDate The seasons start date
|
||||
* @param endDate The seasons end date
|
||||
* @param prefix the splash message prefix (e.g. `newYears` or `xmas`)
|
||||
*/
|
||||
function testSeason(startDate: Date, endDate: Date, prefix: string) {
|
||||
const filterFn = (message: string) => message.startsWith(`splashMessages:${prefix}.`);
|
||||
|
||||
const beforeDate = new Date(startDate);
|
||||
beforeDate.setDate(startDate.getDate() - 1);
|
||||
|
||||
const afterDate = new Date(endDate);
|
||||
afterDate.setDate(endDate.getDate() + 1);
|
||||
|
||||
const dates: Date[] = [beforeDate, startDate, endDate, afterDate];
|
||||
const [before, start, end, after] = dates.map((date) => {
|
||||
vi.setSystemTime(date);
|
||||
console.log("System time set to", date);
|
||||
const count = getSplashMessages().filter(filterFn).length;
|
||||
return count;
|
||||
});
|
||||
|
||||
expect(before).toBe(0);
|
||||
expect(start).toBeGreaterThanOrEqual(10); // make sure to adjust if weight is changed!
|
||||
expect(end).toBeGreaterThanOrEqual(10); // make sure to adjust if weight is changed!
|
||||
expect(after).toBe(0);
|
||||
}
|
@ -69,22 +69,6 @@ describe("A Trainer's Test - Mystery Encounter", () => {
|
||||
expect(ATrainersTestEncounter.options.length).toBe(2);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.A_TRAINERS_TEST);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully ", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = ATrainersTestEncounter;
|
||||
|
@ -66,22 +66,6 @@ describe("Absolute Avarice - Mystery Encounter", () => {
|
||||
expect(AbsoluteAvariceEncounter.options.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.ABSOLUTE_AVARICE);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not spawn outside of proper biomes", async () => {
|
||||
game.override.mysteryEncounterTier(MysteryEncounterTier.GREAT);
|
||||
game.override.startingBiome(Biome.VOLCANO);
|
||||
|
@ -80,22 +80,6 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => {
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully ", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = AnOfferYouCantRefuseEncounter;
|
||||
|
@ -69,22 +69,6 @@ describe("Berries Abound - Mystery Encounter", () => {
|
||||
expect(BerriesAboundEncounter.options.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.BERRIES_ABOUND);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = BerriesAboundEncounter;
|
||||
|
@ -201,22 +201,6 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
||||
expect(BugTypeSuperfanEncounter.options.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.BUG_TYPE_SUPERFAN);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = BugTypeSuperfanEncounter;
|
||||
|
@ -95,14 +95,6 @@ describe("Clowning Around - Mystery Encounter", () => {
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.CLOWNING_AROUND);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = ClowningAroundEncounter;
|
||||
|
@ -69,22 +69,6 @@ describe("Dancing Lessons - Mystery Encounter", () => {
|
||||
expect(DancingLessonsEncounter.options.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DANCING_LESSONS);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not spawn outside of proper biomes", async () => {
|
||||
game.override.mysteryEncounterTier(MysteryEncounterTier.GREAT);
|
||||
game.override.startingBiome(Biome.SPACE);
|
||||
|
@ -66,22 +66,6 @@ describe("Delibird-y - Mystery Encounter", () => {
|
||||
expect(DelibirdyEncounter.options.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DELIBIRDY);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not spawn if player does not have enough money", async () => {
|
||||
scene.money = 0;
|
||||
|
||||
|
@ -79,22 +79,6 @@ describe("Department Store Sale - Mystery Encounter", () => {
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DEPARTMENT_STORE_SALE);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.DEPARTMENT_STORE_SALE);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
describe("Option 1 - TM Shop", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = DepartmentStoreSaleEncounter.options[0];
|
||||
|
@ -72,22 +72,6 @@ describe("Field Trip - Mystery Encounter", () => {
|
||||
expect(FieldTripEncounter.options.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.FIELD_TRIP);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
describe("Option 1 - Show off a physical move", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = FieldTripEncounter.options[0];
|
||||
|
@ -88,14 +88,6 @@ describe("Fiery Fallout - Mystery Encounter", () => {
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.FIERY_FALLOUT);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully ", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = FieryFalloutEncounter;
|
||||
|
@ -67,22 +67,6 @@ describe("Fight or Flight - Mystery Encounter", () => {
|
||||
expect(FightOrFlightEncounter.options.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.FIGHT_OR_FLIGHT);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = FightOrFlightEncounter;
|
||||
|
@ -85,22 +85,6 @@ describe("Fun And Games! - Mystery Encounter", () => {
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.FUN_AND_GAMES);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.FUN_AND_GAMES);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = new MysteryEncounter(FunAndGamesEncounter);
|
||||
|
@ -69,22 +69,6 @@ describe("Global Trade System - Mystery Encounter", () => {
|
||||
expect(GlobalTradeSystemEncounter.options.length).toBe(4);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.GLOBAL_TRADE_SYSTEM);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not spawn outside of CIVILIZATION_ENCOUNTER_BIOMES", async () => {
|
||||
game.override.mysteryEncounterTier(MysteryEncounterTier.COMMON);
|
||||
game.override.startingBiome(Biome.VOLCANO);
|
||||
|
@ -74,22 +74,6 @@ describe("Lost at Sea - Mystery Encounter", () => {
|
||||
expect(game.scene.currentBattle.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.LOST_AT_SEA);
|
||||
});
|
||||
|
||||
it("should not run below wave 11", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(game.scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(game.scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully", () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = LostAtSeaEncounter;
|
||||
|
@ -79,22 +79,6 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.MYSTERIOUS_CHALLENGERS);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.MYSTERIOUS_CHALLENGERS);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = new MysteryEncounter(MysteriousChallengersEncounter);
|
||||
|
@ -80,22 +80,6 @@ describe("Part-Timer - Mystery Encounter", () => {
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.PART_TIMER);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.PART_TIMER);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
describe("Option 1 - Make Deliveries", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = PartTimerEncounter.options[0];
|
||||
|
@ -67,22 +67,6 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
|
||||
expect(TeleportingHijinksEncounter.options.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.TELEPORTING_HIJINKS);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should run in waves that are X1", async () => {
|
||||
game.override.startingWave(11);
|
||||
game.override.mysteryEncounterTier(MysteryEncounterTier.COMMON);
|
||||
|
@ -0,0 +1,283 @@
|
||||
import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters";
|
||||
import { HUMAN_TRANSITABLE_BIOMES } from "#app/data/mystery-encounters/mystery-encounters";
|
||||
import { Biome } from "#app/enums/biome";
|
||||
import { MysteryEncounterType } from "#app/enums/mystery-encounter-type";
|
||||
import { Species } from "#app/enums/species";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { runMysteryEncounterToEnd, skipBattleRunMysteryEncounterRewardsPhase } from "#test/mystery-encounter/encounter-test-utils";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils";
|
||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||
import { CommandPhase } from "#app/phases/command-phase";
|
||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||
import { TheExpertPokemonBreederEncounter } from "#app/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { EggTier } from "#enums/egg-type";
|
||||
import { PostMysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
||||
|
||||
const namespace = "mysteryEncounter:expertPokemonBreeder";
|
||||
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
||||
const defaultBiome = Biome.CAVE;
|
||||
const defaultWave = 45;
|
||||
|
||||
describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
let scene: BattleScene;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({ type: Phaser.HEADLESS });
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
game = new GameManager(phaserGame);
|
||||
scene = game.scene;
|
||||
game.override.mysteryEncounterChance(100);
|
||||
game.override.startingWave(defaultWave);
|
||||
game.override.startingBiome(defaultBiome);
|
||||
game.override.disableTrainerWaves();
|
||||
|
||||
const biomeMap = new Map<Biome, MysteryEncounterType[]>([
|
||||
[Biome.VOLCANO, [MysteryEncounterType.FIGHT_OR_FLIGHT]],
|
||||
]);
|
||||
HUMAN_TRANSITABLE_BIOMES.forEach(biome => {
|
||||
biomeMap.set(biome, [MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER]);
|
||||
});
|
||||
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(biomeMap);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
vi.clearAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("should have the correct properties", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty);
|
||||
|
||||
expect(TheExpertPokemonBreederEncounter.encounterType).toBe(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER);
|
||||
expect(TheExpertPokemonBreederEncounter.encounterTier).toBe(MysteryEncounterTier.ULTRA);
|
||||
expect(TheExpertPokemonBreederEncounter.dialogue).toBeDefined();
|
||||
expect(TheExpertPokemonBreederEncounter.dialogue.intro).toStrictEqual([
|
||||
{
|
||||
text: `${namespace}.intro`
|
||||
},
|
||||
{
|
||||
speaker: "trainerNames:expert_pokemon_breeder",
|
||||
text: `${namespace}.intro_dialogue`
|
||||
},
|
||||
]);
|
||||
expect(TheExpertPokemonBreederEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`);
|
||||
expect(TheExpertPokemonBreederEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`);
|
||||
expect(TheExpertPokemonBreederEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`);
|
||||
expect(TheExpertPokemonBreederEncounter.options.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should not spawn outside of HUMAN_TRANSITABLE_BIOMES", async () => {
|
||||
game.override.mysteryEncounterTier(MysteryEncounterTier.GREAT);
|
||||
game.override.startingBiome(Biome.VOLCANO);
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER);
|
||||
});
|
||||
|
||||
it("should initialize fully", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = new MysteryEncounter(TheExpertPokemonBreederEncounter);
|
||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||
scene.currentBattle.waveIndex = defaultWave;
|
||||
|
||||
const { onInit } = encounter;
|
||||
|
||||
expect(encounter.onInit).toBeDefined();
|
||||
|
||||
encounter.populateDialogueTokensFromRequirements(scene);
|
||||
const onInitResult = onInit!(scene);
|
||||
|
||||
expect(encounter.enemyPartyConfigs).toBeDefined();
|
||||
expect(encounter.enemyPartyConfigs.length).toBe(1);
|
||||
expect(encounter.enemyPartyConfigs[0].trainerType).toBe(TrainerType.EXPERT_POKEMON_BREEDER);
|
||||
expect(encounter.enemyPartyConfigs[0].pokemonConfigs?.length).toBe(3);
|
||||
expect(encounter.spriteConfigs).toBeDefined();
|
||||
expect(encounter.spriteConfigs.length).toBe(2);
|
||||
expect(onInitResult).toBe(true);
|
||||
});
|
||||
|
||||
describe("Option 1 - Battle with Pokemon 1", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = TheExpertPokemonBreederEncounter.options[0];
|
||||
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||
expect(option.dialogue).toBeDefined();
|
||||
expect(option.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}.option.1.label`,
|
||||
buttonTooltip: expect.any(String), // Varies based on pokemon
|
||||
selected: [
|
||||
{
|
||||
speaker: "trainerNames:expert_pokemon_breeder",
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should start battle against the trainer", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||
|
||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
|
||||
expect(scene.currentBattle.trainer).toBeDefined();
|
||||
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
|
||||
expect(scene.getParty().length).toBe(1);
|
||||
});
|
||||
|
||||
it("Should reward the player with friendship and eggs based on pokemon selected", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty);
|
||||
|
||||
const friendshipBefore = scene.currentBattle.mysteryEncounter!.misc.pokemon1.friendship;
|
||||
|
||||
scene.gameData.eggs = [];
|
||||
const eggsBefore = scene.gameData.eggs;
|
||||
expect(eggsBefore).toBeDefined();
|
||||
const eggsBeforeLength = eggsBefore.length;
|
||||
|
||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
|
||||
const eggsAfter = scene.gameData.eggs;
|
||||
const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon1CommonEggs;
|
||||
const rareEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon1RareEggs;
|
||||
expect(eggsAfter).toBeDefined();
|
||||
expect(eggsBeforeLength + commonEggs + rareEggs).toBe(eggsAfter.length);
|
||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs);
|
||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.GREAT).length).toBe(rareEggs);
|
||||
|
||||
game.phaseInterceptor.superEndPhase();
|
||||
await game.phaseInterceptor.to(PostMysteryEncounterPhase);
|
||||
|
||||
const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon1.friendship;
|
||||
expect(friendshipAfter).toBe(friendshipBefore + 20 + 2); // +2 extra for friendship gained from winning battle
|
||||
});
|
||||
});
|
||||
|
||||
describe("Option 2 - Battle with Pokemon 2", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = TheExpertPokemonBreederEncounter.options[1];
|
||||
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||
expect(option.dialogue).toBeDefined();
|
||||
expect(option.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}.option.2.label`,
|
||||
buttonTooltip: expect.any(String), // Varies based on pokemon
|
||||
selected: [
|
||||
{
|
||||
speaker: "trainerNames:expert_pokemon_breeder",
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should start battle against the trainer", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||
|
||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
|
||||
expect(scene.currentBattle.trainer).toBeDefined();
|
||||
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
|
||||
expect(scene.getParty().length).toBe(1);
|
||||
});
|
||||
|
||||
it("Should reward the player with friendship and eggs based on pokemon selected", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty);
|
||||
|
||||
const friendshipBefore = scene.currentBattle.mysteryEncounter!.misc.pokemon2.friendship;
|
||||
|
||||
scene.gameData.eggs = [];
|
||||
const eggsBefore = scene.gameData.eggs;
|
||||
expect(eggsBefore).toBeDefined();
|
||||
const eggsBeforeLength = eggsBefore.length;
|
||||
|
||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
|
||||
const eggsAfter = scene.gameData.eggs;
|
||||
const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon2CommonEggs;
|
||||
const rareEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon2RareEggs;
|
||||
expect(eggsAfter).toBeDefined();
|
||||
expect(eggsBeforeLength + commonEggs + rareEggs).toBe(eggsAfter.length);
|
||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs);
|
||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.GREAT).length).toBe(rareEggs);
|
||||
|
||||
game.phaseInterceptor.superEndPhase();
|
||||
await game.phaseInterceptor.to(PostMysteryEncounterPhase);
|
||||
|
||||
const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon2.friendship;
|
||||
expect(friendshipAfter).toBe(friendshipBefore + 20 + 2); // +2 extra for friendship gained from winning battle
|
||||
});
|
||||
});
|
||||
|
||||
describe("Option 3 - Battle with Pokemon 3", () => {
|
||||
it("should have the correct properties", () => {
|
||||
const option = TheExpertPokemonBreederEncounter.options[2];
|
||||
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||
expect(option.dialogue).toBeDefined();
|
||||
expect(option.dialogue).toStrictEqual({
|
||||
buttonLabel: `${namespace}.option.3.label`,
|
||||
buttonTooltip: expect.any(String), // Varies based on pokemon
|
||||
selected: [
|
||||
{
|
||||
speaker: "trainerNames:expert_pokemon_breeder",
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should start battle against the trainer", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 3, undefined, true);
|
||||
|
||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
|
||||
expect(scene.currentBattle.trainer).toBeDefined();
|
||||
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
|
||||
expect(scene.getParty().length).toBe(1);
|
||||
});
|
||||
|
||||
it("Should reward the player with friendship and eggs based on pokemon selected", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER, defaultParty);
|
||||
|
||||
const friendshipBefore = scene.currentBattle.mysteryEncounter!.misc.pokemon3.friendship;
|
||||
|
||||
scene.gameData.eggs = [];
|
||||
const eggsBefore = scene.gameData.eggs;
|
||||
expect(eggsBefore).toBeDefined();
|
||||
const eggsBeforeLength = eggsBefore.length;
|
||||
|
||||
await runMysteryEncounterToEnd(game, 3, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
|
||||
const eggsAfter = scene.gameData.eggs;
|
||||
const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon3CommonEggs;
|
||||
const rareEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon3RareEggs;
|
||||
expect(eggsAfter).toBeDefined();
|
||||
expect(eggsBeforeLength + commonEggs + rareEggs).toBe(eggsAfter.length);
|
||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs);
|
||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.GREAT).length).toBe(rareEggs);
|
||||
|
||||
game.phaseInterceptor.superEndPhase();
|
||||
await game.phaseInterceptor.to(PostMysteryEncounterPhase);
|
||||
|
||||
const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon3.friendship;
|
||||
expect(friendshipAfter).toBe(friendshipBefore + 20 + 2); // +2 extra for friendship gained from winning battle
|
||||
});
|
||||
});
|
||||
});
|
@ -76,22 +76,6 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_POKEMON_SALESMAN);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_POKEMON_SALESMAN);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully ", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = ThePokemonSalesmanEncounter;
|
||||
|
@ -83,22 +83,6 @@ describe("The Strong Stuff - Mystery Encounter", () => {
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_STRONG_STUFF);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_STRONG_STUFF);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully ", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = TheStrongStuffEncounter;
|
||||
|
@ -90,22 +90,6 @@ describe("The Winstrate Challenge - Mystery Encounter", () => {
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_WINSTRATE_CHALLENGE);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.THE_WINSTRATE_CHALLENGE);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = new MysteryEncounter(TheWinstrateChallengeEncounter);
|
||||
|
@ -71,22 +71,6 @@ describe("Trash to Treasure - Mystery Encounter", () => {
|
||||
expect(TrashToTreasureEncounter.options.length).toBe(2);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.TRASH_TO_TREASURE);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = TrashToTreasureEncounter;
|
||||
|
@ -74,22 +74,6 @@ describe("Uncommon Breed - Mystery Encounter", () => {
|
||||
expect(UncommonBreedEncounter.options.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.UNCOMMON_BREED);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = UncommonBreedEncounter;
|
||||
|
@ -73,22 +73,6 @@ describe("Weird Dream - Mystery Encounter", () => {
|
||||
expect(WeirdDreamEncounter.options.length).toBe(2);
|
||||
});
|
||||
|
||||
it("should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.WEIRD_DREAM);
|
||||
});
|
||||
|
||||
it("should not run above wave 179", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should initialize fully", async () => {
|
||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||
scene.currentBattle.mysteryEncounter = WeirdDreamEncounter;
|
||||
|
@ -4,10 +4,12 @@ import Phaser from "phaser";
|
||||
import { Species } from "#enums/species";
|
||||
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
|
||||
describe("Mystery Encounters", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
let scene: BattleScene;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
@ -21,6 +23,7 @@ describe("Mystery Encounters", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
scene = game.scene;
|
||||
game.override.startingWave(11);
|
||||
game.override.mysteryEncounterChance(100);
|
||||
});
|
||||
@ -32,23 +35,20 @@ describe("Mystery Encounters", () => {
|
||||
expect(game.scene.getCurrentPhase()!.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||
});
|
||||
|
||||
it("", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, [Species.CHARIZARD, Species.VOLCARONA]);
|
||||
it("Encounters should not run below wave 10", async () => {
|
||||
game.override.startingWave(9);
|
||||
|
||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||
expect(game.scene.getCurrentPhase()!.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.MYSTERIOUS_CHALLENGERS);
|
||||
});
|
||||
|
||||
it("spawns mysterious challengers encounter", async () => {
|
||||
});
|
||||
it("Encounters should not run above wave 180", async () => {
|
||||
game.override.startingWave(181);
|
||||
|
||||
it("spawns mysterious chest encounter", async () => {
|
||||
});
|
||||
await game.runToMysteryEncounter();
|
||||
|
||||
it("spawns dark deal encounter", async () => {
|
||||
});
|
||||
|
||||
it("spawns fight or flight encounter", async () => {
|
||||
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -14,6 +14,8 @@ import { initStatsKeys } from "#app/ui/game-stats-ui-handler";
|
||||
import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters";
|
||||
import { beforeAll, vi } from "vitest";
|
||||
|
||||
process.env.TZ = "UTC";
|
||||
|
||||
/** Mock the override import to always return default values, ignoring any custom overrides. */
|
||||
vi.mock("#app/overrides", async (importOriginal) => {
|
||||
const { defaultOverrides } = await importOriginal<typeof import("#app/overrides")>();
|
||||
|
@ -381,17 +381,8 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
|
||||
|
||||
const ownedAbilityAttrs = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr;
|
||||
|
||||
let playerOwnsThisAbility = false;
|
||||
// Check if the player owns ability for the root form
|
||||
if ((ownedAbilityAttrs & 1) > 0 && pokemon.hasSameAbilityInRootForm(0)) {
|
||||
playerOwnsThisAbility = true;
|
||||
}
|
||||
if ((ownedAbilityAttrs & 2) > 0 && pokemon.hasSameAbilityInRootForm(1)) {
|
||||
playerOwnsThisAbility = true;
|
||||
}
|
||||
if ((ownedAbilityAttrs & 4) > 0 && pokemon.hasSameAbilityInRootForm(2)) {
|
||||
playerOwnsThisAbility = true;
|
||||
}
|
||||
const playerOwnsThisAbility = pokemon.checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs);
|
||||
|
||||
if (missingDexAttrs || !playerOwnsThisAbility) {
|
||||
this.ownedIcon.setTint(0x808080);
|
||||
|
@ -267,18 +267,13 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
||||
this.pokemonAbilityText.setColor(getTextColor(abilityTextStyle, false, this.scene.uiTheme));
|
||||
this.pokemonAbilityText.setShadowColor(getTextColor(abilityTextStyle, true, this.scene.uiTheme));
|
||||
|
||||
/**
|
||||
* If the opposing Pokemon only has 1 normal ability and is using the hidden ability it should have the same behavior
|
||||
* if it had 2 normal abilities. This code checks if that is the case and uses the correct opponent Pokemon abilityIndex (2)
|
||||
* for calculations so it aligns with where the hidden ability is stored in the starter data's abilityAttr (4)
|
||||
*/
|
||||
const opponentPokemonOneNormalAbility = (pokemon.species.getAbilityCount() === 2);
|
||||
const opponentPokemonAbilityIndex = (opponentPokemonOneNormalAbility && pokemon.abilityIndex === 1) ? 2 : pokemon.abilityIndex;
|
||||
const opponentPokemonAbilityAttr = 1 << opponentPokemonAbilityIndex;
|
||||
|
||||
const rootFormHasHiddenAbility = starterEntry.abilityAttr & opponentPokemonAbilityAttr;
|
||||
const ownedAbilityAttrs = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr;
|
||||
|
||||
if (!rootFormHasHiddenAbility) {
|
||||
// Check if the player owns ability for the root form
|
||||
const playerOwnsThisAbility = pokemon.checkIfPlayerHasAbilityOfStarter(ownedAbilityAttrs);
|
||||
|
||||
if (!playerOwnsThisAbility) {
|
||||
this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme));
|
||||
this.pokemonAbilityLabelText.setShadowColor(getTextColor(TextStyle.SUMMARY_BLUE, true, this.scene.uiTheme));
|
||||
} else {
|
||||
|
@ -3,11 +3,14 @@ import OptionSelectUiHandler from "./settings/option-select-ui-handler";
|
||||
import { Mode } from "./ui";
|
||||
import * as Utils from "../utils";
|
||||
import { TextStyle, addTextObject, getTextStyleOptions } from "./text";
|
||||
import { getBattleCountSplashMessage, getSplashMessages } from "../data/splash-messages";
|
||||
import { getSplashMessages } from "../data/splash-messages";
|
||||
import i18next from "i18next";
|
||||
import { TimedEventDisplay } from "#app/timed-event-manager";
|
||||
|
||||
export default class TitleUiHandler extends OptionSelectUiHandler {
|
||||
/** If the stats can not be retrieved, use this fallback value */
|
||||
private static readonly BATTLES_WON_FALLBACK: number = -99999999;
|
||||
|
||||
private titleContainer: Phaser.GameObjects.Container;
|
||||
private playerCountLabel: Phaser.GameObjects.Text;
|
||||
private splashMessage: string;
|
||||
@ -72,8 +75,8 @@ export default class TitleUiHandler extends OptionSelectUiHandler {
|
||||
.then(request => request.json())
|
||||
.then(stats => {
|
||||
this.playerCountLabel.setText(`${stats.playerCount} ${i18next.t("menu:playersOnline")}`);
|
||||
if (this.splashMessage === getBattleCountSplashMessage()) {
|
||||
this.splashMessageText.setText(getBattleCountSplashMessage().replace("{COUNT}", stats.battleCount.toLocaleString("en-US")));
|
||||
if (this.splashMessage === "splashMessages:battlesWon") {
|
||||
this.splashMessageText.setText(i18next.t(this.splashMessage, { count: stats.battleCount }));
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
@ -86,7 +89,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler {
|
||||
|
||||
if (ret) {
|
||||
this.splashMessage = Utils.randItem(getSplashMessages());
|
||||
this.splashMessageText.setText(this.splashMessage.replace("{COUNT}", "?"));
|
||||
this.splashMessageText.setText(i18next.t(this.splashMessage, { count: TitleUiHandler.BATTLES_WON_FALLBACK }));
|
||||
|
||||
const ui = this.getUi();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user