commit latest beta merge updates
This commit is contained in:
parent
8899fce571
commit
0b698a04a2
|
@ -111,6 +111,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
||||||
new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER));
|
new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER));
|
||||||
clownConfig.setPartyTemplates(clownPartyTemplate);
|
clownConfig.setPartyTemplates(clownPartyTemplate);
|
||||||
clownConfig.setDoubleOnly();
|
clownConfig.setDoubleOnly();
|
||||||
|
// @ts-ignore
|
||||||
clownConfig.partyTemplateFunc = null; // Overrides party template func if it exists
|
clownConfig.partyTemplateFunc = null; // Overrides party template func if it exists
|
||||||
|
|
||||||
// Generate random ability for Blacephalon from pool
|
// Generate random ability for Blacephalon from pool
|
||||||
|
@ -415,7 +416,7 @@ function onYesAbilitySwap(scene: BattleScene, resolve) {
|
||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
// Do ability swap
|
// Do ability swap
|
||||||
if (!pokemon.mysteryEncounterData) {
|
if (!pokemon.mysteryEncounterData) {
|
||||||
pokemon.mysteryEncounterData = new MysteryEncounterPokemonData(null, Abilities.AERILATE);
|
pokemon.mysteryEncounterData = new MysteryEncounterPokemonData(undefined, Abilities.AERILATE);
|
||||||
}
|
}
|
||||||
pokemon.mysteryEncounterData.ability = scene.currentBattle.mysteryEncounter.misc.ability;
|
pokemon.mysteryEncounterData.ability = scene.currentBattle.mysteryEncounter.misc.ability;
|
||||||
scene.currentBattle.mysteryEncounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender());
|
scene.currentBattle.mysteryEncounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender());
|
||||||
|
|
|
@ -54,8 +54,8 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||||
.withOption(
|
.withOption(
|
||||||
// Option 1: Use a (non fainted) pokemon that can learn Surf to guide you back/
|
// Option 1: Use a (non fainted) pokemon that can learn Surf to guide you back/
|
||||||
MysteryEncounterOptionBuilder
|
MysteryEncounterOptionBuilder
|
||||||
.withPokemonCanLearnMoveRequirement(OPTION_1_REQUIRED_MOVE)
|
|
||||||
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withPokemonCanLearnMoveRequirement(OPTION_1_REQUIRED_MOVE)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}.option.1.label`,
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
disabledButtonLabel: `${namespace}.option.1.label_disabled`,
|
disabledButtonLabel: `${namespace}.option.1.label_disabled`,
|
||||||
|
@ -73,8 +73,8 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
||||||
.withOption(
|
.withOption(
|
||||||
//Option 2: Use a (non fainted) pokemon that can learn fly to guide you back.
|
//Option 2: Use a (non fainted) pokemon that can learn fly to guide you back.
|
||||||
MysteryEncounterOptionBuilder
|
MysteryEncounterOptionBuilder
|
||||||
.withPokemonCanLearnMoveRequirement(OPTION_2_REQUIRED_MOVE)
|
|
||||||
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withPokemonCanLearnMoveRequirement(OPTION_2_REQUIRED_MOVE)
|
||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}.option.2.label`,
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
disabledButtonLabel: `${namespace}.option.2.label_disabled`,
|
disabledButtonLabel: `${namespace}.option.2.label_disabled`,
|
||||||
|
@ -131,7 +131,7 @@ async function handlePokemonGuidingYouPhase(scene: BattleScene) {
|
||||||
const laprasSpecies = getPokemonSpecies(Species.LAPRAS);
|
const laprasSpecies = getPokemonSpecies(Species.LAPRAS);
|
||||||
const { mysteryEncounter } = scene.currentBattle;
|
const { mysteryEncounter } = scene.currentBattle;
|
||||||
|
|
||||||
if (mysteryEncounter.selectedOption) {
|
if (mysteryEncounter.selectedOption?.primaryPokemon?.id) {
|
||||||
setEncounterExp(scene, mysteryEncounter.selectedOption.primaryPokemon.id, laprasSpecies.baseExp, true);
|
setEncounterExp(scene, mysteryEncounter.selectedOption.primaryPokemon.id, laprasSpecies.baseExp, true);
|
||||||
} else {
|
} else {
|
||||||
console.warn("Lost at sea: No guide pokemon found but pokemon guides player. huh!?");
|
console.warn("Lost at sea: No guide pokemon found but pokemon guides player. huh!?");
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequiremen
|
||||||
import { CanLearnMoveRequirement, CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement";
|
import { CanLearnMoveRequirement, CanLearnMoveRequirementOptions } from "./requirements/can-learn-move-requirement";
|
||||||
import { isNullOrUndefined } from "#app/utils";
|
import { isNullOrUndefined } from "#app/utils";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
|
|
||||||
|
|
||||||
export type OptionPhaseCallback = (scene: BattleScene) => Promise<void | boolean>;
|
export type OptionPhaseCallback = (scene: BattleScene) => Promise<void | boolean>;
|
||||||
|
@ -39,7 +40,7 @@ export default class MysteryEncounterOption {
|
||||||
/** Executes after the encounter is over. Usually this will be for calculating dialogueTokens or performing data updates */
|
/** Executes after the encounter is over. Usually this will be for calculating dialogueTokens or performing data updates */
|
||||||
onPostOptionPhase?: OptionPhaseCallback;
|
onPostOptionPhase?: OptionPhaseCallback;
|
||||||
|
|
||||||
constructor(option: MysteryEncounterOption) {
|
constructor(option: MysteryEncounterOption | null) {
|
||||||
Object.assign(this, option);
|
Object.assign(this, option);
|
||||||
this.hasDexProgress = !isNullOrUndefined(this.hasDexProgress) ? this.hasDexProgress : false;
|
this.hasDexProgress = !isNullOrUndefined(this.hasDexProgress) ? this.hasDexProgress : false;
|
||||||
this.requirements = this.requirements ? this.requirements : [];
|
this.requirements = this.requirements ? this.requirements : [];
|
||||||
|
@ -137,41 +138,27 @@ export default class MysteryEncounterOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseOption = {
|
|
||||||
optionMode: MysteryEncounterOptionMode.DEFAULT,
|
|
||||||
hasDexProgress: false,
|
|
||||||
requirements: [],
|
|
||||||
primaryPokemonRequirements: [],
|
|
||||||
secondaryPokemonRequirements: [],
|
|
||||||
excludePrimaryFromSecondaryRequirements: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Picks non-optional fields from an "object" type alias and returns a union of literal types (keys) */
|
|
||||||
type PickNonOptionalFieldsKeys<T> = Exclude<{ [K in Keys<T>]: T extends Record<K, T[K]> ? K : never; }[Keys<T>], undefined>;
|
|
||||||
// type NonFunctionFieldsKeys<T> = { [K in Keys<T>]: T[K] extends Function ? never : K; }[Keys<T>];
|
|
||||||
// type FunctionPropertyKeys<T> = { [K in Keys<T>]: T[K] extends Function ? K : never; }[Keys<T>];
|
|
||||||
/* Extracts keys from T */
|
|
||||||
type Keys<T> = keyof T;
|
|
||||||
/* Filters out all optional fields from an "object" type alias, and all function fields */
|
|
||||||
type PickNonOptionalFields<T> = Pick<T, PickNonOptionalFieldsKeys<T>>;
|
|
||||||
/* Omits keys that exist in the base object and optional keys on MysteryEncounter, as well as functions on MysteryEncounter */
|
|
||||||
type OmitBaseAndOptionalKeys<T, B> = Omit<PickNonOptionalFields<T>, Keys<B>>;
|
|
||||||
|
|
||||||
export class MysteryEncounterOptionBuilder implements Partial<MysteryEncounterOption> {
|
export class MysteryEncounterOptionBuilder implements Partial<MysteryEncounterOption> {
|
||||||
optionMode: MysteryEncounterOptionMode;
|
optionMode: MysteryEncounterOptionMode = MysteryEncounterOptionMode.DEFAULT;
|
||||||
requirements: EncounterSceneRequirement[] = [];
|
requirements: EncounterSceneRequirement[] = [];
|
||||||
primaryPokemonRequirements: EncounterPokemonRequirement[] = [];
|
primaryPokemonRequirements: EncounterPokemonRequirement[] = [];
|
||||||
secondaryPokemonRequirements: EncounterPokemonRequirement[] = [];
|
secondaryPokemonRequirements: EncounterPokemonRequirement[] = [];
|
||||||
excludePrimaryFromSecondaryRequirements: boolean;
|
excludePrimaryFromSecondaryRequirements: boolean = false;
|
||||||
isDisabledOnRequirementsNotMet: boolean;
|
isDisabledOnRequirementsNotMet: boolean = true;
|
||||||
hasDexProgress: boolean;
|
hasDexProgress: boolean = false;
|
||||||
onPreOptionPhase?: OptionPhaseCallback;
|
onPreOptionPhase?: OptionPhaseCallback;
|
||||||
onOptionPhase: OptionPhaseCallback;
|
onOptionPhase: OptionPhaseCallback;
|
||||||
onPostOptionPhase?: OptionPhaseCallback;
|
onPostOptionPhase?: OptionPhaseCallback;
|
||||||
dialogue: OptionTextDisplay;
|
dialogue: OptionTextDisplay;
|
||||||
|
|
||||||
static newOptionWithMode(optionMode: MysteryEncounterOptionMode): MysteryEncounterOptionBuilder & OmitBaseAndOptionalKeys<MysteryEncounterOption, typeof baseOption> & Pick<MysteryEncounterOption, "optionMode"> {
|
hasRequirements = MysteryEncounter.prototype["hasRequirements"];
|
||||||
return Object.assign(new MysteryEncounterOptionBuilder(), { ...baseOption, optionMode });
|
meetsRequirements = MysteryEncounter.prototype["meetsRequirements"];
|
||||||
|
pokemonMeetsPrimaryRequirements = MysteryEncounter.prototype["pokemonMeetsPrimaryRequirements"];
|
||||||
|
meetsPrimaryRequirementAndPrimaryPokemonSelected = MysteryEncounter.prototype["meetsPrimaryRequirementAndPrimaryPokemonSelected"];
|
||||||
|
meetsSupportingRequirementAndSupportingPokemonSelected = MysteryEncounter.prototype["meetsSupportingRequirementAndSupportingPokemonSelected"];
|
||||||
|
|
||||||
|
static newOptionWithMode(optionMode: MysteryEncounterOptionMode): MysteryEncounterOptionBuilder & Pick<MysteryEncounterOption, "optionMode"> {
|
||||||
|
return Object.assign(new MysteryEncounterOptionBuilder(), { optionMode });
|
||||||
}
|
}
|
||||||
|
|
||||||
withHasDexProgress(hasDexProgress: boolean): this & Required<Pick<MysteryEncounterOption, "hasDexProgress">> {
|
withHasDexProgress(hasDexProgress: boolean): this & Required<Pick<MysteryEncounterOption, "hasDexProgress">> {
|
||||||
|
|
|
@ -2,15 +2,15 @@ import { Abilities } from "#enums/abilities";
|
||||||
import { Type } from "#app/data/type";
|
import { Type } from "#app/data/type";
|
||||||
|
|
||||||
export class MysteryEncounterPokemonData {
|
export class MysteryEncounterPokemonData {
|
||||||
public spriteScale: number;
|
public spriteScale: number | undefined;
|
||||||
public ability: Abilities;
|
public ability: Abilities | undefined;
|
||||||
public passive: Abilities;
|
public passive: Abilities | undefined;
|
||||||
public types: Type[] = [];
|
public types: Type[];
|
||||||
|
|
||||||
constructor(spriteScale?: number, ability?: Abilities, passive?: Abilities, types?: Type[]) {
|
constructor(spriteScale?: number, ability?: Abilities, passive?: Abilities, types?: Type[]) {
|
||||||
this.spriteScale = spriteScale;
|
this.spriteScale = spriteScale;
|
||||||
this.ability = ability;
|
this.ability = ability;
|
||||||
this.passive = passive;
|
this.passive = passive;
|
||||||
this.types = types;
|
this.types = types ?? [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ export class CombinationSceneRequirement extends EncounterSceneRequirement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return this.orRequirements[0].getDialogueToken(scene, pokemon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ export class CombinationPokemonRequirement extends EncounterPokemonRequirement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return this.orRequirements[0].getDialogueToken(scene, pokemon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ export class PreviousEncounterRequirement extends EncounterSceneRequirement {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||||
return ["previousEncounter", scene.mysteryEncounterData.encounteredEvents.find(e => e[0] === this.previousEncounterRequirement)[0].toString()];
|
return ["previousEncounter", scene.mysteryEncounterData.encounteredEvents.find(e => e[0] === this.previousEncounterRequirement)?.[0].toString() ?? ""];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ export class WaveRangeRequirement extends EncounterSceneRequirement {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TimeOfDayRequirement extends EncounterSceneRequirement {
|
export class TimeOfDayRequirement extends EncounterSceneRequirement {
|
||||||
requiredTimeOfDay?: TimeOfDay[];
|
requiredTimeOfDay: TimeOfDay[];
|
||||||
|
|
||||||
constructor(timeOfDay: TimeOfDay | TimeOfDay[]) {
|
constructor(timeOfDay: TimeOfDay | TimeOfDay[]) {
|
||||||
super();
|
super();
|
||||||
|
@ -180,7 +180,7 @@ export class TimeOfDayRequirement extends EncounterSceneRequirement {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WeatherRequirement extends EncounterSceneRequirement {
|
export class WeatherRequirement extends EncounterSceneRequirement {
|
||||||
requiredWeather?: WeatherType[];
|
requiredWeather: WeatherType[];
|
||||||
|
|
||||||
constructor(weather: WeatherType | WeatherType[]) {
|
constructor(weather: WeatherType | WeatherType[]) {
|
||||||
super();
|
super();
|
||||||
|
@ -188,8 +188,8 @@ export class WeatherRequirement extends EncounterSceneRequirement {
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirement(scene: BattleScene): boolean {
|
meetsRequirement(scene: BattleScene): boolean {
|
||||||
const currentWeather = scene.arena?.weather?.weatherType;
|
const currentWeather = scene.arena.weather?.weatherType;
|
||||||
if (!isNullOrUndefined(currentWeather) && this?.requiredWeather?.length > 0 && !this.requiredWeather.includes(currentWeather)) {
|
if (!isNullOrUndefined(currentWeather) && this?.requiredWeather?.length > 0 && !this.requiredWeather.includes(currentWeather!)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +197,12 @@ export class WeatherRequirement extends EncounterSceneRequirement {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||||
return ["weather", WeatherType[scene.arena?.weather?.weatherType].replace("_", " ").toLocaleLowerCase()];
|
const currentWeather = scene.arena.weather?.weatherType;
|
||||||
|
let token = "";
|
||||||
|
if (!isNullOrUndefined(currentWeather)) {
|
||||||
|
token = WeatherType[currentWeather!].replace("_", " ").toLocaleLowerCase();
|
||||||
|
}
|
||||||
|
return ["weather", token];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,10 +267,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||||
if (this.requiredHeldItemModifiers.length > 0) {
|
return ["requiredItem", this.requiredHeldItemModifiers[0]];
|
||||||
return ["requiredItem", this.requiredHeldItemModifiers[0]];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,10 +329,10 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||||
if (this.requiredSpecies.includes(pokemon.species.speciesId)) {
|
if (pokemon?.species.speciesId && this.requiredSpecies.includes(pokemon.species.speciesId)) {
|
||||||
return ["species", Species[pokemon.species.speciesId]];
|
return ["species", Species[pokemon.species.speciesId]];
|
||||||
}
|
}
|
||||||
return null;
|
return ["species", ""];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,11 +412,11 @@ export class TypeRequirement extends EncounterPokemonRequirement {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||||
const includedTypes = this.requiredType.filter((ty) => pokemon.getTypes().includes(ty));
|
const includedTypes = this.requiredType.filter((ty) => pokemon?.getTypes().includes(ty));
|
||||||
if (includedTypes.length > 0) {
|
if (includedTypes.length > 0) {
|
||||||
return ["type", Type[includedTypes[0]]];
|
return ["type", Type[includedTypes[0]]];
|
||||||
}
|
}
|
||||||
return null;
|
return ["type", ""];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,19 +443,19 @@ export class MoveRequirement extends EncounterPokemonRequirement {
|
||||||
|
|
||||||
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
|
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
|
||||||
if (!this.invertQuery) {
|
if (!this.invertQuery) {
|
||||||
return partyPokemon.filter((pokemon) => this.requiredMoves.filter((reqMove) => pokemon.moveset.filter((move) => move.moveId === reqMove).length > 0).length > 0);
|
return partyPokemon.filter((pokemon) => this.requiredMoves.filter((reqMove) => pokemon.moveset.filter((move) => move?.moveId === reqMove).length > 0).length > 0);
|
||||||
} else {
|
} else {
|
||||||
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed moves
|
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed moves
|
||||||
return partyPokemon.filter((pokemon) => this.requiredMoves.filter((reqMove) => pokemon.moveset.filter((move) => move.moveId === reqMove).length === 0).length === 0);
|
return partyPokemon.filter((pokemon) => this.requiredMoves.filter((reqMove) => pokemon.moveset.filter((move) => move?.moveId === reqMove).length === 0).length === 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||||
const includedMoves = pokemon.moveset.filter((move) => this.requiredMoves.includes(move.moveId));
|
const includedMoves = pokemon?.moveset.filter((move) => move?.moveId && this.requiredMoves.includes(move.moveId));
|
||||||
if (includedMoves.length > 0) {
|
if (includedMoves && includedMoves.length > 0 && includedMoves[0]) {
|
||||||
return ["move", includedMoves[0].getName()];
|
return ["move", includedMoves[0].getName()];
|
||||||
}
|
}
|
||||||
return null;
|
return ["move", ""];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -485,19 +487,19 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
|
||||||
|
|
||||||
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
|
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
|
||||||
if (!this.invertQuery) {
|
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);
|
return partyPokemon.filter((pokemon) => this.requiredMoves.filter((learnableMove) => pokemon.compatibleTms.filter(tm => !pokemon.moveset.find(m => m?.moveId === tm)).includes(learnableMove)).length > 0);
|
||||||
} else {
|
} else {
|
||||||
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed learnableMoves
|
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed learnableMoves
|
||||||
return partyPokemon.filter((pokemon) => this.requiredMoves.filter((learnableMove) => pokemon.compatibleTms.filter(tm => !pokemon.moveset.find(m => m.moveId === tm)).includes(learnableMove)).length === 0);
|
return partyPokemon.filter((pokemon) => this.requiredMoves.filter((learnableMove) => pokemon.compatibleTms.filter(tm => !pokemon.moveset.find(m => m?.moveId === tm)).includes(learnableMove)).length === 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
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));
|
const includedCompatMoves = this.requiredMoves.filter((reqMove) => pokemon?.compatibleTms.filter((tm) => !pokemon.moveset.find(m => m?.moveId === tm)).includes(reqMove));
|
||||||
if (includedCompatMoves.length > 0) {
|
if (includedCompatMoves.length > 0) {
|
||||||
return ["compatibleMove", Moves[includedCompatMoves[0]]];
|
return ["compatibleMove", Moves[includedCompatMoves[0]]];
|
||||||
}
|
}
|
||||||
return null;
|
return ["compatibleMove", ""];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -572,10 +574,10 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||||
if (this.requiredAbilities.some(a => pokemon.getAbility().id === a)) {
|
if (pokemon?.getAbility().id && this.requiredAbilities.some(a => pokemon.getAbility().id === a)) {
|
||||||
return ["ability", pokemon.getAbility().name];
|
return ["ability", pokemon.getAbility().name];
|
||||||
}
|
}
|
||||||
return null;
|
return ["ability", ""];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,7 +609,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
|
||||||
return this.requiredStatusEffect.some((statusEffect) => {
|
return this.requiredStatusEffect.some((statusEffect) => {
|
||||||
if (statusEffect === StatusEffect.NONE) {
|
if (statusEffect === StatusEffect.NONE) {
|
||||||
// StatusEffect.NONE also checks for null or undefined status
|
// StatusEffect.NONE also checks for null or undefined status
|
||||||
return isNullOrUndefined(pokemon.status) || isNullOrUndefined(pokemon.status.effect) || pokemon.status?.effect === statusEffect;
|
return isNullOrUndefined(pokemon.status) || isNullOrUndefined(pokemon.status!.effect) || pokemon.status?.effect === statusEffect;
|
||||||
} else {
|
} else {
|
||||||
return pokemon.status?.effect === statusEffect;
|
return pokemon.status?.effect === statusEffect;
|
||||||
}
|
}
|
||||||
|
@ -639,7 +641,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
|
||||||
if (reqStatus.length > 0) {
|
if (reqStatus.length > 0) {
|
||||||
return ["status", StatusEffect[reqStatus[0]]];
|
return ["status", StatusEffect[reqStatus[0]]];
|
||||||
}
|
}
|
||||||
return null;
|
return ["status", ""];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
import { isNullOrUndefined } from "#app/utils";
|
import { capitalizeFirstLetter, isNullOrUndefined } from "#app/utils";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounterIntroVisuals, { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro";
|
import MysteryEncounterIntroVisuals, { MysteryEncounterSpriteConfig } from "#app/field/mystery-encounter-intro";
|
||||||
|
@ -125,17 +125,17 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||||
/**
|
/**
|
||||||
* Dialogue object containing all the dialogue, messages, tooltips, etc. for an encounter
|
* Dialogue object containing all the dialogue, messages, tooltips, etc. for an encounter
|
||||||
*/
|
*/
|
||||||
dialogue?: MysteryEncounterDialogue;
|
dialogue: MysteryEncounterDialogue;
|
||||||
/**
|
/**
|
||||||
* Data used for setting up/initializing enemy party in battles
|
* Data used for setting up/initializing enemy party in battles
|
||||||
* Can store multiple configs so that one can be chosen based on option selected
|
* Can store multiple configs so that one can be chosen based on option selected
|
||||||
* Should usually be defined in `onInit()` or `onPreOptionPhase()`
|
* Should usually be defined in `onInit()` or `onPreOptionPhase()`
|
||||||
*/
|
*/
|
||||||
enemyPartyConfigs?: EnemyPartyConfig[];
|
enemyPartyConfigs: EnemyPartyConfig[];
|
||||||
/**
|
/**
|
||||||
* Object instance containing sprite data for an encounter when it is being spawned
|
* Object instance containing sprite data for an encounter when it is being spawned
|
||||||
* Otherwise, will be undefined
|
* Otherwise, will be undefined
|
||||||
* You probably shouldn't do anything with this unless you have a very specific need
|
* You probably shouldn't do anything directly with this unless you have a very specific need
|
||||||
*/
|
*/
|
||||||
introVisuals?: MysteryEncounterIntroVisuals;
|
introVisuals?: MysteryEncounterIntroVisuals;
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||||
return !this.primaryPokemonRequirements.some(req => !req.queryParty(scene.getParty()).map(p => p.id).includes(pokemon.id));
|
return !this.primaryPokemonRequirements.some(req => !req.queryParty(scene.getParty()).map(p => p.id).includes(pokemon.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
private meetsPrimaryRequirementAndPrimaryPokemonSelected(scene: BattleScene): boolean {
|
meetsPrimaryRequirementAndPrimaryPokemonSelected(scene: BattleScene): boolean {
|
||||||
if (this.primaryPokemonRequirements.length === 0) {
|
if (this.primaryPokemonRequirements.length === 0) {
|
||||||
const activeMon = scene.getParty().filter(p => p.isActive(true));
|
const activeMon = scene.getParty().filter(p => p.isActive(true));
|
||||||
if (activeMon.length > 0) {
|
if (activeMon.length > 0) {
|
||||||
|
@ -290,7 +290,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private meetsSecondaryRequirementAndSecondaryPokemonSelected(scene: BattleScene): boolean {
|
meetsSecondaryRequirementAndSecondaryPokemonSelected(scene: BattleScene): boolean {
|
||||||
if (!this.secondaryPokemonRequirements) {
|
if (!this.secondaryPokemonRequirements) {
|
||||||
this.secondaryPokemon = [];
|
this.secondaryPokemon = [];
|
||||||
return true;
|
return true;
|
||||||
|
@ -338,7 +338,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||||
if (!req.invertQuery) {
|
if (!req.invertQuery) {
|
||||||
const value = req.getDialogueToken(scene, this.primaryPokemon);
|
const value = req.getDialogueToken(scene, this.primaryPokemon);
|
||||||
if (value?.length === 2) {
|
if (value?.length === 2) {
|
||||||
this.setDialogueToken("primary" + this.capitalizeFirstLetter(value[0]), value[1]);
|
this.setDialogueToken("primary" + capitalizeFirstLetter(value[0]), value[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,9 +349,9 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||||
if (!req.invertQuery) {
|
if (!req.invertQuery) {
|
||||||
const value = req.getDialogueToken(scene, this.secondaryPokemon[0]);
|
const value = req.getDialogueToken(scene, this.secondaryPokemon[0]);
|
||||||
if (value?.length === 2) {
|
if (value?.length === 2) {
|
||||||
this.setDialogueToken("primary" + this.capitalizeFirstLetter(value[0]), value[1]);
|
this.setDialogueToken("primary" + capitalizeFirstLetter(value[0]), value[1]);
|
||||||
}
|
}
|
||||||
this.setDialogueToken("secondary" + this.capitalizeFirstLetter(value[0]), value[1]);
|
this.setDialogueToken("secondary" + capitalizeFirstLetter(value[0]), value[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -365,7 +365,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||||
for (const req of opt.requirements) {
|
for (const req of opt.requirements) {
|
||||||
const dialogueToken = req.getDialogueToken(scene);
|
const dialogueToken = req.getDialogueToken(scene);
|
||||||
if (dialogueToken?.length === 2) {
|
if (dialogueToken?.length === 2) {
|
||||||
this.setDialogueToken("option" + j + this.capitalizeFirstLetter(dialogueToken[0]), dialogueToken[1]);
|
this.setDialogueToken("option" + j + capitalizeFirstLetter(dialogueToken[0]), dialogueToken[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -375,7 +375,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||||
if (!req.invertQuery) {
|
if (!req.invertQuery) {
|
||||||
const value = req.getDialogueToken(scene, opt.primaryPokemon);
|
const value = req.getDialogueToken(scene, opt.primaryPokemon);
|
||||||
if (value?.length === 2) {
|
if (value?.length === 2) {
|
||||||
this.setDialogueToken("option" + j + "Primary" + this.capitalizeFirstLetter(value[0]), value[1]);
|
this.setDialogueToken("option" + j + "Primary" + capitalizeFirstLetter(value[0]), value[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -386,7 +386,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||||
if (!req.invertQuery) {
|
if (!req.invertQuery) {
|
||||||
const value = req.getDialogueToken(scene, opt.secondaryPokemon[0]);
|
const value = req.getDialogueToken(scene, opt.secondaryPokemon[0]);
|
||||||
if (value?.length === 2) {
|
if (value?.length === 2) {
|
||||||
this.setDialogueToken("option" + j + "Secondary" + this.capitalizeFirstLetter(value[0]), value[1]);
|
this.setDialogueToken("option" + j + "Secondary" + capitalizeFirstLetter(value[0]), value[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -418,42 +418,8 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
||||||
const currentOffset = this.seedOffset ?? scene.currentBattle.waveIndex * 1000;
|
const currentOffset = this.seedOffset ?? scene.currentBattle.waveIndex * 1000;
|
||||||
this.seedOffset = currentOffset + 512;
|
this.seedOffset = currentOffset + 512;
|
||||||
}
|
}
|
||||||
|
|
||||||
private capitalizeFirstLetter(str: string) {
|
|
||||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseMysteryEncounter = {
|
|
||||||
hideBattleIntroMessage: false,
|
|
||||||
autoHideIntroVisuals: true,
|
|
||||||
enterIntroVisualsFromRight: false,
|
|
||||||
catchAllowed: false,
|
|
||||||
continuousEncounter: false,
|
|
||||||
maxAllowedEncounters: 3,
|
|
||||||
requirements: [],
|
|
||||||
primaryPokemonRequirements: [],
|
|
||||||
secondaryPokemonRequirements: [],
|
|
||||||
excludePrimaryFromSupportRequirements: true,
|
|
||||||
dialogueTokens: new Map<string, string>(),
|
|
||||||
encounterMode: MysteryEncounterMode.DEFAULT,
|
|
||||||
lockEncounterRewardTiers: false,
|
|
||||||
startOfBattleEffectsComplete: false,
|
|
||||||
expMultiplier: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Picks non-optional fields from an "object" type alias and returns a union of literal types (keys) */
|
|
||||||
type PickNonOptionalFieldsKeys<T> = Exclude<{ [K in Keys<T>]: T extends Record<K, T[K]> ? K : never; }[Keys<T>], undefined>;
|
|
||||||
// type NonFunctionFieldsKeys<T> = { [K in Keys<T>]: T[K] extends Function ? never : K; }[Keys<T>];
|
|
||||||
// type FunctionPropertyKeys<T> = { [K in Keys<T>]: T[K] extends Function ? K : never; }[Keys<T>];
|
|
||||||
/* Extracts keys from T */
|
|
||||||
type Keys<T> = keyof T;
|
|
||||||
/* Filters out all optional fields from an "object" type alias, and all function fields */
|
|
||||||
type PickNonOptionalFields<T> = Pick<T, PickNonOptionalFieldsKeys<T>>;
|
|
||||||
/* Omits keys that exist in the base object and optional keys on MysteryEncounter, as well as functions on MysteryEncounter */
|
|
||||||
type OmitBaseAndOptionalKeys<T, B> = Omit<PickNonOptionalFields<T>, Keys<B>>;
|
|
||||||
// type OmitBaseSuppliedAndOptionalKeys<T, B, S> = Omit<OmitBaseAndOptionalKeys<T, B>, Keys<S>>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder class for creating a MysteryEncounter
|
* Builder class for creating a MysteryEncounter
|
||||||
* must call `build()` at the end after specifying all params for the MysteryEncounter
|
* must call `build()` at the end after specifying all params for the MysteryEncounter
|
||||||
|
@ -461,8 +427,9 @@ type OmitBaseAndOptionalKeys<T, B> = Omit<PickNonOptionalFields<T>, Keys<B>>;
|
||||||
export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
encounterType: MysteryEncounterType;
|
encounterType: MysteryEncounterType;
|
||||||
encounterMode: MysteryEncounterMode;
|
encounterMode: MysteryEncounterMode;
|
||||||
options: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]];
|
options: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]] = [new MysteryEncounterOption(null), new MysteryEncounterOption(null)];
|
||||||
spriteConfigs: MysteryEncounterSpriteConfig[];
|
spriteConfigs: MysteryEncounterSpriteConfig[];
|
||||||
|
enemyPartyConfigs: EnemyPartyConfig[] = [];
|
||||||
|
|
||||||
dialogue: MysteryEncounterDialogue = {};
|
dialogue: MysteryEncounterDialogue = {};
|
||||||
encounterTier: MysteryEncounterTier;
|
encounterTier: MysteryEncounterTier;
|
||||||
|
@ -478,15 +445,15 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
onInit?: (scene: BattleScene) => boolean;
|
onInit?: (scene: BattleScene) => boolean;
|
||||||
onVisualsStart?: (scene: BattleScene) => boolean;
|
onVisualsStart?: (scene: BattleScene) => boolean;
|
||||||
|
|
||||||
hideBattleIntroMessage: boolean;
|
hideBattleIntroMessage: boolean = false;
|
||||||
hideIntroVisuals: boolean;
|
autoHideIntroVisuals: boolean = true;
|
||||||
enterIntroVisualsFromRight: boolean;
|
enterIntroVisualsFromRight: boolean = false;
|
||||||
continuousEncounter: boolean;
|
continuousEncounter: boolean = false;
|
||||||
catchAllowed: boolean;
|
catchAllowed: boolean = false;
|
||||||
lockEncounterRewardTiers: boolean;
|
lockEncounterRewardTiers: boolean = false;
|
||||||
startOfBattleEffectsComplete: boolean;
|
startOfBattleEffectsComplete: boolean = false;
|
||||||
maxAllowedEncounters: number;
|
maxAllowedEncounters: number = 3;
|
||||||
expMultiplier: number;
|
expMultiplier: number = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder class has to re-declare the {@link MysteryEncounter} class functions so
|
* Builder class has to re-declare the {@link MysteryEncounter} class functions so
|
||||||
|
@ -502,7 +469,6 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
updateSeedOffset = MysteryEncounter.prototype["updateSeedOffset"];
|
updateSeedOffset = MysteryEncounter.prototype["updateSeedOffset"];
|
||||||
meetsPrimaryRequirementAndPrimaryPokemonSelected = MysteryEncounter.prototype["meetsPrimaryRequirementAndPrimaryPokemonSelected"];
|
meetsPrimaryRequirementAndPrimaryPokemonSelected = MysteryEncounter.prototype["meetsPrimaryRequirementAndPrimaryPokemonSelected"];
|
||||||
meetsSecondaryRequirementAndSecondaryPokemonSelected = MysteryEncounter.prototype["meetsSecondaryRequirementAndSecondaryPokemonSelected"];
|
meetsSecondaryRequirementAndSecondaryPokemonSelected = MysteryEncounter.prototype["meetsSecondaryRequirementAndSecondaryPokemonSelected"];
|
||||||
capitalizeFirstLetter = MysteryEncounter.prototype["capitalizeFirstLetter"];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* REQUIRED
|
* REQUIRED
|
||||||
|
@ -514,15 +480,14 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* @param encounterType
|
* @param encounterType
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
static withEncounterType(encounterType: MysteryEncounterType): MysteryEncounterBuilder & OmitBaseAndOptionalKeys<MysteryEncounter, typeof baseMysteryEncounter> & Pick<MysteryEncounter, "encounterType"> {
|
static withEncounterType(encounterType: MysteryEncounterType): MysteryEncounterBuilder & Pick<MysteryEncounter, "encounterType"> {
|
||||||
return Object.assign(new MysteryEncounterBuilder(), { ...baseMysteryEncounter, encounterType });
|
return Object.assign(new MysteryEncounterBuilder(), { encounterType });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines an option for the encounter.
|
* Defines an option for the encounter.
|
||||||
* Use for complex options.
|
* Use for complex options.
|
||||||
* There should be at least 2 options defined and no more than 4.
|
* There should be at least 2 options defined and no more than 4.
|
||||||
* If easy/streamlined use {@linkcode MysteryEncounterBuilder.withOptionPhase}
|
|
||||||
*
|
*
|
||||||
* @param option - MysteryEncounterOption to add, can use MysteryEncounterOptionBuilder to create instance
|
* @param option - MysteryEncounterOption to add, can use MysteryEncounterOptionBuilder to create instance
|
||||||
* @returns
|
* @returns
|
||||||
|
|
|
@ -552,7 +552,7 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
||||||
duration: 750,
|
duration: 750,
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
this.dexProgressContainer.on("pointerover", () => {
|
this.dexProgressContainer.on("pointerover", () => {
|
||||||
(this.scene as BattleScene).ui.showTooltip(null, i18next.t("mysteryEncounter:affects_pokedex"), true);
|
(this.scene as BattleScene).ui.showTooltip("", i18next.t("mysteryEncounter:affects_pokedex"), true);
|
||||||
});
|
});
|
||||||
this.dexProgressContainer.on("pointerout", () => {
|
this.dexProgressContainer.on("pointerout", () => {
|
||||||
(this.scene as BattleScene).ui.hideTooltip();
|
(this.scene as BattleScene).ui.hideTooltip();
|
||||||
|
|
|
@ -560,3 +560,7 @@ export function capitalizeString(str: string, sep: string, lowerFirstChar: boole
|
||||||
export function isNullOrUndefined(object: any): boolean {
|
export function isNullOrUndefined(object: any): boolean {
|
||||||
return null === object || undefined === object;
|
return null === object || undefined === object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function capitalizeFirstLetter(str: string) {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue