more PR feedback and update field trip with Rarer Candy

This commit is contained in:
ImperialSympathizer 2024-09-13 16:44:42 -04:00
parent 282eee8dd8
commit 0e7f9dc723
47 changed files with 240 additions and 156 deletions

View File

@ -265,7 +265,9 @@ export default class BattleScene extends SceneBase {
public money: integer;
public pokemonInfoContainer: PokemonInfoContainer;
private party: PlayerPokemon[];
/** Session save data that pertains to Mystery Encounters */
public mysteryEncounterSaveData: MysteryEncounterSaveData = new MysteryEncounterSaveData();
/** If the previous wave was a MysteryEncounter, tracks the object with this variable. Mostly used for visual object cleanup */
public lastMysteryEncounter?: MysteryEncounter;
/** Combined Biome and Wave count text */
private biomeWaveText: Phaser.GameObjects.Text;

View File

@ -80,6 +80,7 @@ export default class Battle {
public playerFaintsHistory: FaintLogEntry[] = [];
public enemyFaintsHistory: FaintLogEntry[] = [];
/** If the current battle is a Mystery Encounter, this will always be defined */
public mysteryEncounter?: MysteryEncounter;
private rngCounter: number = 0;

View File

@ -9,6 +9,7 @@ import { Moves } from "#enums/moves";
import { SubstituteTag } from "./battler-tags";
import { isNullOrUndefined } from "../utils";
import Phaser from "phaser";
import { EncounterAnim } from "#enums/encounter-anims";
//import fs from 'vite-plugin-fs/browser';
export enum AnimFrameTarget {
@ -105,18 +106,6 @@ export enum CommonAnim {
LOCK_ON = 2120
}
/**
* Animations used for Mystery Encounters
* These are custom animations that may or may not work in any other circumstance
* Use at your own risk
*/
export enum EncounterAnim {
MAGMA_BG,
MAGMA_SPOUT,
SMOKESCREEN,
DANCE
}
export class AnimConfig {
public id: integer;
public graphic: string;

View File

@ -2315,10 +2315,12 @@ export class MysteryEncounterPostSummonTag extends BattlerTag {
super(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON, BattlerTagLapseType.CUSTOM, 1);
}
/** Event when tag is added */
onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon);
}
/** Performs post-summon effects through {@linkcode Pokemon.mysteryEncounterBattleEffects} */
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
const ret = super.lapse(pokemon, lapseType);
@ -2335,6 +2337,7 @@ export class MysteryEncounterPostSummonTag extends BattlerTag {
return ret;
}
/** Event when tag is removed */
onRemove(pokemon: Pokemon): void {
super.onRemove(pokemon);
}

View File

@ -63,8 +63,8 @@ export interface IEggOptions {
*/
overrideHiddenAbility?: boolean,
/** If Egg is of {@link EggSourceType.EVENT}, can customize the message displayed for where the egg was obtained */
eventEggTypeDescriptor?: string;
/** Can customize the message displayed for where the egg was obtained */
eggDescriptor?: string;
}
export class Egg {
@ -86,7 +86,7 @@ export class Egg {
private _overrideHiddenAbility: boolean;
private _eventEggTypeDescriptor?: string;
private _eggDescriptor?: string;
////
// #endregion
@ -197,7 +197,7 @@ export class Egg {
generateEggProperties(eggOptions);
}
this._eventEggTypeDescriptor = eggOptions?.eventEggTypeDescriptor;
this._eggDescriptor = eggOptions?.eggDescriptor;
}
////
@ -299,15 +299,15 @@ export class Egg {
public getEggTypeDescriptor(scene: BattleScene): string {
switch (this.sourceType) {
case EggSourceType.SAME_SPECIES_EGG:
return i18next.t("egg:sameSpeciesEgg", { species: getPokemonSpecies(this._species).getName()});
return this._eggDescriptor ?? i18next.t("egg:sameSpeciesEgg", { species: getPokemonSpecies(this._species).getName()});
case EggSourceType.GACHA_LEGENDARY:
return `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp)).getName()})`;
return this._eggDescriptor ?? `${i18next.t("egg:gachaTypeLegendary")} (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp)).getName()})`;
case EggSourceType.GACHA_SHINY:
return i18next.t("egg:gachaTypeShiny");
return this._eggDescriptor ?? i18next.t("egg:gachaTypeShiny");
case EggSourceType.GACHA_MOVE:
return i18next.t("egg:gachaTypeMove");
return this._eggDescriptor ?? i18next.t("egg:gachaTypeMove");
case EggSourceType.EVENT:
return this._eventEggTypeDescriptor ?? i18next.t("egg:eventType");
return this._eggDescriptor ?? i18next.t("egg:eventType");
default:
console.warn("getEggTypeDescriptor case not defined. Returning default empty string");
return "";

View File

@ -147,7 +147,7 @@ export const ATrainersTestEncounter: MysteryEncounter =
scene,
pulled: false,
sourceType: EggSourceType.EVENT,
eventEggTypeDescriptor: encounter.misc.trainerEggDescription,
eggDescriptor: encounter.misc.trainerEggDescription,
tier: EggTier.ULTRA
};
encounter.setDialogueToken("eggType", i18next.t(`${namespace}.eggTypes.epic`));
@ -170,7 +170,7 @@ export const ATrainersTestEncounter: MysteryEncounter =
scene,
pulled: false,
sourceType: EggSourceType.EVENT,
eventEggTypeDescriptor: encounter.misc.trainerEggDescription,
eggDescriptor: encounter.misc.trainerEggDescription,
tier: EggTier.GREAT
};
encounter.setDialogueToken("eggType", i18next.t(`${namespace}.eggTypes.rare`));

View File

@ -23,6 +23,7 @@ import HeldModifierConfig from "#app/interfaces/held-modifier-config";
import { BerryType } from "#enums/berry-type";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:absoluteAvarice";
@ -35,7 +36,7 @@ const namespace = "mysteryEncounter:absoluteAvarice";
export const AbsoluteAvariceEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.ABSOLUTE_AVARICE)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new PersistentModifierRequirement("BerryModifier", 4)) // Must have at least 4 berries to spawn
.withIntroSpriteConfigs([
{

View File

@ -12,6 +12,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:offerYouCantRefuse";
@ -24,7 +25,7 @@ const namespace = "mysteryEncounter:offerYouCantRefuse";
export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party
.withIntroSpriteConfigs([
{

View File

@ -30,6 +30,7 @@ import i18next from "#app/plugins/i18n";
import { BerryType } from "#enums/berry-type";
import { PERMANENT_STATS, Stat } from "#enums/stat";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:berriesAbound";
@ -42,7 +43,7 @@ const namespace = "mysteryEncounter:berriesAbound";
export const BerriesAboundEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.BERRIES_ABOUND)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true)
.withHideWildIntroMessage(true)
.withIntroSpriteConfigs([]) // Set in onInit()

View File

@ -49,6 +49,7 @@ import i18next from "i18next";
import MoveInfoOverlay from "#app/ui/move-info-overlay";
import { allMoves } from "#app/data/move";
import { ModifierTier } from "#app/modifier/modifier-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:bugTypeSuperfan";
@ -191,7 +192,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
new AttackTypeBoosterHeldItemTypeRequirement(Type.BUG, 1),
new TypeRequirement(Type.BUG, false, 1)
))
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withIntroSpriteConfigs([]) // These are set in onInit()
.withAutoHideIntroVisuals(false)
.withIntroDialogue([

View File

@ -26,10 +26,11 @@ import { BerryModifier } from "#app/modifier/modifier";
import { BerryType } from "#enums/berry-type";
import { BattlerIndex } from "#app/battle";
import { Moves } from "#enums/moves";
import { EncounterAnim, EncounterBattleAnim } from "#app/data/battle-anims";
import { EncounterBattleAnim } from "#app/data/battle-anims";
import { MoveCategory } from "#app/data/move";
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
import { GameModes } from "#app/game-mode";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES, GameModes } from "#app/game-mode";
import { EncounterAnim } from "#enums/encounter-anims";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:clowningAround";
@ -61,7 +62,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.CLOWNING_AROUND)
.withEncounterTier(MysteryEncounterTier.ULTRA)
.withDisabledGameModes(GameModes.CHALLENGE)
.withSceneWaveRangeRequirement(80, 180)
.withSceneWaveRangeRequirement(80, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
.withAnimations(EncounterAnim.SMOKESCREEN)
.withAutoHideIntroVisuals(false)
.withIntroSpriteConfigs([

View File

@ -12,7 +12,7 @@ import { Moves } from "#enums/moves";
import { TrainerSlot } from "#app/data/trainer-config";
import PokemonData from "#app/system/pokemon-data";
import { Biome } from "#enums/biome";
import { EncounterAnim, EncounterBattleAnim } from "#app/data/battle-anims";
import { EncounterBattleAnim } from "#app/data/battle-anims";
import { BattlerTagType } from "#enums/battler-tag-type";
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
@ -25,6 +25,8 @@ import { modifierTypes } from "#app/modifier/modifier-type";
import { LearnMovePhase } from "#app/phases/learn-move-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat";
import { EncounterAnim } from "#enums/encounter-anims";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:dancingLessons";
@ -81,7 +83,7 @@ const SENSU_STYLE_BIOMES = [
export const DancingLessonsEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DANCING_LESSONS)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withIntroSpriteConfigs([]) // Uses a real Pokemon sprite instead of ME Intro Visuals
.withAnimations(EncounterAnim.DANCE)
.withHideWildIntroMessage(true)

View File

@ -13,6 +13,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** i18n namespace for encounter */
const namespace = "mysteryEncounter:darkDeal";
@ -100,7 +101,7 @@ export const DarkDealEncounter: MysteryEncounter =
text: `${namespace}.intro_dialogue`,
},
])
.withSceneWaveRangeRequirement(30, 180) // waves 30 to 180
.withSceneWaveRangeRequirement(30, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
.withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party
.withCatchAllowed(true)
.withTitle(`${namespace}.title`)

View File

@ -16,6 +16,7 @@ import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/u
import i18next from "#app/plugins/i18n";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:delibirdy";
@ -40,7 +41,7 @@ const OPTION_3_DISALLOWED_MODIFIERS = [
export const DelibirdyEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DELIBIRDY)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new MoneyRequirement(0, 2)) // Must have enough money for it to spawn at the very least
.withPrimaryPokemonRequirement(new CombinationPokemonRequirement( // Must also have either option 2 or 3 available to spawn
new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS),

View File

@ -11,6 +11,7 @@ import MysteryEncounter, {
MysteryEncounterBuilder,
} from "../mystery-encounter";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** i18n namespace for encounter */
const namespace = "mysteryEncounter:departmentStoreSale";
@ -23,7 +24,7 @@ const namespace = "mysteryEncounter:departmentStoreSale";
export const DepartmentStoreSaleEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(10, 100)
.withSceneWaveRangeRequirement(CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[0], 100)
.withIntroSpriteConfigs([
{
spriteKey: "b2w2_lady",

View File

@ -11,6 +11,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { Stat } from "#enums/stat";
import i18next from "i18next";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** i18n namespace for the encounter */
const namespace = "mysteryEncounter:fieldTrip";
@ -23,7 +24,7 @@ const namespace = "mysteryEncounter:fieldTrip";
export const FieldTripEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withIntroSpriteConfigs([
{
spriteKey: "preschooler_m",
@ -90,6 +91,7 @@ export const FieldTripEncounter: MysteryEncounter =
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.DEF])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!,
];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
@ -135,6 +137,7 @@ export const FieldTripEncounter: MysteryEncounter =
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPDEF])!,
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!,
];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
@ -180,6 +183,7 @@ export const FieldTripEncounter: MysteryEncounter =
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_STAGE_BOOSTER, [Stat.SPD])!,
generateModifierTypeOption(scene, modifierTypes.GREAT_BALL)!,
generateModifierTypeOption(scene, modifierTypes.IV_SCANNER)!,
generateModifierTypeOption(scene, modifierTypes.RARER_CANDY)!,
];
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });

View File

@ -12,7 +12,7 @@ import { Type } from "#app/data/type";
import { BattlerIndex } from "#app/battle";
import { PokemonMove } from "#app/field/pokemon";
import { Moves } from "#enums/moves";
import { EncounterAnim, EncounterBattleAnim } from "#app/data/battle-anims";
import { EncounterBattleAnim } from "#app/data/battle-anims";
import { WeatherType } from "#app/data/weather";
import { isNullOrUndefined, randSeedInt } from "#app/utils";
import { StatusEffect } from "#app/data/status-effect";
@ -20,6 +20,8 @@ import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encoun
import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { EncounterAnim } from "#enums/encounter-anims";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:fieryFallout";
@ -39,7 +41,7 @@ const DAMAGE_PERCENTAGE: number = 20;
export const FieryFalloutEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIERY_FALLOUT)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(40, 180)
.withSceneWaveRangeRequirement(40, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
.withCatchAllowed(true)
.withIntroSpriteConfigs([]) // Set in onInit()
.withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT)

View File

@ -28,6 +28,7 @@ import { BattlerTagType } from "#enums/battler-tag-type";
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { randSeedInt } from "#app/utils";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:fightOrFlight";
@ -40,7 +41,7 @@ const namespace = "mysteryEncounter:fightOrFlight";
export const FightOrFlightEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIGHT_OR_FLIGHT)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true)
.withHideWildIntroMessage(true)
.withIntroSpriteConfigs([]) // Set in onInit()

View File

@ -21,6 +21,7 @@ import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms";
import { PostSummonPhase } from "#app/phases/post-summon-phase";
import { modifierTypes } from "#app/modifier/modifier-type";
import { Nature } from "#enums/nature";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:funAndGames";
@ -33,7 +34,7 @@ const namespace = "mysteryEncounter:funAndGames";
export const FunAndGamesEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FUN_AND_GAMES)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new MoneyRequirement(0, 1.5)) // Cost equal to 1 Max Potion to play
.withAutoHideIntroVisuals(false)
// Allows using move without a visible enemy pokemon

View File

@ -22,6 +22,7 @@ import { getNatureName } from "#app/data/nature";
import { getPokeballAtlasKey, getPokeballTintColor, PokeballType } from "#app/data/pokeball";
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { trainerNamePools } from "#app/data/trainer-names";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:globalTradeSystem";
@ -70,7 +71,7 @@ const EXCLUDED_TRADE_SPECIES = [
export const GlobalTradeSystemEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.GLOBAL_TRADE_SYSTEM)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withAutoHideIntroVisuals(false)
.withIntroSpriteConfigs([
{

View File

@ -9,6 +9,7 @@ import { leaveEncounterWithoutBattle, setEncounterExp } from "../utils/encounter
import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
const OPTION_1_REQUIRED_MOVE = Moves.SURF;
const OPTION_2_REQUIRED_MOVE = Moves.FLY;
@ -28,7 +29,7 @@ const namespace = "mysteryEncounter:lostAtSea";
*/
export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.LOST_AT_SEA)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(11, 179)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withIntroSpriteConfigs([
{
spriteKey: "buoy",

View File

@ -17,6 +17,7 @@ import BattleScene from "#app/battle-scene";
import * as Utils 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";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:mysteriousChallengers";
@ -29,7 +30,7 @@ const namespace = "mysteryEncounter:mysteriousChallengers";
export const MysteriousChallengersEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHALLENGERS)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withIntroSpriteConfigs([]) // These are set in onInit()
.withIntroDialogue([
{

View File

@ -13,6 +13,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Species } from "#enums/species";
import { Moves } from "#enums/moves";
import { GameOverPhase } from "#app/phases/game-over-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** i18n namespace for encounter */
const namespace = "mysteryEncounter:mysteriousChest";
@ -31,7 +32,7 @@ const MASTER_REWARDS_WEIGHT = 65; // 5%
export const MysteriousChestEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHEST)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withAutoHideIntroVisuals(false)
.withCatchAllowed(true)
.withIntroSpriteConfigs([

View File

@ -11,6 +11,7 @@ import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requir
import { getEncounterText, showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import i18next from "i18next";
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:partTimer";
@ -23,7 +24,7 @@ const namespace = "mysteryEncounter:partTimer";
export const PartTimerEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.PART_TIMER)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withIntroSpriteConfigs([
{
spriteKey: "warehouse_crate",

View File

@ -18,6 +18,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { ScanIvsPhase } from "#app/phases/scan-ivs-phase";
import { SummonPhase } from "#app/phases/summon-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:safariZone";
@ -34,7 +35,7 @@ const SAFARI_MONEY_MULTIPLIER = 2.75;
export const SafariZoneEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SAFARI_ZONE)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new MoneyRequirement(0, SAFARI_MONEY_MULTIPLIER)) // Cost equal to 1 Max Revive
.withAutoHideIntroVisuals(false)
.withIntroSpriteConfigs([

View File

@ -14,6 +14,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { Nature } from "#enums/nature";
import { getNatureName } from "#app/data/nature";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:shadyVitaminDealer";
@ -26,7 +27,7 @@ const namespace = "mysteryEncounter:shadyVitaminDealer";
export const ShadyVitaminDealerEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SHADY_VITAMIN_DEALER)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new MoneyRequirement(0, 1.5)) // Must have the money for at least the cheap deal
.withPrimaryPokemonHealthRatioRequirement([0.5, 1]) // At least 1 Pokemon must have above half HP
.withIntroSpriteConfigs([

View File

@ -16,6 +16,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { PartyHealPhase } from "#app/phases/party-heal-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** i18n namespace for the encounter */
const namespace = "mysteryEncounter:slumberingSnorlax";
@ -28,7 +29,7 @@ const namespace = "mysteryEncounter:slumberingSnorlax";
export const SlumberingSnorlaxEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.SLUMBERING_SNORLAX)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true)
.withHideWildIntroMessage(true)
.withIntroSpriteConfigs([

View File

@ -19,6 +19,7 @@ import { BattlerTagType } from "#enums/battler-tag-type";
import { getPokemonNameWithAffix } from "#app/messages";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:teleportingHijinks";
@ -35,7 +36,7 @@ const MACHINE_INTERFACING_TYPES = [Type.ELECTRIC, Type.STEEL];
export const TeleportingHijinksEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.TELEPORTING_HIJINKS)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new WaveModulusRequirement([1, 2, 3], 10)) // Must be in first 3 waves after boss wave
.withSceneRequirement(new MoneyRequirement(undefined, MONEY_COST_MULTIPLIER)) // Must be able to pay teleport cost
.withAutoHideIntroVisuals(false)

View File

@ -14,6 +14,7 @@ import { showEncounterDialogue } from "#app/data/mystery-encounters/utils/encoun
import PokemonData from "#app/system/pokemon-data";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:pokemonSalesman";
@ -28,7 +29,7 @@ const MAX_POKEMON_PRICE_MULTIPLIER = 6;
export const ThePokemonSalesmanEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_POKEMON_SALESMAN)
.withEncounterTier(MysteryEncounterTier.ULTRA)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new MoneyRequirement(undefined, MAX_POKEMON_PRICE_MULTIPLIER)) // Some costs may not be as significant, this is the max you'd pay
.withAutoHideIntroVisuals(false)
.withIntroSpriteConfigs([

View File

@ -17,6 +17,7 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
import { Stat } from "#enums/stat";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:theStrongStuff";
@ -33,7 +34,7 @@ const BST_INCREASE_VALUE = 10;
export const TheStrongStuffEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_STRONG_STUFF)
.withEncounterTier(MysteryEncounterTier.GREAT)
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withScenePartySizeRequirement(3, 6) // Must have at least 3 pokemon in party
.withHideWildIntroMessage(true)
.withAutoHideIntroVisuals(false)

View File

@ -22,6 +22,7 @@ import { ShowTrainerPhase } from "#app/phases/show-trainer-phase";
import { ReturnPhase } from "#app/phases/return-phase";
import i18next from "i18next";
import { ModifierTier } from "#app/modifier/modifier-tier";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:theWinstrateChallenge";
@ -34,7 +35,7 @@ const namespace = "mysteryEncounter:theWinstrateChallenge";
export const TheWinstrateChallengeEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_WINSTRATE_CHALLENGE)
.withEncounterTier(MysteryEncounterTier.ROGUE)
.withSceneWaveRangeRequirement(100, 180)
.withSceneWaveRangeRequirement(100, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
.withIntroSpriteConfigs([
{
spriteKey: "vito",

View File

@ -19,6 +19,7 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
import i18next from "i18next";
import { getStatKey } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** The i18n namespace for the encounter */
const namespace = "mysteryEncounter:trainingSession";
@ -31,7 +32,7 @@ const namespace = "mysteryEncounter:trainingSession";
export const TrainingSessionEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.TRAINING_SESSION)
.withEncounterTier(MysteryEncounterTier.ULTRA)
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withScenePartySizeRequirement(2, 6, true) // Must have at least 2 unfainted pokemon in party
.withHideWildIntroMessage(true)
.withIntroSpriteConfigs([

View File

@ -17,6 +17,7 @@ import { Moves } from "#enums/moves";
import { BattlerIndex } from "#app/battle";
import { PokemonMove } from "#app/field/pokemon";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:trashToTreasure";
@ -31,7 +32,7 @@ const SOUND_EFFECT_WAIT_TIME = 700;
export const TrashToTreasureEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.TRASH_TO_TREASURE)
.withEncounterTier(MysteryEncounterTier.ULTRA)
.withSceneWaveRangeRequirement(60, 180)
.withSceneWaveRangeRequirement(60, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
.withMaxAllowedEncounters(1)
.withIntroSpriteConfigs([
{

View File

@ -23,6 +23,7 @@ import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encoun
import { BerryModifier } from "#app/modifier/modifier";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:uncommonBreed";
@ -35,7 +36,7 @@ const namespace = "mysteryEncounter:uncommonBreed";
export const UncommonBreedEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.UNCOMMON_BREED)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withCatchAllowed(true)
.withHideWildIntroMessage(true)
.withIntroSpriteConfigs([]) // Set in onInit()

View File

@ -20,7 +20,7 @@ import i18next from "#app/plugins/i18n";
import { doPokemonTransformationSequence, TransformationScreenPosition } from "#app/data/mystery-encounters/utils/encounter-transformation-sequence";
import { getLevelTotalExp } from "#app/data/exp";
import { Stat } from "#enums/stat";
import { GameModes } from "#app/game-mode";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES, GameModes } from "#app/game-mode";
/** i18n namespace for encounter */
const namespace = "mysteryEncounter:weirdDream";
@ -82,10 +82,16 @@ const SUPER_LEGENDARY_BST_THRESHOLD = 600;
const NON_LEGENDARY_BST_THRESHOLD = 570;
const GAIN_OLD_GATEAU_ITEM_BST_THRESHOLD = 450;
/** Value ranges of the resulting species BST transformations after adding values to original species */
const HIGH_BST_TRANSFORM_BASE_VALUES = [90, 110];
const STANDARD_BST_TRANSFORM_BASE_VALUES = [40, 50];
/**
* Value ranges of the resulting species BST transformations after adding values to original species
* 2 Pokemon in the party use this range
*/
const HIGH_BST_TRANSFORM_BASE_VALUES: [number, number] = [90, 110];
/**
* Value ranges of the resulting species BST transformations after adding values to original species
* All remaining Pokemon in the party use this range
*/
const STANDARD_BST_TRANSFORM_BASE_VALUES: [number, number] = [40, 50];
/**
* Weird Dream encounter.
@ -96,7 +102,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.WEIRD_DREAM)
.withEncounterTier(MysteryEncounterTier.ROGUE)
.withDisabledGameModes(GameModes.CHALLENGE)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withIntroSpriteConfigs([
{
spriteKey: "weird_dream_woman",
@ -252,7 +258,7 @@ function getTeamTransformations(scene: BattleScene): PokemonTransformation[] {
scene.removePokemonFromPlayerParty(removed, false);
const bst = removed.calculateBaseStats().reduce((a, b) => a + b, 0);
let newBstRange;
let newBstRange: [number, number];
if (i < 2) {
newBstRange = HIGH_BST_TRANSFORM_BASE_VALUES;
} else {

View File

@ -20,7 +20,8 @@ export class EncounterOptionsDialogue {
title?: string;
description?: string;
query?: string;
options?: [...OptionTextDisplay[]]; // Options array with minimum 2 options
/** Options array with minimum 2 options */
options?: [...OptionTextDisplay[]];
}
/**

View File

@ -2,6 +2,10 @@ import { Abilities } from "#enums/abilities";
import { Type } from "#app/data/type";
import { isNullOrUndefined } from "#app/utils";
/**
* Data that can customize a Pokemon in non-standard ways from its Species
* Currently only used by Mystery Encounters, may need to be renamed if it becomes more widely used
*/
export class MysteryEncounterPokemonData {
public spriteScale: number;
public ability: Abilities | -1;

View File

@ -53,7 +53,7 @@ export class CombinationSceneRequirement extends EncounterSceneRequirement {
return false;
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
for (const req of this.orRequirements) {
if (req.meetsRequirement(scene)) {
return req.getDialogueToken(scene, pokemon);
@ -99,7 +99,7 @@ export class CombinationPokemonRequirement extends EncounterPokemonRequirement {
this.orRequirements = orRequirements;
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
for (const req of this.orRequirements) {
if (req.meetsRequirement(scene)) {
return true;
@ -108,7 +108,7 @@ export class CombinationPokemonRequirement extends EncounterPokemonRequirement {
return false;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
for (const req of this.orRequirements) {
const result = req.queryParty(partyPokemon);
if (result?.length > 0) {
@ -119,7 +119,7 @@ export class CombinationPokemonRequirement extends EncounterPokemonRequirement {
return [];
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
for (const req of this.orRequirements) {
if (req.meetsRequirement(scene)) {
return req.getDialogueToken(scene, pokemon);
@ -142,11 +142,11 @@ export class PreviousEncounterRequirement extends EncounterSceneRequirement {
this.previousEncounterRequirement = previousEncounterRequirement;
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
return scene.mysteryEncounterSaveData.encounteredEvents.some(e => e.type === this.previousEncounterRequirement);
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["previousEncounter", scene.mysteryEncounterSaveData.encounteredEvents.find(e => e.type === this.previousEncounterRequirement)?.[0].toString() ?? ""];
}
}
@ -164,7 +164,7 @@ export class WaveRangeRequirement extends EncounterSceneRequirement {
this.waveRange = waveRange;
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
if (!isNullOrUndefined(this.waveRange) && this.waveRange?.[0] <= this.waveRange?.[1]) {
const waveIndex = scene.currentBattle.waveIndex;
if (waveIndex >= 0 && (this.waveRange?.[0] >= 0 && this.waveRange?.[0] > waveIndex) || (this.waveRange?.[1] >= 0 && this.waveRange?.[1] < waveIndex)) {
@ -174,7 +174,7 @@ export class WaveRangeRequirement extends EncounterSceneRequirement {
return true;
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["waveIndex", scene.currentBattle.waveIndex.toString()];
}
}
@ -199,11 +199,11 @@ export class WaveModulusRequirement extends EncounterSceneRequirement {
this.modulusValue = modulusValue;
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
return this.waveModuli.includes(scene.currentBattle.waveIndex % this.modulusValue);
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["waveIndex", scene.currentBattle.waveIndex.toString()];
}
}
@ -216,7 +216,7 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
this.requiredTimeOfDay = Array.isArray(timeOfDay) ? timeOfDay : [timeOfDay];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const timeOfDay = scene.arena?.getTimeOfDay();
if (!isNullOrUndefined(timeOfDay) && this.requiredTimeOfDay?.length > 0 && !this.requiredTimeOfDay.includes(timeOfDay)) {
return false;
@ -225,7 +225,7 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
return true;
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["timeOfDay", TimeOfDay[scene.arena.getTimeOfDay()].toLocaleLowerCase()];
}
}
@ -238,7 +238,7 @@ export class WeatherRequirement extends EncounterSceneRequirement {
this.requiredWeather = Array.isArray(weather) ? weather : [weather];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const currentWeather = scene.arena.weather?.weatherType;
if (!isNullOrUndefined(currentWeather) && this.requiredWeather?.length > 0 && !this.requiredWeather.includes(currentWeather!)) {
return false;
@ -247,7 +247,7 @@ export class WeatherRequirement extends EncounterSceneRequirement {
return true;
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const currentWeather = scene.arena.weather?.weatherType;
let token = "";
if (!isNullOrUndefined(currentWeather)) {
@ -273,7 +273,7 @@ export class PartySizeRequirement extends EncounterSceneRequirement {
this.excludeFainted = excludeFainted;
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
if (!isNullOrUndefined(this.partySizeRange) && this.partySizeRange?.[0] <= this.partySizeRange?.[1]) {
const partySize = this.excludeFainted ? scene.getParty().filter(p => p.isAllowedInBattle()).length : scene.getParty().length;
if (partySize >= 0 && (this.partySizeRange?.[0] >= 0 && this.partySizeRange?.[0] > partySize) || (this.partySizeRange?.[1] >= 0 && this.partySizeRange?.[1] < partySize)) {
@ -284,7 +284,7 @@ export class PartySizeRequirement extends EncounterSceneRequirement {
return true;
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["partySize", scene.getParty().length.toString()];
}
}
@ -299,7 +299,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const partyPokemon = scene.getParty();
if (isNullOrUndefined(partyPokemon) || this.requiredHeldItemModifiers?.length < 0) {
return false;
@ -317,7 +317,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
return modifierCount >= this.minNumberOfItems;
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["requiredItem", this.requiredHeldItemModifiers[0]];
}
}
@ -332,7 +332,7 @@ export class MoneyRequirement extends EncounterSceneRequirement {
this.scalingMultiplier = scalingMultiplier ?? 0;
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const money = scene.money;
if (isNullOrUndefined(money)) {
return false;
@ -344,7 +344,7 @@ export class MoneyRequirement extends EncounterSceneRequirement {
return !(this.requiredMoney > 0 && this.requiredMoney > money);
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const value = this.scalingMultiplier > 0 ? scene.getWaveMoneyAmount(this.scalingMultiplier).toString() : this.requiredMoney.toString();
return ["money", value];
}
@ -362,7 +362,7 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
this.requiredSpecies = Array.isArray(species) ? species : [species];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const partyPokemon = scene.getParty();
if (isNullOrUndefined(partyPokemon) || this.requiredSpecies?.length < 0) {
return false;
@ -370,7 +370,7 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => this.requiredSpecies.filter((species) => pokemon.species.speciesId === species).length > 0);
} else {
@ -379,7 +379,7 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
if (pokemon?.species.speciesId && this.requiredSpecies.includes(pokemon.species.speciesId)) {
return ["species", Species[pokemon.species.speciesId]];
}
@ -400,7 +400,7 @@ export class NatureRequirement extends EncounterPokemonRequirement {
this.requiredNature = Array.isArray(nature) ? nature : [nature];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const partyPokemon = scene.getParty();
if (isNullOrUndefined(partyPokemon) || this.requiredNature?.length < 0) {
return false;
@ -408,7 +408,7 @@ export class NatureRequirement extends EncounterPokemonRequirement {
return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => this.requiredNature.filter((nature) => pokemon.nature === nature).length > 0);
} else {
@ -417,7 +417,7 @@ export class NatureRequirement extends EncounterPokemonRequirement {
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
if (!isNullOrUndefined(pokemon?.nature) && this.requiredNature.includes(pokemon!.nature)) {
return ["nature", Nature[pokemon!.nature]];
}
@ -439,7 +439,7 @@ export class TypeRequirement extends EncounterPokemonRequirement {
this.requiredType = Array.isArray(type) ? type : [type];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
let partyPokemon = scene.getParty();
if (isNullOrUndefined(partyPokemon)) {
@ -453,7 +453,7 @@ export class TypeRequirement extends EncounterPokemonRequirement {
return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => this.requiredType.filter((type) => pokemon.getTypes().includes(type)).length > 0);
} else {
@ -462,7 +462,7 @@ export class TypeRequirement extends EncounterPokemonRequirement {
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const includedTypes = this.requiredType.filter((ty) => pokemon?.getTypes().includes(ty));
if (includedTypes.length > 0) {
return ["type", Type[includedTypes[0]]];
@ -484,7 +484,7 @@ export class MoveRequirement extends EncounterPokemonRequirement {
this.requiredMoves = Array.isArray(moves) ? moves : [moves];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const partyPokemon = scene.getParty();
if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) {
return false;
@ -492,7 +492,7 @@ export class MoveRequirement extends EncounterPokemonRequirement {
return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => this.requiredMoves.filter((reqMove) => pokemon.moveset.filter((move) => move?.moveId === reqMove).length > 0).length > 0);
} else {
@ -501,7 +501,7 @@ export class MoveRequirement extends EncounterPokemonRequirement {
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const includedMoves = pokemon?.moveset.filter((move) => move?.moveId && this.requiredMoves.includes(move.moveId));
if (includedMoves && includedMoves.length > 0 && includedMoves[0]) {
return ["move", includedMoves[0].getName()];
@ -528,7 +528,7 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
this.requiredMoves = Array.isArray(learnableMove) ? learnableMove : [learnableMove];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const partyPokemon = scene.getParty();
if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) {
return false;
@ -536,7 +536,7 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => this.requiredMoves.filter((learnableMove) => pokemon.compatibleTms.filter(tm => !pokemon.moveset.find(m => m?.moveId === tm)).includes(learnableMove)).length > 0);
} else {
@ -545,7 +545,7 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const includedCompatMoves = this.requiredMoves.filter((reqMove) => pokemon?.compatibleTms.filter((tm) => !pokemon.moveset.find(m => m?.moveId === tm)).includes(reqMove));
if (includedCompatMoves.length > 0) {
return ["compatibleMove", Moves[includedCompatMoves[0]]];
@ -568,7 +568,7 @@ export class EvolutionTargetSpeciesRequirement extends EncounterPokemonRequireme
this.requiredEvolutionTargetSpecies = Array.isArray(evolutionTargetSpecies) ? evolutionTargetSpecies : [evolutionTargetSpecies];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const partyPokemon = scene.getParty();
if (isNullOrUndefined(partyPokemon) || this.requiredEvolutionTargetSpecies?.length < 0) {
return false;
@ -576,7 +576,7 @@ export class EvolutionTargetSpeciesRequirement extends EncounterPokemonRequireme
return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => this.requiredEvolutionTargetSpecies.filter((evolutionTargetSpecies) => pokemon.getEvolution()?.speciesId === evolutionTargetSpecies).length > 0);
} else {
@ -607,7 +607,7 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
this.requiredAbilities = Array.isArray(abilities) ? abilities : [abilities];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const partyPokemon = scene.getParty();
if (isNullOrUndefined(partyPokemon) || this.requiredAbilities?.length < 0) {
return false;
@ -615,7 +615,7 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => this.requiredAbilities.some((ability) => pokemon.getAbility().id === ability));
} else {
@ -624,7 +624,7 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
if (pokemon?.getAbility().id && this.requiredAbilities.some(a => pokemon.getAbility().id === a)) {
return ["ability", pokemon.getAbility().name];
}
@ -644,7 +644,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
this.requiredStatusEffect = Array.isArray(statusEffect) ? statusEffect : [statusEffect];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const partyPokemon = scene.getParty();
if (isNullOrUndefined(partyPokemon) || this.requiredStatusEffect?.length < 0) {
return false;
@ -654,7 +654,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
return x;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => {
return this.requiredStatusEffect.some((statusEffect) => {
@ -682,7 +682,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const reqStatus = this.requiredStatusEffect.filter((a) => {
if (a === StatusEffect.NONE) {
return isNullOrUndefined(pokemon?.status) || isNullOrUndefined(pokemon!.status!.effect) || pokemon!.status!.effect === a;
@ -714,7 +714,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
this.requiredFormChangeItem = Array.isArray(formChangeItem) ? formChangeItem : [formChangeItem];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const partyPokemon = scene.getParty();
if (isNullOrUndefined(partyPokemon) || this.requiredFormChangeItem?.length < 0) {
return false;
@ -735,7 +735,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
}
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => this.requiredFormChangeItem.filter((formChangeItem) => this.filterByForm(pokemon, formChangeItem)).length > 0);
} else {
@ -744,7 +744,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const requiredItems = this.requiredFormChangeItem.filter((formChangeItem) => this.filterByForm(pokemon, formChangeItem));
if (requiredItems.length > 0) {
return ["formChangeItem", FormChangeItem[requiredItems[0]]];
@ -766,7 +766,7 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
this.requiredEvolutionItem = Array.isArray(evolutionItems) ? evolutionItems : [evolutionItems];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const partyPokemon = scene.getParty();
if (isNullOrUndefined(partyPokemon) || this.requiredEvolutionItem?.length < 0) {
return false;
@ -785,7 +785,7 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
return false;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => this.requiredEvolutionItem.filter((evolutionItem) => this.filterByEvo(pokemon, evolutionItem)).length > 0);
} else {
@ -794,7 +794,7 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const requiredItems = this.requiredEvolutionItem.filter((evoItem) => this.filterByEvo(pokemon, evoItem));
if (requiredItems.length > 0) {
return ["evolutionItem", EvolutionItem[requiredItems[0]]];
@ -815,7 +815,7 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const partyPokemon = scene.getParty();
if (isNullOrUndefined(partyPokemon)) {
return false;
@ -823,7 +823,7 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => this.requiredHeldItemModifiers.some((heldItem) => {
return pokemon.getHeldItems().some((it) => {
@ -839,7 +839,7 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const requiredItems = pokemon?.getHeldItems().filter((it) => {
return this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem);
});
@ -862,7 +862,7 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
this.requiredHeldItemTypes = Array.isArray(heldItemTypes) ? heldItemTypes : [heldItemTypes];
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
const partyPokemon = scene.getParty();
if (isNullOrUndefined(partyPokemon)) {
return false;
@ -870,7 +870,7 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
return this.queryParty(partyPokemon).length >= this.minNumberOfPokemon;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => this.requiredHeldItemTypes.some((heldItemType) => {
return pokemon.getHeldItems().some((it) => {
@ -886,7 +886,7 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const requiredItems = pokemon?.getHeldItems().filter((it) => {
return this.requiredHeldItemTypes.some(heldItemType => it instanceof AttackTypeBoosterModifier && (it.type as AttackTypeBoosterModifierType).moveType === heldItemType);
});
@ -909,7 +909,7 @@ export class LevelRequirement extends EncounterPokemonRequirement {
this.requiredLevelRange = requiredLevelRange;
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
// Party Pokemon inside required level range
if (!isNullOrUndefined(this.requiredLevelRange) && this.requiredLevelRange[0] <= this.requiredLevelRange[1]) {
const partyPokemon = scene.getParty();
@ -921,7 +921,7 @@ export class LevelRequirement extends EncounterPokemonRequirement {
return true;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => pokemon.level >= this.requiredLevelRange[0] && pokemon.level <= this.requiredLevelRange[1]);
} else {
@ -930,7 +930,7 @@ export class LevelRequirement extends EncounterPokemonRequirement {
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["level", pokemon?.level.toString() ?? ""];
}
}
@ -947,7 +947,7 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
this.requiredFriendshipRange = requiredFriendshipRange;
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
// Party Pokemon inside required friendship range
if (!isNullOrUndefined(this.requiredFriendshipRange) && this.requiredFriendshipRange[0] <= this.requiredFriendshipRange[1]) {
const partyPokemon = scene.getParty();
@ -959,7 +959,7 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
return true;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => pokemon.friendship >= this.requiredFriendshipRange[0] && pokemon.friendship <= this.requiredFriendshipRange[1]);
} else {
@ -968,7 +968,7 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["friendship", pokemon?.friendship.toString() ?? ""];
}
}
@ -990,7 +990,7 @@ export class HealthRatioRequirement extends EncounterPokemonRequirement {
this.requiredHealthRange = requiredHealthRange;
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
// Party Pokemon inside required level range
if (!isNullOrUndefined(this.requiredHealthRange) && this.requiredHealthRange[0] <= this.requiredHealthRange[1]) {
const partyPokemon = scene.getParty();
@ -1002,7 +1002,7 @@ export class HealthRatioRequirement extends EncounterPokemonRequirement {
return true;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => {
return pokemon.getHpRatio() >= this.requiredHealthRange[0] && pokemon.getHpRatio() <= this.requiredHealthRange[1];
@ -1013,7 +1013,7 @@ export class HealthRatioRequirement extends EncounterPokemonRequirement {
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
if (!isNullOrUndefined(pokemon?.getHpRatio())) {
return ["healthRatio", Math.floor(pokemon!.getHpRatio() * 100).toString() + "%"];
}
@ -1033,7 +1033,7 @@ export class WeightRequirement extends EncounterPokemonRequirement {
this.requiredWeightRange = requiredWeightRange;
}
meetsRequirement(scene: BattleScene): boolean {
override meetsRequirement(scene: BattleScene): boolean {
// Party Pokemon inside required friendship range
if (!isNullOrUndefined(this.requiredWeightRange) && this.requiredWeightRange[0] <= this.requiredWeightRange[1]) {
const partyPokemon = scene.getParty();
@ -1045,7 +1045,7 @@ export class WeightRequirement extends EncounterPokemonRequirement {
return true;
}
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
override queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
if (!this.invertQuery) {
return partyPokemon.filter((pokemon) => pokemon.getWeight() >= this.requiredWeightRange[0] && pokemon.getWeight() <= this.requiredWeightRange[1]);
} else {
@ -1054,7 +1054,7 @@ export class WeightRequirement extends EncounterPokemonRequirement {
}
}
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["weight", pokemon?.getWeight().toString() ?? ""];
}
}

View File

@ -10,11 +10,11 @@ import MysteryEncounterDialogue, { OptionTextDisplay } from "./mystery-encounter
import MysteryEncounterOption, { MysteryEncounterOptionBuilder, OptionPhaseCallback } from "./mystery-encounter-option";
import { EncounterPokemonRequirement, EncounterSceneRequirement, HealthRatioRequirement, PartySizeRequirement, StatusEffectRequirement, WaveRangeRequirement } from "./mystery-encounter-requirements";
import { BattlerIndex } from "#app/battle";
import { EncounterAnim } from "#app/data/battle-anims";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { GameModes } from "#app/game-mode";
import { EncounterAnim } from "#enums/encounter-anims";
export interface EncounterStartOfBattleEffect {
sourcePokemon?: Pokemon;
@ -72,15 +72,14 @@ export interface IMysteryEncounter {
* Unless you know what you're doing, you should use MysteryEncounterBuilder to create an instance for this class
*/
export default class MysteryEncounter implements IMysteryEncounter {
/**
* Required params
*/
// #region Required params
encounterType: MysteryEncounterType;
options: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]];
spriteConfigs: MysteryEncounterSpriteConfig[];
/**
* Optional params
*/
// #region Optional params
encounterTier: MysteryEncounterTier;
/**
* Custom battle animations that are configured for encounter effects and visuals
@ -137,9 +136,8 @@ export default class MysteryEncounter implements IMysteryEncounter {
*/
skipToFightInput: boolean;
/**
* Event callback functions
*/
// #region Event callback functions
/** Event when Encounter is first loaded, use it for data conditioning */
onInit?: (scene: BattleScene) => boolean;
/** Event when battlefield visuals have finished sliding in and the encounter dialogue begins */
@ -171,9 +169,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
primaryPokemon?: PlayerPokemon;
secondaryPokemon?: PlayerPokemon[];
/**
* Post-construct / Auto-populated params
*/
// #region Post-construct / Auto-populated params
/**
* Dialogue object containing all the dialogue, messages, tooltips, etc. for an encounter
@ -192,9 +188,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
*/
introVisuals?: MysteryEncounterIntroVisuals;
/**
* Flags
*/
// #region Flags
/**
* Can be set for uses programatic dialogue during an encounter (storing the name of one of the party's pokemon, etc.)

View File

@ -1,6 +1,9 @@
import { Moves } from "#enums/moves";
import { Abilities } from "#enums/abilities";
/**
* Moves that "steal" things
*/
export const STEALING_MOVES = [
Moves.PLUCK,
Moves.COVET,
@ -10,6 +13,9 @@ export const STEALING_MOVES = [
Moves.SWITCHEROO
];
/**
* Moves that "charm" someone
*/
export const CHARMING_MOVES = [
Moves.CHARM,
Moves.FLATTER,
@ -39,6 +45,9 @@ export const DANCING_MOVES = [
Moves.VICTORY_DANCE
];
/**
* Moves that can distract someone/something
*/
export const DISTRACTION_MOVES = [
Moves.FAKE_OUT,
Moves.FOLLOW_ME,
@ -54,6 +63,9 @@ export const DISTRACTION_MOVES = [
Moves.SHED_TAIL
];
/**
* Moves that protect in some way
*/
export const PROTECTING_MOVES = [
Moves.PROTECT,
Moves.WIDE_GUARD,
@ -70,6 +82,9 @@ export const PROTECTING_MOVES = [
Moves.DETECT
];
/**
* Moves that (loosely) can be used to trap/rob someone
*/
export const EXTORTION_MOVES = [
Moves.BIND,
Moves.CLAMP,
@ -93,6 +108,9 @@ export const EXTORTION_MOVES = [
Moves.STRING_SHOT,
];
/**
* Abilities that (loosely) can be used to trap/rob someone
*/
export const EXTORTION_ABILITIES = [
Abilities.INTIMIDATE,
Abilities.ARENA_TRAP,

View File

@ -88,13 +88,18 @@ export interface EnemyPokemonConfig {
}
export interface EnemyPartyConfig {
levelAdditiveMultiplier?: number; // Formula for enemy: level += waveIndex / 10 * levelAdditive
/** Formula for enemy: level += waveIndex / 10 * levelAdditive */
levelAdditiveMultiplier?: number;
doubleBattle?: boolean;
trainerType?: TrainerType; // Generates trainer battle solely off trainer type
trainerConfig?: TrainerConfig; // More customizable option for configuring trainer battle
/** Generates trainer battle solely off trainer type */
trainerType?: TrainerType;
/** More customizable option for configuring trainer battle */
trainerConfig?: TrainerConfig;
pokemonConfigs?: EnemyPokemonConfig[];
female?: boolean; // True for female trainer, false for male
disableSwitch?: boolean; // True will prevent player from switching
/** True for female trainer, false for male */
female?: boolean;
/** True will prevent player from switching */
disableSwitch?: boolean;
}
/**

View File

@ -0,0 +1,11 @@
/**
* Animations used for Mystery Encounters
* These are custom animations that may or may not work in any other circumstance
* Use at your own risk
*/
export enum EncounterAnim {
MAGMA_BG,
MAGMA_SPOUT,
SMOKESCREEN,
DANCE
}

View File

@ -33,8 +33,8 @@ interface GameModeConfig {
}
// Describes min and max waves for MEs in specific game modes
const CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES: [number, number] = [10, 180];
const CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES: [number, number] = [10, 180];
export const CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES: [number, number] = [10, 180];
export const CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES: [number, number] = [10, 180];
export class GameMode implements GameModeConfig {
public modeId: GameModes;
@ -325,6 +325,9 @@ export class GameMode implements GameModeConfig {
}
}
/**
* Returns the wave range where MEs can spawn for the game mode [min, max]
*/
getMysteryEncounterLegalWaves(): [number, number] {
switch (this.modeId) {
default:

View File

@ -174,6 +174,13 @@ export class MysteryEncounterOptionSelectedPhase extends Phase {
this.onOptionSelect = this.scene.currentBattle.mysteryEncounter!.selectedOption!.onOptionPhase;
}
/**
* Will handle (in order):
* - Execute onOptionSelect() logic if it exists for the selected option
*
* It is important to point out that no phases are directly queued by any logic within this phase
* Any phase that is meant to follow this one MUST be queued via the onOptionSelect() logic of the selected option
*/
start() {
super.start();
if (this.scene.currentBattle.mysteryEncounter?.autoHideIntroVisuals) {

View File

@ -18,6 +18,9 @@ export class PartyExpPhase extends Phase {
this.pokemonParticipantIds = pokemonParticipantIds;
}
/**
* Gives EXP to the party
*/
start() {
super.start();

View File

@ -57,6 +57,7 @@ export default class PokemonData {
public bossSegments?: integer;
public summonData: PokemonSummonData;
/** Data that can customize a Pokemon in non-standard ways from its Species */
public mysteryEncounterPokemonData: MysteryEncounterPokemonData;
constructor(source: Pokemon | any, forHistory: boolean = false) {

View File

@ -117,11 +117,12 @@ describe("Field Trip - Mystery Encounter", () => {
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
expect(modifierSelectHandler.options.length).toEqual(4);
expect(modifierSelectHandler.options.length).toEqual(5);
expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe("X Attack");
expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe("X Defense");
expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe("X Speed");
expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe("Dire Hit");
expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe("Rarer Candy");
});
it("should leave encounter without battle", async () => {
@ -163,11 +164,12 @@ describe("Field Trip - Mystery Encounter", () => {
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
expect(modifierSelectHandler.options.length).toEqual(4);
expect(modifierSelectHandler.options.length).toEqual(5);
expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe("X Sp. Atk");
expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe("X Sp. Def");
expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe("X Speed");
expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe("Dire Hit");
expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe("Dire Hit");
});
it("should leave encounter without battle", async () => {
@ -209,11 +211,12 @@ describe("Field Trip - Mystery Encounter", () => {
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
expect(modifierSelectHandler.options.length).toEqual(4);
expect(modifierSelectHandler.options.length).toEqual(5);
expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe("X Accuracy");
expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe("X Speed");
expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe("5x Great Ball");
expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe("IV Scanner");
expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe("Rarer Candy");
});
it("should leave encounter without battle", async () => {