Merge branch 'mystery-battle-events' into event/getting-lost-at-the-sea
This commit is contained in:
commit
1ae7a86cf9
|
@ -4,8 +4,8 @@
|
||||||
"image": "mad_scientist_m.png",
|
"image": "mad_scientist_m.png",
|
||||||
"format": "RGBA8888",
|
"format": "RGBA8888",
|
||||||
"size": {
|
"size": {
|
||||||
"w": 44,
|
"w": 46,
|
||||||
"h": 74
|
"h": 76
|
||||||
},
|
},
|
||||||
"scale": 1,
|
"scale": 1,
|
||||||
"frames": [
|
"frames": [
|
||||||
|
@ -24,8 +24,8 @@
|
||||||
"h": 74
|
"h": 74
|
||||||
},
|
},
|
||||||
"frame": {
|
"frame": {
|
||||||
"x": 0,
|
"x": 1,
|
||||||
"y": 0,
|
"y": 1,
|
||||||
"w": 44,
|
"w": 44,
|
||||||
"h": 74
|
"h": 74
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,6 @@
|
||||||
"meta": {
|
"meta": {
|
||||||
"app": "https://www.codeandweb.com/texturepacker",
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
"version": "3.0",
|
"version": "3.0",
|
||||||
"smartupdate": "$TexturePacker:SmartUpdate:895f0a79b89fa0fb44167f4584fd9a22:357b46953b7e17c6b2f43a62d52855d8:cc1ed0e4f90aaa9dcf1b39a0af1283b0$"
|
"smartupdate": "$TexturePacker:SmartUpdate:a7f8ff2bbb362868f51125c254eb6681:cf76e61ddd31a8f46af67ced168c44a2:4fc09abe16c0608828269e5da81d0744$"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 920 B |
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
{
|
||||||
|
"image": "teacher.png",
|
||||||
|
"format": "RGBA8888",
|
||||||
|
"size": {
|
||||||
|
"w": 43,
|
||||||
|
"h": 74
|
||||||
|
},
|
||||||
|
"scale": 1,
|
||||||
|
"frames": [
|
||||||
|
{
|
||||||
|
"filename": "0001.png",
|
||||||
|
"rotated": false,
|
||||||
|
"trimmed": true,
|
||||||
|
"sourceSize": {
|
||||||
|
"w": 80,
|
||||||
|
"h": 80
|
||||||
|
},
|
||||||
|
"spriteSourceSize": {
|
||||||
|
"x": 19,
|
||||||
|
"y": 8,
|
||||||
|
"w": 41,
|
||||||
|
"h": 72
|
||||||
|
},
|
||||||
|
"frame": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 1,
|
||||||
|
"w": 41,
|
||||||
|
"h": 72
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
|
"version": "3.0",
|
||||||
|
"smartupdate": "$TexturePacker:SmartUpdate:506e5a4ce79c134a7b4af90a90aef244:1b81d3d84bf12cedc419805eaff82548:59bc5dd000b5e72588320b473e31c312$"
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 727 B |
|
@ -67,7 +67,7 @@ import { Species } from "#enums/species";
|
||||||
import { UiTheme } from "#enums/ui-theme";
|
import { UiTheme } from "#enums/ui-theme";
|
||||||
import { TimedEventManager } from "#app/timed-event-manager.js";
|
import { TimedEventManager } from "#app/timed-event-manager.js";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import MysteryEncounter, { MysteryEncounterTier, MysteryEncounterVariant } from "./data/mystery-encounters/mystery-encounter";
|
import IMysteryEncounter, { MysteryEncounterTier, MysteryEncounterVariant } from "./data/mystery-encounters/mystery-encounter";
|
||||||
import { mysteryEncountersByBiome, allMysteryEncounters, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters";
|
import { mysteryEncountersByBiome, allMysteryEncounters, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters";
|
||||||
import { MysteryEncounterData } from "#app/data/mystery-encounters/mystery-encounter-data";
|
import { MysteryEncounterData } from "#app/data/mystery-encounters/mystery-encounter-data";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
@ -216,7 +216,7 @@ export default class BattleScene extends SceneBase {
|
||||||
public pokemonInfoContainer: PokemonInfoContainer;
|
public pokemonInfoContainer: PokemonInfoContainer;
|
||||||
private party: PlayerPokemon[];
|
private party: PlayerPokemon[];
|
||||||
public mysteryEncounterData: MysteryEncounterData = new MysteryEncounterData(null);
|
public mysteryEncounterData: MysteryEncounterData = new MysteryEncounterData(null);
|
||||||
public lastMysteryEncounter: MysteryEncounter;
|
public lastMysteryEncounter: IMysteryEncounter;
|
||||||
/** Combined Biome and Wave count text */
|
/** Combined Biome and Wave count text */
|
||||||
private biomeWaveText: Phaser.GameObjects.Text;
|
private biomeWaveText: Phaser.GameObjects.Text;
|
||||||
private moneyText: Phaser.GameObjects.Text;
|
private moneyText: Phaser.GameObjects.Text;
|
||||||
|
@ -1026,7 +1026,7 @@ export default class BattleScene extends SceneBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean, mysteryEncounter?: MysteryEncounter): Battle {
|
newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean, mysteryEncounter?: IMysteryEncounter): Battle {
|
||||||
const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave;
|
const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave;
|
||||||
const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1);
|
const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1);
|
||||||
let newDouble: boolean;
|
let newDouble: boolean;
|
||||||
|
@ -2364,7 +2364,7 @@ export default class BattleScene extends SceneBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
party.every((enemyPokemon: EnemyPokemon, i: integer) => {
|
party.every((enemyPokemon: EnemyPokemon, i: integer) => {
|
||||||
if (customHeldModifiers && i < customHeldModifiers.length && customHeldModifiers[i].length > 0) {
|
if (customHeldModifiers && i < customHeldModifiers.length && customHeldModifiers[i] && customHeldModifiers[i].length > 0) {
|
||||||
customHeldModifiers[i].forEach(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this));
|
customHeldModifiers[i].forEach(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2621,9 +2621,9 @@ export default class BattleScene extends SceneBase {
|
||||||
* @param override - used to load session encounter when restarting game, etc.
|
* @param override - used to load session encounter when restarting game, etc.
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
getMysteryEncounter(override: MysteryEncounter): MysteryEncounter {
|
getMysteryEncounter(override: IMysteryEncounter): IMysteryEncounter {
|
||||||
// Loading override or session encounter
|
// Loading override or session encounter
|
||||||
let encounter: MysteryEncounter;
|
let encounter: IMysteryEncounter;
|
||||||
if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)) {
|
if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)) {
|
||||||
encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE];
|
encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE];
|
||||||
} else {
|
} else {
|
||||||
|
@ -2645,8 +2645,8 @@ export default class BattleScene extends SceneBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encounter) {
|
if (encounter) {
|
||||||
encounter = new MysteryEncounter(encounter);
|
encounter = new IMysteryEncounter(encounter);
|
||||||
encounter.meetsRequirements(this);
|
encounter.populateDialogueTokensFromRequirements(this);
|
||||||
return encounter;
|
return encounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2674,7 +2674,7 @@ export default class BattleScene extends SceneBase {
|
||||||
tier = Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE;
|
tier = Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE;
|
||||||
}
|
}
|
||||||
|
|
||||||
let availableEncounters = [];
|
let availableEncounters: IMysteryEncounter[] = [];
|
||||||
// New encounter will never be the same as the most recent encounter
|
// New encounter will never be the same as the most recent encounter
|
||||||
const previousEncounter = this.mysteryEncounterData.encounteredEvents?.length > 0 ? this.mysteryEncounterData.encounteredEvents[this.mysteryEncounterData.encounteredEvents.length - 1][0] : null;
|
const previousEncounter = this.mysteryEncounterData.encounteredEvents?.length > 0 ? this.mysteryEncounterData.encounteredEvents[this.mysteryEncounterData.encounteredEvents.length - 1][0] : null;
|
||||||
const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType);
|
const biomeMysteryEncounters = mysteryEncountersByBiome.get(this.arena.biomeType);
|
||||||
|
@ -2695,8 +2695,8 @@ export default class BattleScene extends SceneBase {
|
||||||
}
|
}
|
||||||
encounter = availableEncounters[Utils.randSeedInt(availableEncounters.length)];
|
encounter = availableEncounters[Utils.randSeedInt(availableEncounters.length)];
|
||||||
// New encounter object to not dirty flags
|
// New encounter object to not dirty flags
|
||||||
encounter = new MysteryEncounter(encounter);
|
encounter = new IMysteryEncounter(encounter);
|
||||||
encounter.meetsRequirements(this);
|
encounter.populateDialogueTokensFromRequirements(this);
|
||||||
return encounter;
|
return encounter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { PlayerGender } from "#enums/player-gender";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
import MysteryEncounter, { MysteryEncounterVariant } from "./data/mystery-encounters/mystery-encounter";
|
import IMysteryEncounter, { MysteryEncounterVariant } from "./data/mystery-encounters/mystery-encounter";
|
||||||
|
|
||||||
export enum BattleType {
|
export enum BattleType {
|
||||||
WILD,
|
WILD,
|
||||||
|
@ -69,7 +69,7 @@ export default class Battle {
|
||||||
public lastUsedPokeball: PokeballType;
|
public lastUsedPokeball: PokeballType;
|
||||||
public playerFaints: number; // The amount of times pokemon on the players side have fainted
|
public playerFaints: number; // The amount of times pokemon on the players side have fainted
|
||||||
public enemyFaints: number; // The amount of times pokemon on the enemies side have fainted
|
public enemyFaints: number; // The amount of times pokemon on the enemies side have fainted
|
||||||
public mysteryEncounter: MysteryEncounter;
|
public mysteryEncounter: IMysteryEncounter;
|
||||||
|
|
||||||
private rngCounter: integer = 0;
|
private rngCounter: integer = 0;
|
||||||
|
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
|
||||||
|
|
||||||
export const DarkDealDialogue: MysteryEncounterDialogue = {
|
|
||||||
intro: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:dark_deal_intro_message"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
speaker: "mysteryEncounter:dark_deal_speaker",
|
|
||||||
text: "mysteryEncounter:dark_deal_intro_dialogue"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
encounterOptionsDialogue: {
|
|
||||||
title: "mysteryEncounter:dark_deal_title",
|
|
||||||
description: "mysteryEncounter:dark_deal_description",
|
|
||||||
query: "mysteryEncounter:dark_deal_query",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:dark_deal_option_1_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:dark_deal_option_1_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
speaker: "mysteryEncounter:dark_deal_speaker",
|
|
||||||
text: "mysteryEncounter:dark_deal_option_1_selected"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:dark_deal_option_1_selected_message"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:dark_deal_option_2_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:dark_deal_option_2_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
speaker: "mysteryEncounter:dark_deal_speaker",
|
|
||||||
text: "mysteryEncounter:dark_deal_option_2_selected"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
outro: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:dark_deal_outro"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
|
@ -1,36 +0,0 @@
|
||||||
import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
|
||||||
|
|
||||||
export const DepartmentStoreSaleDialogue: MysteryEncounterDialogue = {
|
|
||||||
intro: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:department_store_sale_intro_message"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:department_store_sale_intro_dialogue",
|
|
||||||
speaker: "mysteryEncounter:department_store_sale_speaker"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
encounterOptionsDialogue: {
|
|
||||||
title: "mysteryEncounter:department_store_sale_title",
|
|
||||||
description: "mysteryEncounter:department_store_sale_description",
|
|
||||||
query: "mysteryEncounter:department_store_sale_query",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:department_store_sale_option_1_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:department_store_sale_option_1_tooltip"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:department_store_sale_option_2_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:department_store_sale_option_2_tooltip"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:department_store_sale_option_3_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:department_store_sale_option_3_tooltip"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:department_store_sale_option_4_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:department_store_sale_option_4_tooltip"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,38 +0,0 @@
|
||||||
import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
|
||||||
|
|
||||||
export const FightOrFlightDialogue: MysteryEncounterDialogue = {
|
|
||||||
intro: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:fight_or_flight_intro_message"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
encounterOptionsDialogue: {
|
|
||||||
title: "mysteryEncounter:fight_or_flight_title",
|
|
||||||
description: "mysteryEncounter:fight_or_flight_description",
|
|
||||||
query: "mysteryEncounter:fight_or_flight_query",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:fight_or_flight_option_1_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:fight_or_flight_option_1_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:fight_or_flight_option_1_selected_message"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:fight_or_flight_option_2_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:fight_or_flight_option_2_tooltip"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:fight_or_flight_option_3_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:fight_or_flight_option_3_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:fight_or_flight_option_3_selected"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,57 +0,0 @@
|
||||||
import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
|
||||||
|
|
||||||
export const MysteriousChallengersDialogue: MysteryEncounterDialogue = {
|
|
||||||
intro: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:mysterious_challengers_intro_message"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
encounterOptionsDialogue: {
|
|
||||||
title: "mysteryEncounter:mysterious_challengers_title",
|
|
||||||
description: "mysteryEncounter:mysterious_challengers_description",
|
|
||||||
query: "mysteryEncounter:mysterious_challengers_query",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:mysterious_challengers_option_1_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:mysterious_challengers_option_1_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:mysterious_challengers_option_selected_message"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:mysterious_challengers_option_2_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:mysterious_challengers_option_2_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:mysterious_challengers_option_selected_message"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:mysterious_challengers_option_3_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:mysterious_challengers_option_3_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:mysterious_challengers_option_selected_message"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:mysterious_challengers_option_4_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:mysterious_challengers_option_4_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:mysterious_challengers_option_4_selected_message"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
outro: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:mysterious_challengers_outro_win"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
|
@ -1,34 +0,0 @@
|
||||||
import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
|
||||||
|
|
||||||
export const MysteriousChestDialogue: MysteryEncounterDialogue = {
|
|
||||||
intro: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:mysterious_chest_intro_message"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
encounterOptionsDialogue: {
|
|
||||||
title: "mysteryEncounter:mysterious_chest_title",
|
|
||||||
description: "mysteryEncounter:mysterious_chest_description",
|
|
||||||
query: "mysteryEncounter:mysterious_chest_query",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:mysterious_chest_option_1_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:mysterious_chest_option_1_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:mysterious_chest_option_1_selected_message"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:mysterious_chest_option_2_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:mysterious_chest_option_2_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:mysterious_chest_option_2_selected_message"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,42 +0,0 @@
|
||||||
import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
|
||||||
|
|
||||||
export const ShadyVitaminDealerDialogue: MysteryEncounterDialogue = {
|
|
||||||
intro: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:shady_vitamin_dealer_intro_message"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:shady_vitamin_dealer_intro_dialogue",
|
|
||||||
speaker: "mysteryEncounter:shady_vitamin_dealer_speaker"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
encounterOptionsDialogue: {
|
|
||||||
title: "mysteryEncounter:shady_vitamin_dealer_title",
|
|
||||||
description: "mysteryEncounter:shady_vitamin_dealer_description",
|
|
||||||
query: "mysteryEncounter:shady_vitamin_dealer_query",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:shady_vitamin_dealer_option_1_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:shady_vitamin_dealer_option_1_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:shady_vitamin_dealer_option_selected"
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:shady_vitamin_dealer_option_2_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:shady_vitamin_dealer_option_2_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:shady_vitamin_dealer_option_selected"
|
|
||||||
},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:shady_vitamin_dealer_option_3_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:shady_vitamin_dealer_option_3_tooltip"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,39 +0,0 @@
|
||||||
import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
|
||||||
|
|
||||||
export const SleepingSnorlaxDialogue: MysteryEncounterDialogue = {
|
|
||||||
intro: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:sleeping_snorlax_intro_message"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
encounterOptionsDialogue: {
|
|
||||||
title: "mysteryEncounter:sleeping_snorlax_title",
|
|
||||||
description: "mysteryEncounter:sleeping_snorlax_description",
|
|
||||||
query: "mysteryEncounter:sleeping_snorlax_query",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:sleeping_snorlax_option_1_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:sleeping_snorlax_option_1_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:sleeping_snorlax_option_1_selected_message"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:sleeping_snorlax_option_2_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:sleeping_snorlax_option_2_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:sleeping_snorlax_option_2_selected_message"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:sleeping_snorlax_option_3_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:sleeping_snorlax_option_3_tooltip",
|
|
||||||
disabledTooltip: "mysteryEncounter:sleeping_snorlax_option_3_disabled_tooltip"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,50 +0,0 @@
|
||||||
import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
|
||||||
|
|
||||||
export const TrainingSessionDialogue: MysteryEncounterDialogue = {
|
|
||||||
intro: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:training_session_intro_message"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
encounterOptionsDialogue: {
|
|
||||||
title: "mysteryEncounter:training_session_title",
|
|
||||||
description: "mysteryEncounter:training_session_description",
|
|
||||||
query: "mysteryEncounter:training_session_query",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:training_session_option_1_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:training_session_option_1_tooltip",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:training_session_option_selected_message"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:training_session_option_2_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:training_session_option_2_tooltip",
|
|
||||||
secondOptionPrompt: "mysteryEncounter:training_session_option_2_select_prompt",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:training_session_option_selected_message"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
buttonLabel: "mysteryEncounter:training_session_option_3_label",
|
|
||||||
buttonTooltip: "mysteryEncounter:training_session_option_3_tooltip",
|
|
||||||
secondOptionPrompt: "mysteryEncounter:training_session_option_3_select_prompt",
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:training_session_option_selected_message"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
outro: [
|
|
||||||
{
|
|
||||||
text: "mysteryEncounter:training_session_outro_win"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
import { Type } from "#app/data/type";
|
||||||
|
import { ModifierRewardPhase } from "#app/phases";
|
||||||
|
import { isNullOrUndefined, randSeedInt } from "#app/utils";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import BattleScene from "../../../battle-scene";
|
||||||
|
import { AddPokeballModifierType } from "../../../modifier/modifier-type";
|
||||||
|
import { PokeballType } from "../../pokeball";
|
||||||
|
import { getPokemonSpecies } from "../../pokemon-species";
|
||||||
|
import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter";
|
||||||
|
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { EnemyPartyConfig, EnemyPokemonConfig, getRandomPlayerPokemon, getRandomSpeciesByStarterTier, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, } from "../mystery-encounter-utils";
|
||||||
|
|
||||||
|
/** i18n namespace for encounter */
|
||||||
|
const namespace = "mysteryEncounter:dark_deal";
|
||||||
|
|
||||||
|
// Exclude Ultra Beasts, Paradox, Necrozma, Eternatus, and egg-locked mythicals
|
||||||
|
const excludedBosses = [
|
||||||
|
Species.NECROZMA,
|
||||||
|
Species.COSMOG,
|
||||||
|
Species.COSMOEM,
|
||||||
|
Species.SOLGALEO,
|
||||||
|
Species.LUNALA,
|
||||||
|
Species.ETERNATUS,
|
||||||
|
Species.NIHILEGO,
|
||||||
|
Species.BUZZWOLE,
|
||||||
|
Species.PHEROMOSA,
|
||||||
|
Species.XURKITREE,
|
||||||
|
Species.CELESTEELA,
|
||||||
|
Species.KARTANA,
|
||||||
|
Species.GUZZLORD,
|
||||||
|
Species.POIPOLE,
|
||||||
|
Species.NAGANADEL,
|
||||||
|
Species.STAKATAKA,
|
||||||
|
Species.BLACEPHALON,
|
||||||
|
Species.GREAT_TUSK,
|
||||||
|
Species.SCREAM_TAIL,
|
||||||
|
Species.BRUTE_BONNET,
|
||||||
|
Species.FLUTTER_MANE,
|
||||||
|
Species.SLITHER_WING,
|
||||||
|
Species.SANDY_SHOCKS,
|
||||||
|
Species.ROARING_MOON,
|
||||||
|
Species.KORAIDON,
|
||||||
|
Species.WALKING_WAKE,
|
||||||
|
Species.GOUGING_FIRE,
|
||||||
|
Species.RAGING_BOLT,
|
||||||
|
Species.IRON_TREADS,
|
||||||
|
Species.IRON_BUNDLE,
|
||||||
|
Species.IRON_HANDS,
|
||||||
|
Species.IRON_JUGULIS,
|
||||||
|
Species.IRON_MOTH,
|
||||||
|
Species.IRON_THORNS,
|
||||||
|
Species.IRON_VALIANT,
|
||||||
|
Species.MIRAIDON,
|
||||||
|
Species.IRON_LEAVES,
|
||||||
|
Species.IRON_BOULDER,
|
||||||
|
Species.IRON_CROWN,
|
||||||
|
Species.MEW,
|
||||||
|
Species.CELEBI,
|
||||||
|
Species.DEOXYS,
|
||||||
|
Species.JIRACHI,
|
||||||
|
Species.PHIONE,
|
||||||
|
Species.MANAPHY,
|
||||||
|
Species.ARCEUS,
|
||||||
|
Species.VICTINI,
|
||||||
|
Species.MELTAN,
|
||||||
|
Species.PECHARUNT,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const DarkDealEncounter: IMysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DARK_DEAL)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "mad_scientist_m",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "dark_deal_porygon",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}_intro_message`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
speaker: `${namespace}_speaker`,
|
||||||
|
text: `${namespace}_intro_dialogue`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withSceneWaveRangeRequirement(30, 180) // waves 30 to 180
|
||||||
|
.withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party
|
||||||
|
.withCatchAllowed(true)
|
||||||
|
.withTitle(`${namespace}_title`)
|
||||||
|
.withDescription(`${namespace}_description`)
|
||||||
|
.withQuery(`${namespace}_query`)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}_option_1_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_1_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
speaker: `${namespace}_speaker`,
|
||||||
|
text: `${namespace}_option_1_selected`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_1_selected_message`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Removes random pokemon (including fainted) from party and adds name to dialogue data tokens
|
||||||
|
// Will never return last battle able mon and instead pick fainted/unable to battle
|
||||||
|
const removedPokemon = getRandomPlayerPokemon(scene, false, true);
|
||||||
|
scene.removePokemonFromPlayerParty(removedPokemon);
|
||||||
|
|
||||||
|
scene.currentBattle.mysteryEncounter.setDialogueToken(
|
||||||
|
"pokeName",
|
||||||
|
removedPokemon.name
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store removed pokemon types
|
||||||
|
scene.currentBattle.mysteryEncounter.misc = [
|
||||||
|
removedPokemon.species.type1,
|
||||||
|
];
|
||||||
|
if (removedPokemon.species.type2) {
|
||||||
|
scene.currentBattle.mysteryEncounter.misc.push(removedPokemon.species.type2);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Give the player 5 Rogue Balls
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, () => new AddPokeballModifierType("rb", PokeballType.ROGUE_BALL, 5)));
|
||||||
|
|
||||||
|
// Start encounter with random legendary (7-10 starter strength) that has level additive
|
||||||
|
const bossTypes = scene.currentBattle.mysteryEncounter.misc as Type[];
|
||||||
|
// Starter egg tier, 35/50/10/5 %odds for tiers 6/7/8/9+
|
||||||
|
const roll = randSeedInt(100);
|
||||||
|
const starterTier: number | [number, number] =
|
||||||
|
roll > 65 ? 6 : roll > 15 ? 7 : roll > 5 ? 8 : [9, 10];
|
||||||
|
const bossSpecies = getPokemonSpecies(getRandomSpeciesByStarterTier(starterTier, excludedBosses, bossTypes));
|
||||||
|
const pokemonConfig: EnemyPokemonConfig = {
|
||||||
|
species: bossSpecies,
|
||||||
|
isBoss: true,
|
||||||
|
};
|
||||||
|
if (!isNullOrUndefined(bossSpecies.forms) && bossSpecies.forms.length > 0) {
|
||||||
|
pokemonConfig.formIndex = 0;
|
||||||
|
}
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
levelAdditiveMultiplier: 0.75,
|
||||||
|
pokemonConfigs: [pokemonConfig],
|
||||||
|
};
|
||||||
|
return initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}_option_2_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_2_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
speaker: `${namespace}_speaker`,
|
||||||
|
text: `${namespace}_option_2_selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Leave encounter with no rewards or exp
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOutroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}_outro`
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.build();
|
|
@ -1,133 +0,0 @@
|
||||||
import { Type } from "#app/data/type";
|
|
||||||
import { ModifierRewardPhase } from "#app/phases";
|
|
||||||
import { isNullOrUndefined, randSeedInt } from "#app/utils";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import BattleScene from "../../../battle-scene";
|
|
||||||
import { AddPokeballModifierType } from "../../../modifier/modifier-type";
|
|
||||||
import { PokeballType } from "../../pokeball";
|
|
||||||
import { getPokemonSpecies } from "../../pokemon-species";
|
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter";
|
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
|
||||||
import {
|
|
||||||
EnemyPartyConfig, EnemyPokemonConfig,
|
|
||||||
getRandomPlayerPokemon,
|
|
||||||
getRandomSpeciesByStarterTier,
|
|
||||||
initBattleWithEnemyConfig,
|
|
||||||
leaveEncounterWithoutBattle
|
|
||||||
} from "../mystery-encounter-utils";
|
|
||||||
|
|
||||||
// Exclude Ultra Beasts, Paradox, Necrozma, Eternatus, and egg-locked mythicals
|
|
||||||
const excludedBosses = [
|
|
||||||
Species.NECROZMA,
|
|
||||||
Species.ETERNATUS,
|
|
||||||
Species.NIHILEGO,
|
|
||||||
Species.BUZZWOLE,
|
|
||||||
Species.PHEROMOSA,
|
|
||||||
Species.XURKITREE,
|
|
||||||
Species.CELESTEELA,
|
|
||||||
Species.KARTANA,
|
|
||||||
Species.GUZZLORD,
|
|
||||||
Species.POIPOLE,
|
|
||||||
Species.NAGANADEL,
|
|
||||||
Species.STAKATAKA,
|
|
||||||
Species.BLACEPHALON,
|
|
||||||
Species.GREAT_TUSK,
|
|
||||||
Species.SCREAM_TAIL,
|
|
||||||
Species.BRUTE_BONNET,
|
|
||||||
Species.FLUTTER_MANE,
|
|
||||||
Species.SLITHER_WING,
|
|
||||||
Species.SANDY_SHOCKS,
|
|
||||||
Species.ROARING_MOON,
|
|
||||||
Species.KORAIDON,
|
|
||||||
Species.WALKING_WAKE,
|
|
||||||
Species.GOUGING_FIRE,
|
|
||||||
Species.RAGING_BOLT,
|
|
||||||
Species.IRON_TREADS,
|
|
||||||
Species.IRON_BUNDLE,
|
|
||||||
Species.IRON_HANDS,
|
|
||||||
Species.IRON_JUGULIS,
|
|
||||||
Species.IRON_MOTH,
|
|
||||||
Species.IRON_THORNS,
|
|
||||||
Species.IRON_VALIANT,
|
|
||||||
Species.MIRAIDON,
|
|
||||||
Species.IRON_LEAVES,
|
|
||||||
Species.IRON_BOULDER,
|
|
||||||
Species.IRON_CROWN,
|
|
||||||
Species.MEW,
|
|
||||||
Species.CELEBI,
|
|
||||||
Species.DEOXYS,
|
|
||||||
Species.JIRACHI,
|
|
||||||
Species.PHIONE,
|
|
||||||
Species.MANAPHY,
|
|
||||||
Species.ARCEUS,
|
|
||||||
Species.VICTINI,
|
|
||||||
Species.MELTAN,
|
|
||||||
Species.PECHARUNT
|
|
||||||
];
|
|
||||||
|
|
||||||
export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|
||||||
.withEncounterType(MysteryEncounterType.DARK_DEAL)
|
|
||||||
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
|
||||||
.withIntroSpriteConfigs([
|
|
||||||
{
|
|
||||||
spriteKey: "mad_scientist_m",
|
|
||||||
fileRoot: "mystery-encounters",
|
|
||||||
hasShadow: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
spriteKey: "dark_deal_porygon",
|
|
||||||
fileRoot: "mystery-encounters",
|
|
||||||
hasShadow: true,
|
|
||||||
repeat: true
|
|
||||||
}
|
|
||||||
])
|
|
||||||
.withSceneWaveRangeRequirement(30, 180) // waves 30 to 180
|
|
||||||
.withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party
|
|
||||||
.withCatchAllowed(true)
|
|
||||||
.withOption(new MysteryEncounterOptionBuilder()
|
|
||||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Removes random pokemon (including fainted) from party and adds name to dialogue data tokens
|
|
||||||
// Will never return last battle able mon and instead pick fainted/unable to battle
|
|
||||||
const removedPokemon = getRandomPlayerPokemon(scene, false, true);
|
|
||||||
scene.removePokemonFromPlayerParty(removedPokemon);
|
|
||||||
|
|
||||||
scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", removedPokemon.name);
|
|
||||||
|
|
||||||
// Store removed pokemon types
|
|
||||||
scene.currentBattle.mysteryEncounter.misc = [removedPokemon.species.type1];
|
|
||||||
if (removedPokemon.species.type2) {
|
|
||||||
scene.currentBattle.mysteryEncounter.misc.push(removedPokemon.species.type2);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Give the player 5 Rogue Balls
|
|
||||||
scene.unshiftPhase(new ModifierRewardPhase(scene, () => new AddPokeballModifierType("rb", PokeballType.ROGUE_BALL, 5)));
|
|
||||||
|
|
||||||
// Start encounter with random legendary (7-10 starter strength) that has level additive
|
|
||||||
const bossTypes = scene.currentBattle.mysteryEncounter.misc as Type[];
|
|
||||||
// Starter egg tier, 35/50/10/5 %odds for tiers 6/7/8/9+
|
|
||||||
const roll = randSeedInt(100);
|
|
||||||
const starterTier: number | [number, number] = roll > 65 ? 6 : roll > 15 ? 7 : roll > 5 ? 8 : [9, 10];
|
|
||||||
const bossSpecies = getPokemonSpecies(getRandomSpeciesByStarterTier(starterTier, excludedBosses, bossTypes));
|
|
||||||
const pokemonConfig: EnemyPokemonConfig = {
|
|
||||||
species: bossSpecies,
|
|
||||||
isBoss: true
|
|
||||||
};
|
|
||||||
if (!isNullOrUndefined(bossSpecies.forms) && bossSpecies.forms.length > 0) {
|
|
||||||
pokemonConfig.formIndex = 0;
|
|
||||||
}
|
|
||||||
const config: EnemyPartyConfig = {
|
|
||||||
levelAdditiveMultiplier: 0.75,
|
|
||||||
pokemonConfigs: [pokemonConfig]
|
|
||||||
};
|
|
||||||
return initBattleWithEnemyConfig(scene, config);
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Leave encounter with no rewards or exp
|
|
||||||
leaveEncounterWithoutBattle(scene, true);
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.build();
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
import {
|
||||||
|
leaveEncounterWithoutBattle,
|
||||||
|
setEncounterRewards,
|
||||||
|
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { randSeedInt } from "#app/utils";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import BattleScene from "../../../battle-scene";
|
||||||
|
import IMysteryEncounter, {
|
||||||
|
MysteryEncounterBuilder,
|
||||||
|
MysteryEncounterTier,
|
||||||
|
} from "../mystery-encounter";
|
||||||
|
|
||||||
|
/** i18n namespace for encounter */
|
||||||
|
const namespace = "mysteryEncounter:department_store_sale";
|
||||||
|
|
||||||
|
export const DepartmentStoreSaleEncounter: IMysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 100)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "b2w2_lady",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
x: -20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: Species.FURFROU.toString(),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
x: 30,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}_intro_message`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}_intro_dialogue`,
|
||||||
|
speaker: `${namespace}_speaker`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withHideIntroVisuals(false)
|
||||||
|
.withTitle(`${namespace}_title`)
|
||||||
|
.withDescription(`${namespace}_description`)
|
||||||
|
.withQuery(`${namespace}_query`)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}_option_1_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_1_tooltip`,
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Choose TMs
|
||||||
|
const modifiers = [];
|
||||||
|
let i = 0;
|
||||||
|
while (i < 4) {
|
||||||
|
// 2/2/1 weight on TM rarity
|
||||||
|
const roll = randSeedInt(5);
|
||||||
|
if (roll < 2) {
|
||||||
|
modifiers.push(modifierTypes.TM_COMMON);
|
||||||
|
} else if (roll < 4) {
|
||||||
|
modifiers.push(modifierTypes.TM_GREAT);
|
||||||
|
} else {
|
||||||
|
modifiers.push(modifierTypes.TM_ULTRA);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}_option_2_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_2_tooltip`,
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Choose Vitamins
|
||||||
|
const modifiers = [];
|
||||||
|
let i = 0;
|
||||||
|
while (i < 3) {
|
||||||
|
// 2/1 weight on base stat booster vs PP Up
|
||||||
|
const roll = randSeedInt(3);
|
||||||
|
if (roll === 0) {
|
||||||
|
modifiers.push(modifierTypes.PP_UP);
|
||||||
|
} else {
|
||||||
|
modifiers.push(modifierTypes.BASE_STAT_BOOSTER);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}_option_3_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_3_tooltip`,
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Choose X Items
|
||||||
|
const modifiers = [];
|
||||||
|
let i = 0;
|
||||||
|
while (i < 5) {
|
||||||
|
// 4/1 weight on base stat booster vs Dire Hit
|
||||||
|
const roll = randSeedInt(5);
|
||||||
|
if (roll === 0) {
|
||||||
|
modifiers.push(modifierTypes.DIRE_HIT);
|
||||||
|
} else {
|
||||||
|
modifiers.push(modifierTypes.TEMP_STAT_BOOSTER);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}_option_4_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_4_tooltip`,
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Choose Pokeballs
|
||||||
|
const modifiers = [];
|
||||||
|
let i = 0;
|
||||||
|
while (i < 4) {
|
||||||
|
// 10/30/20/5 weight on pokeballs
|
||||||
|
const roll = randSeedInt(65);
|
||||||
|
if (roll < 10) {
|
||||||
|
modifiers.push(modifierTypes.POKEBALL);
|
||||||
|
} else if (roll < 40) {
|
||||||
|
modifiers.push(modifierTypes.GREAT_BALL);
|
||||||
|
} else if (roll < 60) {
|
||||||
|
modifiers.push(modifierTypes.ULTRA_BALL);
|
||||||
|
} else {
|
||||||
|
modifiers.push(modifierTypes.ROGUE_BALL);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false, });
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -1,111 +0,0 @@
|
||||||
import {
|
|
||||||
leaveEncounterWithoutBattle, setEncounterExp,
|
|
||||||
setEncounterRewards,
|
|
||||||
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
|
||||||
import { randSeedInt } from "#app/utils";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import BattleScene from "../../../battle-scene";
|
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter";
|
|
||||||
|
|
||||||
export const DepartmentStoreSaleEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|
||||||
.withEncounterType(MysteryEncounterType.DEPARTMENT_STORE_SALE)
|
|
||||||
.withEncounterTier(MysteryEncounterTier.COMMON)
|
|
||||||
.withIntroSpriteConfigs([
|
|
||||||
{
|
|
||||||
spriteKey: "b2w2_lady",
|
|
||||||
fileRoot: "mystery-encounters",
|
|
||||||
hasShadow: true,
|
|
||||||
x: -20
|
|
||||||
},
|
|
||||||
{
|
|
||||||
spriteKey: Species.FURFROU.toString(),
|
|
||||||
fileRoot: "pokemon",
|
|
||||||
hasShadow: true,
|
|
||||||
repeat: true,
|
|
||||||
x: 30
|
|
||||||
}
|
|
||||||
])
|
|
||||||
// .withHideIntroVisuals(false)
|
|
||||||
.withSceneWaveRangeRequirement(10, 100)
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Choose TMs
|
|
||||||
const modifiers = [];
|
|
||||||
let i = 0;
|
|
||||||
while (i < 4) {
|
|
||||||
// 2/2/1 weight on TM rarity
|
|
||||||
const roll = randSeedInt(5);
|
|
||||||
if (roll < 2) {
|
|
||||||
modifiers.push(modifierTypes.TM_COMMON);
|
|
||||||
} else if (roll < 4) {
|
|
||||||
modifiers.push(modifierTypes.TM_GREAT);
|
|
||||||
} else {
|
|
||||||
modifiers.push(modifierTypes.TM_ULTRA);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
setEncounterExp(scene, scene.getParty().map(p => p.id), 300);
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false });
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Choose Vitamins
|
|
||||||
const modifiers = [];
|
|
||||||
let i = 0;
|
|
||||||
while (i < 3) {
|
|
||||||
// 2/1 weight on base stat booster vs PP Up
|
|
||||||
const roll = randSeedInt(3);
|
|
||||||
if (roll === 0) {
|
|
||||||
modifiers.push(modifierTypes.PP_UP);
|
|
||||||
} else {
|
|
||||||
modifiers.push(modifierTypes.BASE_STAT_BOOSTER);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false });
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Choose X Items
|
|
||||||
const modifiers = [];
|
|
||||||
let i = 0;
|
|
||||||
while (i < 5) {
|
|
||||||
// 4/1 weight on base stat booster vs Dire Hit
|
|
||||||
const roll = randSeedInt(5);
|
|
||||||
if (roll === 0) {
|
|
||||||
modifiers.push(modifierTypes.DIRE_HIT);
|
|
||||||
} else {
|
|
||||||
modifiers.push(modifierTypes.TEMP_STAT_BOOSTER);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false });
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Choose Pokeballs
|
|
||||||
const modifiers = [];
|
|
||||||
let i = 0;
|
|
||||||
while (i < 4) {
|
|
||||||
// 10/30/20/5 weight on pokeballs
|
|
||||||
const roll = randSeedInt(65);
|
|
||||||
if (roll < 10) {
|
|
||||||
modifiers.push(modifierTypes.POKEBALL);
|
|
||||||
} else if (roll < 40) {
|
|
||||||
modifiers.push(modifierTypes.GREAT_BALL);
|
|
||||||
} else if (roll < 60) {
|
|
||||||
modifiers.push(modifierTypes.ULTRA_BALL);
|
|
||||||
} else {
|
|
||||||
modifiers.push(modifierTypes.ROGUE_BALL);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false });
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
})
|
|
||||||
.build();
|
|
|
@ -0,0 +1,284 @@
|
||||||
|
import { MoveCategory } from "#app/data/move";
|
||||||
|
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import {
|
||||||
|
generateModifierTypeOption,
|
||||||
|
leaveEncounterWithoutBattle,
|
||||||
|
selectPokemonForOption,
|
||||||
|
setEncounterExp,
|
||||||
|
setEncounterRewards,
|
||||||
|
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||||
|
import { TempBattleStat } from "#app/data/temp-battle-stat";
|
||||||
|
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "../../../battle-scene";
|
||||||
|
import IMysteryEncounter, {
|
||||||
|
MysteryEncounterBuilder,
|
||||||
|
MysteryEncounterTier,
|
||||||
|
} from "../mystery-encounter";
|
||||||
|
|
||||||
|
/** i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:field_trip";
|
||||||
|
|
||||||
|
export const FieldTripEncounter: IMysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.FIELD_TRIP)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "preschooler_m",
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "teacher",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "preschooler_f",
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}_intro_message`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}_intro_dialogue`,
|
||||||
|
speaker: `${namespace}_speaker`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withHideIntroVisuals(false)
|
||||||
|
.withTitle(`${namespace}_title`)
|
||||||
|
.withDescription(`${namespace}_description`)
|
||||||
|
.withQuery(`${namespace}_query`)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}_option_1_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_1_tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}_second_option_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Return the options for Pokemon move valid for this option
|
||||||
|
return pokemon.moveset.map((move: PokemonMove) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: move.getName(),
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and move selected
|
||||||
|
const correctMove = move.getMove().category === MoveCategory.PHYSICAL;
|
||||||
|
encounter.setDialogueToken("moveCategory", "Physical");
|
||||||
|
if (!correctMove) {
|
||||||
|
encounter.options[0].dialogue.selected = [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_incorrect`,
|
||||||
|
speaker: `${namespace}_speaker`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}_lesson_learned`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||||
|
} else {
|
||||||
|
encounter.setDialogueToken("pokeName", pokemon.name);
|
||||||
|
encounter.setDialogueToken("move", move.getName());
|
||||||
|
encounter.options[0].dialogue.selected = [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setEncounterExp(scene, [pokemon.id], 100);
|
||||||
|
}
|
||||||
|
encounter.misc = {
|
||||||
|
correctMove: correctMove,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
if (encounter.misc.correctMove) {
|
||||||
|
const modifiers = [
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.ATK]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.DEF]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT),
|
||||||
|
];
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}_option_2_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_2_tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}_second_option_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Return the options for Pokemon move valid for this option
|
||||||
|
return pokemon.moveset.map((move: PokemonMove) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: move.getName(),
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and move selected
|
||||||
|
const correctMove = move.getMove().category === MoveCategory.SPECIAL;
|
||||||
|
encounter.setDialogueToken("moveCategory", "Special");
|
||||||
|
if (!correctMove) {
|
||||||
|
encounter.options[1].dialogue.selected = [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_incorrect`,
|
||||||
|
speaker: `${namespace}_speaker`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}_lesson_learned`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||||
|
} else {
|
||||||
|
encounter.setDialogueToken("pokeName", pokemon.name);
|
||||||
|
encounter.setDialogueToken("move", move.getName());
|
||||||
|
encounter.options[1].dialogue.selected = [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setEncounterExp(scene, [pokemon.id], 100);
|
||||||
|
}
|
||||||
|
encounter.misc = {
|
||||||
|
correctMove: correctMove,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
if (encounter.misc.correctMove) {
|
||||||
|
const modifiers = [
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPATK]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPDEF]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.DIRE_HIT),
|
||||||
|
];
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}_option_3_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_3_tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}_second_option_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Return the options for Pokemon move valid for this option
|
||||||
|
return pokemon.moveset.map((move: PokemonMove) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: move.getName(),
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and move selected
|
||||||
|
const correctMove = move.getMove().category === MoveCategory.STATUS;
|
||||||
|
encounter.setDialogueToken("moveCategory", "Status");
|
||||||
|
if (!correctMove) {
|
||||||
|
encounter.options[2].dialogue.selected = [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_incorrect`,
|
||||||
|
speaker: `${namespace}_speaker`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}_lesson_learned`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setEncounterExp(
|
||||||
|
scene,
|
||||||
|
scene.getParty().map((p) => p.id),
|
||||||
|
50
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
encounter.setDialogueToken("pokeName", pokemon.name);
|
||||||
|
encounter.setDialogueToken("move", move.getName());
|
||||||
|
encounter.options[2].dialogue.selected = [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
setEncounterExp(scene, [pokemon.id], 100);
|
||||||
|
}
|
||||||
|
encounter.misc = {
|
||||||
|
correctMove: correctMove,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
if (encounter.misc.correctMove) {
|
||||||
|
const modifiers = [
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.ACC]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.TEMP_STAT_BOOSTER, [TempBattleStat.SPD]),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.GREAT_BALL),
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.IV_SCANNER),
|
||||||
|
];
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: modifiers, fillRemaining: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, !encounter.misc.correctMove);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -0,0 +1,189 @@
|
||||||
|
import { BattleStat } from "#app/data/battle-stat";
|
||||||
|
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import {
|
||||||
|
EnemyPartyConfig,
|
||||||
|
initBattleWithEnemyConfig,
|
||||||
|
leaveEncounterWithoutBattle,
|
||||||
|
queueEncounterMessage,
|
||||||
|
setEncounterRewards,
|
||||||
|
showEncounterText,
|
||||||
|
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||||
|
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
|
import Pokemon from "#app/field/pokemon";
|
||||||
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
|
import {
|
||||||
|
getPartyLuckValue,
|
||||||
|
getPlayerModifierTypeOptions,
|
||||||
|
ModifierPoolType,
|
||||||
|
ModifierTypeOption,
|
||||||
|
regenerateModifierPoolThresholds,
|
||||||
|
} from "#app/modifier/modifier-type";
|
||||||
|
import { StatChangePhase } from "#app/phases";
|
||||||
|
import { randSeedInt } from "#app/utils";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "../../../battle-scene";
|
||||||
|
import IMysteryEncounter, {
|
||||||
|
MysteryEncounterBuilder,
|
||||||
|
MysteryEncounterTier,
|
||||||
|
} from "../mystery-encounter";
|
||||||
|
import { MoveRequirement } from "../mystery-encounter-requirements";
|
||||||
|
|
||||||
|
/** the i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:fight_or_flight";
|
||||||
|
|
||||||
|
export const FightOrFlightEncounter: IMysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(
|
||||||
|
MysteryEncounterType.FIGHT_OR_FLIGHT
|
||||||
|
)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
||||||
|
.withCatchAllowed(true)
|
||||||
|
.withHideWildIntroMessage(true)
|
||||||
|
.withIntroSpriteConfigs([]) // Set in onInit()
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}_intro_message`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
|
||||||
|
// Calculate boss mon
|
||||||
|
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, scene.currentBattle.waveIndex, 0, getPartyLuckValue(scene.getParty()), true);
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
levelAdditiveMultiplier: 1,
|
||||||
|
pokemonConfigs: [{ species: bossSpecies, isBoss: true }],
|
||||||
|
};
|
||||||
|
encounter.enemyPartyConfigs = [config];
|
||||||
|
|
||||||
|
// Calculate item
|
||||||
|
// 10-60 GREAT, 60-110 ULTRA, 110-160 ROGUE, 160-180 MASTER
|
||||||
|
const tier =
|
||||||
|
scene.currentBattle.waveIndex > 160
|
||||||
|
? ModifierTier.MASTER
|
||||||
|
: scene.currentBattle.waveIndex > 110
|
||||||
|
? ModifierTier.ROGUE
|
||||||
|
: scene.currentBattle.waveIndex > 60
|
||||||
|
? ModifierTier.ULTRA
|
||||||
|
: ModifierTier.GREAT;
|
||||||
|
regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0);
|
||||||
|
const item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier] })[0];
|
||||||
|
encounter.setDialogueToken("itemName", item.type.name);
|
||||||
|
encounter.misc = item;
|
||||||
|
|
||||||
|
const bossSpriteKey = bossSpecies.getSpriteId(false, bossSpecies.forms ? 0 : null, false, bossSpecies.hasVariants() ? 0 : null);
|
||||||
|
encounter.spriteConfigs = [
|
||||||
|
{
|
||||||
|
spriteKey: item.type.iconImage,
|
||||||
|
fileRoot: "items",
|
||||||
|
hasShadow: false,
|
||||||
|
x: 35,
|
||||||
|
y: -5,
|
||||||
|
scale: 0.75,
|
||||||
|
isItem: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: bossSpriteKey,
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: true,
|
||||||
|
tint: 0.25,
|
||||||
|
x: -5,
|
||||||
|
repeat: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// If player has a stealing move, they succeed automatically
|
||||||
|
encounter.options[1].meetsRequirements(scene);
|
||||||
|
const primaryPokemon = encounter.options[1].primaryPokemon;
|
||||||
|
if (primaryPokemon) {
|
||||||
|
// Use primaryPokemon to execute the thievery
|
||||||
|
encounter.options[1].dialogue.buttonTooltip = `${namespace}_option_2_steal_tooltip`;
|
||||||
|
} else {
|
||||||
|
encounter.options[1].dialogue.buttonTooltip = `${namespace}_option_2_tooltip`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withTitle(`${namespace}_title`)
|
||||||
|
.withDescription(`${namespace}_description`)
|
||||||
|
.withQuery(`${namespace}_query`)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}_option_1_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_1_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_1_selected_message`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Pick battle
|
||||||
|
const item = scene.currentBattle.mysteryEncounter
|
||||||
|
.misc as ModifierTypeOption;
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
|
||||||
|
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DEFAULT_OR_SPECIAL)
|
||||||
|
.withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES)) // Will set option2PrimaryName and option2PrimaryMove dialogue tokens automatically
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}_option_2_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_2_tooltip`,
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Pick steal
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption;
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
|
||||||
|
|
||||||
|
// If player has a stealing move, they succeed automatically
|
||||||
|
const primaryPokemon = encounter.options[1].primaryPokemon;
|
||||||
|
if (primaryPokemon) {
|
||||||
|
// Use primaryPokemon to execute the thievery
|
||||||
|
await showEncounterText(scene, `${namespace}_option_2_steal_result`);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const roll = randSeedInt(16);
|
||||||
|
if (roll > 6) {
|
||||||
|
// Noticed and attacked by boss, gets +1 to all stats at start of fight (62.5%)
|
||||||
|
const config = scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0];
|
||||||
|
config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
|
||||||
|
config.pokemonConfigs[0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
|
||||||
|
pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken("enemyPokemon", pokemon.name);
|
||||||
|
queueEncounterMessage(pokemon.scene, `${namespace}_boss_enraged`);
|
||||||
|
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1));
|
||||||
|
};
|
||||||
|
await showEncounterText(scene, `${namespace}_option_2_bad_result`);
|
||||||
|
await initBattleWithEnemyConfig(scene, config);
|
||||||
|
} else {
|
||||||
|
// Steal item (37.5%)
|
||||||
|
// Display result message then proceed to rewards
|
||||||
|
await showEncounterText(scene, `${namespace}_option_2_good_result`);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}_option_3_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_3_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_3_selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Leave encounter with no rewards or exp
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -1,150 +0,0 @@
|
||||||
import { BattleStat } from "#app/data/battle-stat";
|
|
||||||
import {
|
|
||||||
EnemyPartyConfig,
|
|
||||||
initBattleWithEnemyConfig,
|
|
||||||
leaveEncounterWithoutBattle, queueEncounterMessage,
|
|
||||||
setEncounterRewards,
|
|
||||||
showEncounterText
|
|
||||||
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
|
||||||
import Pokemon from "#app/field/pokemon";
|
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
|
||||||
import {
|
|
||||||
getPartyLuckValue,
|
|
||||||
getPlayerModifierTypeOptions,
|
|
||||||
ModifierPoolType,
|
|
||||||
ModifierTypeOption,
|
|
||||||
regenerateModifierPoolThresholds
|
|
||||||
} from "#app/modifier/modifier-type";
|
|
||||||
import { StatChangePhase } from "#app/phases";
|
|
||||||
import { TextStyle } from "#app/ui/text";
|
|
||||||
import { randSeedInt } from "#app/utils";
|
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
|
||||||
import { Moves } from "#enums/moves";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import BattleScene from "../../../battle-scene";
|
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter";
|
|
||||||
import { MoveRequirement } from "../mystery-encounter-requirements";
|
|
||||||
|
|
||||||
const validMovesForSteal = [
|
|
||||||
Moves.PLUCK,
|
|
||||||
Moves.COVET,
|
|
||||||
Moves.FAKE_OUT,
|
|
||||||
Moves.THIEF,
|
|
||||||
Moves.TRICK,
|
|
||||||
Moves.SWITCHEROO,
|
|
||||||
Moves.GIGA_DRAIN
|
|
||||||
];
|
|
||||||
|
|
||||||
export const FightOrFlightEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|
||||||
.withEncounterType(MysteryEncounterType.FIGHT_OR_FLIGHT)
|
|
||||||
.withEncounterTier(MysteryEncounterTier.COMMON)
|
|
||||||
.withIntroSpriteConfigs([]) // Set in onInit()
|
|
||||||
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
|
||||||
.withCatchAllowed(true)
|
|
||||||
.withHideWildIntroMessage(true)
|
|
||||||
.withOnInit((scene: BattleScene) => {
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
|
|
||||||
// Calculate boss mon
|
|
||||||
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, scene.currentBattle.waveIndex, 0, getPartyLuckValue(scene.getParty()), true);
|
|
||||||
const config: EnemyPartyConfig = {
|
|
||||||
levelAdditiveMultiplier: 1,
|
|
||||||
pokemonConfigs: [{ species: bossSpecies, isBoss: true }]
|
|
||||||
};
|
|
||||||
encounter.enemyPartyConfigs = [config];
|
|
||||||
|
|
||||||
// Calculate item
|
|
||||||
// 10-60 GREAT, 60-110 ULTRA, 110-160 ROGUE, 160-180 MASTER
|
|
||||||
const tier = scene.currentBattle.waveIndex > 160 ? ModifierTier.MASTER : scene.currentBattle.waveIndex > 110 ? ModifierTier.ROGUE : scene.currentBattle.waveIndex > 60 ? ModifierTier.ULTRA : ModifierTier.GREAT;
|
|
||||||
regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0); // refresh player item pool
|
|
||||||
const item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier] })[0];
|
|
||||||
encounter.setDialogueToken("itemName", item.type.name);
|
|
||||||
encounter.misc = item;
|
|
||||||
|
|
||||||
encounter.spriteConfigs = [
|
|
||||||
{
|
|
||||||
spriteKey: item.type.iconImage,
|
|
||||||
fileRoot: "items",
|
|
||||||
hasShadow: false,
|
|
||||||
x: 35,
|
|
||||||
y: -5,
|
|
||||||
scale: 0.75,
|
|
||||||
isItem: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
spriteKey: bossSpecies.speciesId.toString(),
|
|
||||||
fileRoot: "pokemon",
|
|
||||||
hasShadow: true,
|
|
||||||
tint: 0.25,
|
|
||||||
x: -5,
|
|
||||||
repeat: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
// If player has a stealing move, they succeed automatically
|
|
||||||
const moveRequirement = new MoveRequirement(validMovesForSteal);
|
|
||||||
const validPokemon = moveRequirement.queryParty(scene.getParty());
|
|
||||||
if (validPokemon?.length > 0) {
|
|
||||||
// Use first valid pokemon to execute the theivery
|
|
||||||
const pokemon = validPokemon[0];
|
|
||||||
encounter.setDialogueToken("thiefPokemon", pokemon.name);
|
|
||||||
encounter.setDialogueToken(...moveRequirement.getDialogueToken(scene, pokemon));
|
|
||||||
encounter.dialogue.encounterOptionsDialogue.options[1].buttonTooltip = "mysteryEncounter:fight_or_flight_option_2_steal_tooltip";
|
|
||||||
encounter.dialogue.encounterOptionsDialogue.options[1].style = TextStyle.SUMMARY_GREEN;
|
|
||||||
} else {
|
|
||||||
encounter.dialogue.encounterOptionsDialogue.options[1].buttonTooltip = "mysteryEncounter:fight_or_flight_option_2_tooltip";
|
|
||||||
encounter.dialogue.encounterOptionsDialogue.options[1].style = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Pick battle
|
|
||||||
const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption;
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
|
|
||||||
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]);
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Pick steal
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption;
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
|
|
||||||
|
|
||||||
// If player has a stealing move, they succeed automatically
|
|
||||||
const moveRequirement = new MoveRequirement(validMovesForSteal);
|
|
||||||
const validPokemon = moveRequirement.queryParty(scene.getParty());
|
|
||||||
if (validPokemon?.length > 0) {
|
|
||||||
// Use first valid pokemon to execute the theivery
|
|
||||||
const pokemon = validPokemon[0];
|
|
||||||
encounter.setDialogueToken("thiefPokemon", pokemon.name);
|
|
||||||
encounter.setDialogueToken(...moveRequirement.getDialogueToken(scene, pokemon));
|
|
||||||
await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_steal_result");
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const roll = randSeedInt(16);
|
|
||||||
if (roll > 6) {
|
|
||||||
// Noticed and attacked by boss, gets +1 to all stats at start of fight (62.5%)
|
|
||||||
const config = scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0];
|
|
||||||
config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
|
|
||||||
config.pokemonConfigs[0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
|
|
||||||
pokemon.scene.currentBattle.mysteryEncounter.setDialogueToken("enemyPokemon", pokemon.name);
|
|
||||||
queueEncounterMessage(pokemon.scene, "mysteryEncounter:fight_or_flight_boss_enraged");
|
|
||||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1));
|
|
||||||
};
|
|
||||||
await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_bad_result");
|
|
||||||
await initBattleWithEnemyConfig(scene, config);
|
|
||||||
} else {
|
|
||||||
// Steal item (37.5%)
|
|
||||||
// Display result message then proceed to rewards
|
|
||||||
await showEncounterText(scene, "mysteryEncounter:fight_or_flight_option_2_good_result");
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Leave encounter with no rewards or exp
|
|
||||||
leaveEncounterWithoutBattle(scene, true);
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.build();
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
import {
|
||||||
|
EnemyPartyConfig,
|
||||||
|
initBattleWithEnemyConfig,
|
||||||
|
setEncounterRewards,
|
||||||
|
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||||
|
import {
|
||||||
|
trainerConfigs,
|
||||||
|
TrainerPartyCompoundTemplate,
|
||||||
|
TrainerPartyTemplate,
|
||||||
|
trainerPartyTemplates,
|
||||||
|
} from "#app/data/trainer-config";
|
||||||
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||||
|
import BattleScene from "../../../battle-scene";
|
||||||
|
import * as Utils from "../../../utils";
|
||||||
|
import IMysteryEncounter, {
|
||||||
|
MysteryEncounterBuilder,
|
||||||
|
MysteryEncounterTier,
|
||||||
|
} from "../mystery-encounter";
|
||||||
|
|
||||||
|
/** the i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:mysterious_challengers";
|
||||||
|
|
||||||
|
export const MysteriousChallengersEncounter: IMysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(
|
||||||
|
MysteryEncounterType.MYSTERIOUS_CHALLENGERS
|
||||||
|
)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
||||||
|
.withIntroSpriteConfigs([]) // These are set in onInit()
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}_intro_message`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Calculates what trainers are available for battle in the encounter
|
||||||
|
|
||||||
|
// Normal difficulty trainer is randomly pulled from biome
|
||||||
|
const normalTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex);
|
||||||
|
const normalConfig = trainerConfigs[normalTrainerType].copy();
|
||||||
|
let female = false;
|
||||||
|
if (normalConfig.hasGenders) {
|
||||||
|
female = !!Utils.randSeedInt(2);
|
||||||
|
}
|
||||||
|
const normalSpriteKey = normalConfig.getSpriteKey(female, normalConfig.doubleOnly);
|
||||||
|
encounter.enemyPartyConfigs.push({
|
||||||
|
trainerConfig: normalConfig,
|
||||||
|
female: female,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config
|
||||||
|
// Number of mons is based off wave: 1-20 is 2, 20-40 is 3, etc. capping at 6 after wave 100
|
||||||
|
const hardTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex);
|
||||||
|
const hardTemplate = new TrainerPartyCompoundTemplate(
|
||||||
|
new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, false, true),
|
||||||
|
new TrainerPartyTemplate(
|
||||||
|
Math.min(Math.ceil(scene.currentBattle.waveIndex / 20), 5),
|
||||||
|
PartyMemberStrength.AVERAGE,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const hardConfig = trainerConfigs[hardTrainerType].copy();
|
||||||
|
hardConfig.setPartyTemplates(hardTemplate);
|
||||||
|
female = false;
|
||||||
|
if (hardConfig.hasGenders) {
|
||||||
|
female = !!Utils.randSeedInt(2);
|
||||||
|
}
|
||||||
|
const hardSpriteKey = hardConfig.getSpriteKey(female, hardConfig.doubleOnly);
|
||||||
|
encounter.enemyPartyConfigs.push({
|
||||||
|
trainerConfig: hardConfig,
|
||||||
|
levelAdditiveMultiplier: 0.5,
|
||||||
|
female: female,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Brutal trainer is pulled from pool of boss trainers (gym leaders) for the biome
|
||||||
|
// They are given an E4 template team, so will be stronger than usual boss encounter and always have 6 mons
|
||||||
|
const brutalTrainerType = scene.arena.randomTrainerType(
|
||||||
|
scene.currentBattle.waveIndex,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
const e4Template = trainerPartyTemplates.ELITE_FOUR;
|
||||||
|
const brutalConfig = trainerConfigs[brutalTrainerType].copy();
|
||||||
|
brutalConfig.setPartyTemplates(e4Template);
|
||||||
|
brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func
|
||||||
|
female = false;
|
||||||
|
if (brutalConfig.hasGenders) {
|
||||||
|
female = !!Utils.randSeedInt(2);
|
||||||
|
}
|
||||||
|
const brutalSpriteKey = brutalConfig.getSpriteKey(female, brutalConfig.doubleOnly);
|
||||||
|
encounter.enemyPartyConfigs.push({
|
||||||
|
trainerConfig: brutalConfig,
|
||||||
|
levelAdditiveMultiplier: 1.1,
|
||||||
|
female: female,
|
||||||
|
});
|
||||||
|
|
||||||
|
encounter.spriteConfigs = [
|
||||||
|
{
|
||||||
|
spriteKey: normalSpriteKey,
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
tint: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: hardSpriteKey,
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
tint: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: brutalSpriteKey,
|
||||||
|
fileRoot: "trainer",
|
||||||
|
hasShadow: true,
|
||||||
|
tint: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withTitle(`${namespace}_title`)
|
||||||
|
.withDescription(`${namespace}_description`)
|
||||||
|
.withQuery(`${namespace}_query`)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}_option_1_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_1_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected_message`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Spawn standard trainer battle with memory mushroom reward
|
||||||
|
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM], fillRemaining: true });
|
||||||
|
|
||||||
|
// Seed offsets to remove possibility of different trainers having exact same teams
|
||||||
|
let ret;
|
||||||
|
scene.executeWithSeedOffset(() => {
|
||||||
|
ret = initBattleWithEnemyConfig(scene, config);
|
||||||
|
}, scene.currentBattle.waveIndex * 10);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}_option_2_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_2_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected_message`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Spawn hard fight with ULTRA/GREAT reward (can improve with luck)
|
||||||
|
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1];
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], fillRemaining: true });
|
||||||
|
|
||||||
|
// Seed offsets to remove possibility of different trainers having exact same teams
|
||||||
|
let ret;
|
||||||
|
scene.executeWithSeedOffset(() => {
|
||||||
|
ret = initBattleWithEnemyConfig(scene, config);
|
||||||
|
}, scene.currentBattle.waveIndex * 100);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}_option_3_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_3_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected_message`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
// Spawn brutal fight with ROGUE/ULTRA/GREAT reward (can improve with luck)
|
||||||
|
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[2];
|
||||||
|
|
||||||
|
// To avoid player level snowballing from picking this option
|
||||||
|
encounter.expMultiplier = 0.9;
|
||||||
|
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT], fillRemaining: true });
|
||||||
|
|
||||||
|
// Seed offsets to remove possibility of different trainers having exact same teams
|
||||||
|
let ret;
|
||||||
|
scene.executeWithSeedOffset(() => {
|
||||||
|
ret = initBattleWithEnemyConfig(scene, config);
|
||||||
|
}, scene.currentBattle.waveIndex * 1000);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOutroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}_outro_win`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.build();
|
|
@ -1,143 +0,0 @@
|
||||||
import { EnemyPartyConfig, initBattleWithEnemyConfig, setEncounterRewards } from "#app/data/mystery-encounters/mystery-encounter-utils";
|
|
||||||
import {
|
|
||||||
trainerConfigs,
|
|
||||||
TrainerPartyCompoundTemplate,
|
|
||||||
TrainerPartyTemplate,
|
|
||||||
trainerPartyTemplates
|
|
||||||
} from "#app/data/trainer-config";
|
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
|
||||||
import BattleScene from "../../../battle-scene";
|
|
||||||
import * as Utils from "../../../utils";
|
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter";
|
|
||||||
|
|
||||||
export const MysteriousChallengersEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|
||||||
.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHALLENGERS)
|
|
||||||
.withEncounterTier(MysteryEncounterTier.GREAT)
|
|
||||||
.withIntroSpriteConfigs([]) // These are set in onInit()
|
|
||||||
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
|
||||||
.withOnInit((scene: BattleScene) => {
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
// Calculates what trainers are available for battle in the encounter
|
|
||||||
|
|
||||||
// Normal difficulty trainer is randomly pulled from biome
|
|
||||||
const normalTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex);
|
|
||||||
const normalConfig = trainerConfigs[normalTrainerType].copy();
|
|
||||||
let female = false;
|
|
||||||
if (normalConfig.hasGenders) {
|
|
||||||
female = !!(Utils.randSeedInt(2));
|
|
||||||
}
|
|
||||||
const normalSpriteKey = normalConfig.getSpriteKey(female, normalConfig.doubleOnly);
|
|
||||||
encounter.enemyPartyConfigs.push({
|
|
||||||
trainerConfig: normalConfig,
|
|
||||||
female: female
|
|
||||||
});
|
|
||||||
|
|
||||||
// Hard difficulty trainer is another random trainer, but with AVERAGE_BALANCED config
|
|
||||||
// Number of mons is based off wave: 1-20 is 2, 20-40 is 3, etc. capping at 6 after wave 100
|
|
||||||
const hardTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex);
|
|
||||||
const hardTemplate = new TrainerPartyCompoundTemplate(
|
|
||||||
new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER, false, true),
|
|
||||||
new TrainerPartyTemplate(Math.min(Math.ceil(scene.currentBattle.waveIndex / 20), 5), PartyMemberStrength.AVERAGE, false, true));
|
|
||||||
const hardConfig = trainerConfigs[hardTrainerType].copy();
|
|
||||||
hardConfig.setPartyTemplates(hardTemplate);
|
|
||||||
female = false;
|
|
||||||
if (hardConfig.hasGenders) {
|
|
||||||
female = !!(Utils.randSeedInt(2));
|
|
||||||
}
|
|
||||||
const hardSpriteKey = hardConfig.getSpriteKey(female, hardConfig.doubleOnly);
|
|
||||||
encounter.enemyPartyConfigs.push({
|
|
||||||
trainerConfig: hardConfig,
|
|
||||||
levelAdditiveMultiplier: 0.5,
|
|
||||||
female: female,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Brutal trainer is pulled from pool of boss trainers (gym leaders) for the biome
|
|
||||||
// They are given an E4 template team, so will be stronger than usual boss encounter and always have 6 mons
|
|
||||||
const brutalTrainerType = scene.arena.randomTrainerType(scene.currentBattle.waveIndex, true);
|
|
||||||
const e4Template = trainerPartyTemplates.ELITE_FOUR;
|
|
||||||
const brutalConfig = trainerConfigs[brutalTrainerType].copy();
|
|
||||||
brutalConfig.setPartyTemplates(e4Template);
|
|
||||||
brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func
|
|
||||||
female = false;
|
|
||||||
if (brutalConfig.hasGenders) {
|
|
||||||
female = !!(Utils.randSeedInt(2));
|
|
||||||
}
|
|
||||||
const brutalSpriteKey = brutalConfig.getSpriteKey(female, brutalConfig.doubleOnly);
|
|
||||||
encounter.enemyPartyConfigs.push({
|
|
||||||
trainerConfig: brutalConfig,
|
|
||||||
levelAdditiveMultiplier: 1.1,
|
|
||||||
female: female
|
|
||||||
});
|
|
||||||
|
|
||||||
encounter.spriteConfigs = [
|
|
||||||
{
|
|
||||||
spriteKey: normalSpriteKey,
|
|
||||||
fileRoot: "trainer",
|
|
||||||
hasShadow: true,
|
|
||||||
tint: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
spriteKey: hardSpriteKey,
|
|
||||||
fileRoot: "trainer",
|
|
||||||
hasShadow: true,
|
|
||||||
tint: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
spriteKey: brutalSpriteKey,
|
|
||||||
fileRoot: "trainer",
|
|
||||||
hasShadow: true,
|
|
||||||
tint: 1
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
// Spawn standard trainer battle with memory mushroom reward
|
|
||||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
|
||||||
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM], fillRemaining: true });
|
|
||||||
|
|
||||||
// Seed offsets to remove possibility of different trainers having exact same teams
|
|
||||||
let ret;
|
|
||||||
scene.executeWithSeedOffset(() => {
|
|
||||||
ret = initBattleWithEnemyConfig(scene, config);
|
|
||||||
}, scene.currentBattle.waveIndex * 10);
|
|
||||||
return ret;
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
// Spawn hard fight with ULTRA/GREAT reward (can improve with luck)
|
|
||||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1];
|
|
||||||
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], fillRemaining: true });
|
|
||||||
|
|
||||||
// Seed offsets to remove possibility of different trainers having exact same teams
|
|
||||||
let ret;
|
|
||||||
scene.executeWithSeedOffset(() => {
|
|
||||||
ret = initBattleWithEnemyConfig(scene, config);
|
|
||||||
}, scene.currentBattle.waveIndex * 100);
|
|
||||||
return ret;
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
// Spawn brutal fight with ROGUE/ULTRA/GREAT reward (can improve with luck)
|
|
||||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[2];
|
|
||||||
|
|
||||||
// To avoid player level snowballing from picking this option
|
|
||||||
encounter.expMultiplier = 0.9;
|
|
||||||
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT], fillRemaining: true });
|
|
||||||
|
|
||||||
// Seed offsets to remove possibility of different trainers having exact same teams
|
|
||||||
let ret;
|
|
||||||
scene.executeWithSeedOffset(() => {
|
|
||||||
ret = initBattleWithEnemyConfig(scene, config);
|
|
||||||
}, scene.currentBattle.waveIndex * 1000);
|
|
||||||
return ret;
|
|
||||||
})
|
|
||||||
.build();
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
import {
|
||||||
|
getHighestLevelPlayerPokemon,
|
||||||
|
koPlayerPokemon,
|
||||||
|
leaveEncounterWithoutBattle,
|
||||||
|
queueEncounterMessage,
|
||||||
|
setEncounterRewards,
|
||||||
|
showEncounterText,
|
||||||
|
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||||
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
|
import { GameOverPhase } from "#app/phases";
|
||||||
|
import { randSeedInt } from "#app/utils";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "../../../battle-scene";
|
||||||
|
import IMysteryEncounter, {
|
||||||
|
MysteryEncounterBuilder,
|
||||||
|
MysteryEncounterTier,
|
||||||
|
} from "../mystery-encounter";
|
||||||
|
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
|
||||||
|
export const MysteriousChestEncounter: IMysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(
|
||||||
|
MysteryEncounterType.MYSTERIOUS_CHEST
|
||||||
|
)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180) // waves 2 to 180
|
||||||
|
.withHideIntroVisuals(false)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "chest_blue",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
x: 4,
|
||||||
|
y: 8,
|
||||||
|
disableAnimation: true, // Re-enabled after option select
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: "mysteryEncounter:mysterious_chest_intro_message",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withTitle("mysteryEncounter:mysterious_chest_title")
|
||||||
|
.withDescription("mysteryEncounter:mysterious_chest_description")
|
||||||
|
.withQuery("mysteryEncounter:mysterious_chest_query")
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: "mysteryEncounter:mysterious_chest_option_1_label",
|
||||||
|
buttonTooltip: "mysteryEncounter:mysterious_chest_option_1_tooltip",
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: "mysteryEncounter:mysterious_chest_option_1_selected_message",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Play animation
|
||||||
|
const introVisuals =
|
||||||
|
scene.currentBattle.mysteryEncounter.introVisuals;
|
||||||
|
introVisuals.spriteConfigs[0].disableAnimation = false;
|
||||||
|
introVisuals.playAnim();
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Open the chest
|
||||||
|
const roll = randSeedInt(100);
|
||||||
|
if (roll > 60) {
|
||||||
|
// Choose between 2 COMMON / 2 GREAT tier items (40%)
|
||||||
|
setEncounterRewards(scene, {
|
||||||
|
guaranteedModifierTiers: [
|
||||||
|
ModifierTier.COMMON,
|
||||||
|
ModifierTier.COMMON,
|
||||||
|
ModifierTier.GREAT,
|
||||||
|
ModifierTier.GREAT,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
// Display result message then proceed to rewards
|
||||||
|
queueEncounterMessage(
|
||||||
|
scene,
|
||||||
|
"mysteryEncounter:mysterious_chest_option_1_normal_result"
|
||||||
|
);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
} else if (roll > 40) {
|
||||||
|
// Choose between 3 ULTRA tier items (20%)
|
||||||
|
setEncounterRewards(scene, {
|
||||||
|
guaranteedModifierTiers: [
|
||||||
|
ModifierTier.ULTRA,
|
||||||
|
ModifierTier.ULTRA,
|
||||||
|
ModifierTier.ULTRA,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
// Display result message then proceed to rewards
|
||||||
|
queueEncounterMessage(
|
||||||
|
scene,
|
||||||
|
"mysteryEncounter:mysterious_chest_option_1_good_result"
|
||||||
|
);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
} else if (roll > 36) {
|
||||||
|
// Choose between 2 ROGUE tier items (4%)
|
||||||
|
setEncounterRewards(scene, {
|
||||||
|
guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE],
|
||||||
|
});
|
||||||
|
// Display result message then proceed to rewards
|
||||||
|
queueEncounterMessage(
|
||||||
|
scene,
|
||||||
|
"mysteryEncounter:mysterious_chest_option_1_great_result"
|
||||||
|
);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
} else if (roll > 35) {
|
||||||
|
// Choose 1 MASTER tier item (1%)
|
||||||
|
setEncounterRewards(scene, {
|
||||||
|
guaranteedModifierTiers: [ModifierTier.MASTER],
|
||||||
|
});
|
||||||
|
// Display result message then proceed to rewards
|
||||||
|
queueEncounterMessage(
|
||||||
|
scene,
|
||||||
|
"mysteryEncounter:mysterious_chest_option_1_amazing_result"
|
||||||
|
);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
} else {
|
||||||
|
// Your highest level unfainted Pok<6F>mon gets OHKO. Progress with no rewards (35%)
|
||||||
|
const highestLevelPokemon = getHighestLevelPlayerPokemon(
|
||||||
|
scene,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
koPlayerPokemon(highestLevelPokemon);
|
||||||
|
|
||||||
|
scene.currentBattle.mysteryEncounter.setDialogueToken(
|
||||||
|
"pokeName",
|
||||||
|
highestLevelPokemon.name
|
||||||
|
);
|
||||||
|
// Show which Pokemon was KOed, then leave encounter with no rewards
|
||||||
|
// Does this synchronously so that game over doesn't happen over result message
|
||||||
|
await showEncounterText(
|
||||||
|
scene,
|
||||||
|
"mysteryEncounter:mysterious_chest_option_1_bad_result"
|
||||||
|
).then(() => {
|
||||||
|
if (
|
||||||
|
scene.getParty().filter((p) => p.isAllowedInBattle()).length ===
|
||||||
|
0
|
||||||
|
) {
|
||||||
|
// All pokemon fainted, game over
|
||||||
|
scene.clearPhaseQueue();
|
||||||
|
scene.unshiftPhase(new GameOverPhase(scene));
|
||||||
|
} else {
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: "mysteryEncounter:mysterious_chest_option_2_label",
|
||||||
|
buttonTooltip: "mysteryEncounter:mysterious_chest_option_2_tooltip",
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: "mysteryEncounter:mysterious_chest_option_2_selected_message",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Leave encounter with no rewards or exp
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -1,93 +0,0 @@
|
||||||
import {
|
|
||||||
getHighestLevelPlayerPokemon,
|
|
||||||
koPlayerPokemon,
|
|
||||||
leaveEncounterWithoutBattle,
|
|
||||||
queueEncounterMessage,
|
|
||||||
setEncounterRewards,
|
|
||||||
showEncounterText
|
|
||||||
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
|
||||||
import { GameOverPhase } from "#app/phases";
|
|
||||||
import { randSeedInt } from "#app/utils";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import BattleScene from "../../../battle-scene";
|
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter";
|
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
|
||||||
|
|
||||||
export const MysteriousChestEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|
||||||
.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHEST)
|
|
||||||
.withEncounterTier(MysteryEncounterTier.COMMON)
|
|
||||||
.withIntroSpriteConfigs([
|
|
||||||
{
|
|
||||||
spriteKey: "chest_blue",
|
|
||||||
fileRoot: "mystery-encounters",
|
|
||||||
hasShadow: true,
|
|
||||||
x: 4,
|
|
||||||
y: 8,
|
|
||||||
disableAnimation: true // Re-enabled after option select
|
|
||||||
}
|
|
||||||
])
|
|
||||||
.withHideIntroVisuals(false)
|
|
||||||
.withSceneWaveRangeRequirement(10, 180) // waves 2 to 180
|
|
||||||
.withOption(new MysteryEncounterOptionBuilder()
|
|
||||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Play animation
|
|
||||||
const introVisuals = scene.currentBattle.mysteryEncounter.introVisuals;
|
|
||||||
introVisuals.spriteConfigs[0].disableAnimation = false;
|
|
||||||
introVisuals.playAnim();
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Open the chest
|
|
||||||
const roll = randSeedInt(100);
|
|
||||||
if (roll > 60) {
|
|
||||||
// Choose between 2 COMMON / 2 GREAT tier items (40%)
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.COMMON, ModifierTier.COMMON, ModifierTier.GREAT, ModifierTier.GREAT] });
|
|
||||||
// Display result message then proceed to rewards
|
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_normal_result");
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
} else if (roll > 40) {
|
|
||||||
// Choose between 3 ULTRA tier items (20%)
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA] });
|
|
||||||
// Display result message then proceed to rewards
|
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_good_result");
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
} else if (roll > 36) {
|
|
||||||
// Choose between 2 ROGUE tier items (4%)
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE] });
|
|
||||||
// Display result message then proceed to rewards
|
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_great_result");
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
} else if (roll > 35) {
|
|
||||||
// Choose 1 MASTER tier item (1%)
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.MASTER] });
|
|
||||||
// Display result message then proceed to rewards
|
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_amazing_result");
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
} else {
|
|
||||||
// Your highest level unfainted Pok<6F>mon gets OHKO. Progress with no rewards (35%)
|
|
||||||
const highestLevelPokemon = getHighestLevelPlayerPokemon(scene, true);
|
|
||||||
koPlayerPokemon(highestLevelPokemon);
|
|
||||||
|
|
||||||
scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", highestLevelPokemon.name);
|
|
||||||
// Show which Pokemon was KOed, then leave encounter with no rewards
|
|
||||||
// Does this synchronously so that game over doesn't happen over result message
|
|
||||||
await showEncounterText(scene, "mysteryEncounter:mysterious_chest_option_1_bad_result")
|
|
||||||
.then(() => {
|
|
||||||
if (scene.getParty().filter(p => p.isAllowedInBattle()).length === 0) {
|
|
||||||
// All pokemon fainted, game over
|
|
||||||
scene.clearPhaseQueue();
|
|
||||||
scene.unshiftPhase(new GameOverPhase(scene));
|
|
||||||
} else {
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Leave encounter with no rewards or exp
|
|
||||||
leaveEncounterWithoutBattle(scene, true);
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.build();
|
|
|
@ -0,0 +1,233 @@
|
||||||
|
import { generateModifierTypeOption, leaveEncounterWithoutBattle, queueEncounterMessage, selectPokemonForOption, setEncounterExp, updatePlayerMoney, } from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||||
|
import { StatusEffect } from "#app/data/status-effect";
|
||||||
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { randSeedInt } from "#app/utils";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import BattleScene from "../../../battle-scene";
|
||||||
|
import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter";
|
||||||
|
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { MoneyRequirement } from "../mystery-encounter-requirements";
|
||||||
|
|
||||||
|
/** the i18n namespace for this encounter */
|
||||||
|
const namespace = "mysteryEncounter:shady_vitamin_dealer";
|
||||||
|
|
||||||
|
export const ShadyVitaminDealerEncounter: IMysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(
|
||||||
|
MysteryEncounterType.SHADY_VITAMIN_DEALER
|
||||||
|
)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180)
|
||||||
|
.withPrimaryPokemonStatusEffectRequirement([StatusEffect.NONE]) // Pokemon must not have status
|
||||||
|
.withPrimaryPokemonHealthRatioRequirement([0.34, 1]) // Pokemon must have above 1/3rd HP
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: Species.KROOKODILE.toString(),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: true,
|
||||||
|
repeat: true,
|
||||||
|
x: 10,
|
||||||
|
y: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spriteKey: "b2w2_veteran_m",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
x: -10,
|
||||||
|
y: 2,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}_intro_message`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `${namespace}_intro_dialogue`,
|
||||||
|
speaker: `${namespace}_speaker`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}_title`)
|
||||||
|
.withDescription(`${namespace}_description`)
|
||||||
|
.withQuery(`${namespace}_query`)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withSceneMoneyRequirement(0, 2) // Wave scaling money multiplier of 2
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}_option_1_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_1_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Update money
|
||||||
|
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
|
||||||
|
// Calculate modifiers and dialogue tokens
|
||||||
|
const modifiers = [
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type,
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type,
|
||||||
|
];
|
||||||
|
encounter.setDialogueToken("boost1", modifiers[0].name);
|
||||||
|
encounter.setDialogueToken("boost2", modifiers[1].name);
|
||||||
|
encounter.misc = {
|
||||||
|
chosenPokemon: pokemon,
|
||||||
|
modifiers: modifiers,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
|
const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
|
if (!meetsReqs) {
|
||||||
|
return i18next.t(`${namespace}_invalid_selection`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Choose Cheap Option
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const chosenPokemon = encounter.misc.chosenPokemon;
|
||||||
|
const modifiers = encounter.misc.modifiers;
|
||||||
|
|
||||||
|
for (const modType of modifiers) {
|
||||||
|
const modifier = modType.newModifier(chosenPokemon);
|
||||||
|
await scene.addModifier(modifier, true, false, false, true);
|
||||||
|
}
|
||||||
|
scene.updateModifiers(true);
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
})
|
||||||
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Damage and status applied after dealer leaves (to make thematic sense)
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const chosenPokemon = encounter.misc.chosenPokemon;
|
||||||
|
|
||||||
|
// Pokemon takes 1/3 max HP damage
|
||||||
|
const damage = Math.round(chosenPokemon.getMaxHp() / 3);
|
||||||
|
chosenPokemon.hp = Math.max(chosenPokemon.hp - damage, 0);
|
||||||
|
|
||||||
|
// Roll for poison (80%)
|
||||||
|
if (randSeedInt(10) < 8) {
|
||||||
|
if (chosenPokemon.trySetStatus(StatusEffect.TOXIC)) {
|
||||||
|
// Toxic applied
|
||||||
|
queueEncounterMessage(scene, `${namespace}_bad_poison`);
|
||||||
|
} else {
|
||||||
|
// Pokemon immune or something else prevents status
|
||||||
|
queueEncounterMessage(scene, `${namespace}_damage_only`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
queueEncounterMessage(scene, `${namespace}_damage_only`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncounterExp(scene, [chosenPokemon.id], 100);
|
||||||
|
|
||||||
|
chosenPokemon.updateInfo();
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DISABLED_OR_DEFAULT)
|
||||||
|
.withSceneMoneyRequirement(0, 5) // Wave scaling money multiplier of 5
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}_option_2_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_2_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Update money
|
||||||
|
updatePlayerMoney(scene, -(encounter.options[1].requirements[0] as MoneyRequirement).requiredMoney);
|
||||||
|
// Calculate modifiers and dialogue tokens
|
||||||
|
const modifiers = [
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type,
|
||||||
|
generateModifierTypeOption(scene, modifierTypes.BASE_STAT_BOOSTER).type,
|
||||||
|
];
|
||||||
|
encounter.setDialogueToken("boost1", modifiers[0].name);
|
||||||
|
encounter.setDialogueToken("boost2", modifiers[1].name);
|
||||||
|
encounter.misc = {
|
||||||
|
chosenPokemon: pokemon,
|
||||||
|
modifiers: modifiers,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
|
const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
|
if (!meetsReqs) {
|
||||||
|
return i18next.t(`${namespace}_invalid_selection`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Choose Expensive Option
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const chosenPokemon = encounter.misc.chosenPokemon;
|
||||||
|
const modifiers = encounter.misc.modifiers;
|
||||||
|
|
||||||
|
for (const modType of modifiers) {
|
||||||
|
const modifier = modType.newModifier(chosenPokemon);
|
||||||
|
await scene.addModifier(modifier, true, false, false, true);
|
||||||
|
}
|
||||||
|
scene.updateModifiers(true);
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
})
|
||||||
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Status applied after dealer leaves (to make thematic sense)
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const chosenPokemon = encounter.misc.chosenPokemon;
|
||||||
|
|
||||||
|
// Roll for poison (20%)
|
||||||
|
if (randSeedInt(10) < 2) {
|
||||||
|
if (chosenPokemon.trySetStatus(StatusEffect.POISON)) {
|
||||||
|
// Poison applied
|
||||||
|
queueEncounterMessage(scene, `${namespace}_poison`);
|
||||||
|
} else {
|
||||||
|
// Pokemon immune or something else prevents status
|
||||||
|
queueEncounterMessage(scene, `${namespace}_no_bad_effects`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
queueEncounterMessage(scene, `${namespace}_no_bad_effects`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setEncounterExp(scene, [chosenPokemon.id], 100);
|
||||||
|
|
||||||
|
chosenPokemon.updateInfo();
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}_option_3_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_3_tooltip`,
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Leave encounter with no rewards or exp
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -1,145 +0,0 @@
|
||||||
import {
|
|
||||||
generateModifierType,
|
|
||||||
leaveEncounterWithoutBattle,
|
|
||||||
queueEncounterMessage,
|
|
||||||
selectPokemonForOption,
|
|
||||||
setEncounterRewards,
|
|
||||||
updatePlayerMoney,
|
|
||||||
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
|
||||||
import { StatusEffect } from "#app/data/status-effect";
|
|
||||||
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
|
||||||
import { randSeedInt } from "#app/utils";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import BattleScene from "../../../battle-scene";
|
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter";
|
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
|
||||||
import {
|
|
||||||
MoneyRequirement
|
|
||||||
} from "../mystery-encounter-requirements";
|
|
||||||
|
|
||||||
export const ShadyVitaminDealerEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|
||||||
.withEncounterType(MysteryEncounterType.SHADY_VITAMIN_DEALER)
|
|
||||||
.withEncounterTier(MysteryEncounterTier.COMMON)
|
|
||||||
.withIntroSpriteConfigs([
|
|
||||||
{
|
|
||||||
spriteKey: Species.KROOKODILE.toString(),
|
|
||||||
fileRoot: "pokemon",
|
|
||||||
hasShadow: true,
|
|
||||||
repeat: true,
|
|
||||||
x: 10,
|
|
||||||
y: -1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
spriteKey: "b2w2_veteran_m",
|
|
||||||
fileRoot: "mystery-encounters",
|
|
||||||
hasShadow: true,
|
|
||||||
x: -10,
|
|
||||||
y: 2
|
|
||||||
}
|
|
||||||
])
|
|
||||||
.withSceneWaveRangeRequirement(10, 180)
|
|
||||||
.withPrimaryPokemonStatusEffectRequirement([StatusEffect.NONE]) // Pokemon must not have status
|
|
||||||
.withPrimaryPokemonHealthRatioRequirement([0.34, 1]) // Pokemon must have above 1/3rd HP
|
|
||||||
.withOption(new MysteryEncounterOptionBuilder()
|
|
||||||
.withSceneMoneyRequirement(0, 2) // Wave scaling multiplier of 2 for cost
|
|
||||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
|
||||||
// Update money
|
|
||||||
updatePlayerMoney(scene, -(encounter.options[0].requirements[0] as MoneyRequirement).requiredMoney);
|
|
||||||
// Calculate modifiers and dialogue tokens
|
|
||||||
const modifiers = [
|
|
||||||
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER),
|
|
||||||
generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER)
|
|
||||||
];
|
|
||||||
encounter.setDialogueToken("boost1", modifiers[0].name);
|
|
||||||
encounter.setDialogueToken("boost2", modifiers[1].name);
|
|
||||||
encounter.misc = {
|
|
||||||
chosenPokemon: pokemon,
|
|
||||||
modifiers: modifiers
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Only Pokemon that can gain benefits are unfainted with no status
|
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
|
||||||
// If pokemon meets primary pokemon reqs, it can be selected
|
|
||||||
const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon);
|
|
||||||
if (!meetsReqs) {
|
|
||||||
return "Pokémon must be healthy enough.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
return selectPokemonForOption(scene, onPokemonSelected, null, selectableFilter);
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Choose Cheap Option
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
const chosenPokemon = encounter.misc.chosenPokemon;
|
|
||||||
const modifiers = encounter.misc.modifiers;
|
|
||||||
|
|
||||||
for (const modType of modifiers) {
|
|
||||||
const modifier = modType.newModifier(chosenPokemon);
|
|
||||||
await scene.addModifier(modifier, true, false, false, true);
|
|
||||||
}
|
|
||||||
scene.updateModifiers(true);
|
|
||||||
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
})
|
|
||||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Damage and status applied after dealer leaves (to make thematic sense)
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
const chosenPokemon = encounter.misc.chosenPokemon;
|
|
||||||
|
|
||||||
// Pokemon takes 1/3 max HP damage
|
|
||||||
const damage = Math.round(chosenPokemon.getMaxHp() / 3);
|
|
||||||
chosenPokemon.hp = Math.max(chosenPokemon.hp - damage, 0);
|
|
||||||
|
|
||||||
// Roll for poison (80%)
|
|
||||||
if (randSeedInt(10) < 10) {
|
|
||||||
if (chosenPokemon.trySetStatus(StatusEffect.TOXIC)) {
|
|
||||||
// Toxic applied
|
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_bad_poison");
|
|
||||||
} else {
|
|
||||||
// Pokemon immune or something else prevents status
|
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_damage_only");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:shady_vitamin_dealer_damage_only");
|
|
||||||
}
|
|
||||||
|
|
||||||
chosenPokemon.updateInfo();
|
|
||||||
})
|
|
||||||
.build())
|
|
||||||
|
|
||||||
.withOption(new MysteryEncounterOptionBuilder()
|
|
||||||
.withSceneMoneyRequirement(0, 5) // Wave scaling multiplier of 2 for cost
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Choose Expensive Option
|
|
||||||
const modifiers = [];
|
|
||||||
let i = 0;
|
|
||||||
while (i < 3) {
|
|
||||||
// 2/1 weight on base stat booster vs PP Up
|
|
||||||
const roll = randSeedInt(3);
|
|
||||||
if (roll === 0) {
|
|
||||||
modifiers.push(modifierTypes.PP_UP);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false });
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Leave encounter with no rewards or exp
|
|
||||||
leaveEncounterWithoutBattle(scene, true);
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.build();
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
import { STEALING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { BerryType } from "#enums/berry-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import BattleScene from "../../../battle-scene";
|
||||||
|
import * as Utils from "../../../utils";
|
||||||
|
import { getPokemonSpecies } from "../../pokemon-species";
|
||||||
|
import { Status, StatusEffect } from "../../status-effect";
|
||||||
|
import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter";
|
||||||
|
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
import { MoveRequirement } from "../mystery-encounter-requirements";
|
||||||
|
import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, queueEncounterMessage, setEncounterExp, setEncounterRewards, } from "../mystery-encounter-utils";
|
||||||
|
|
||||||
|
/** i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:sleeping_snorlax";
|
||||||
|
|
||||||
|
export const SleepingSnorlaxEncounter: IMysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(
|
||||||
|
MysteryEncounterType.SLEEPING_SNORLAX
|
||||||
|
)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
||||||
|
.withCatchAllowed(true)
|
||||||
|
.withHideWildIntroMessage(true)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: Species.SNORLAX.toString(),
|
||||||
|
fileRoot: "pokemon",
|
||||||
|
hasShadow: true,
|
||||||
|
tint: 0.25,
|
||||||
|
scale: 1.5,
|
||||||
|
repeat: true,
|
||||||
|
y: 5,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}_intro_message`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withOnInit((scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
console.log(encounter);
|
||||||
|
|
||||||
|
// Calculate boss mon
|
||||||
|
const bossSpecies = getPokemonSpecies(Species.SNORLAX);
|
||||||
|
const pokemonConfig: EnemyPokemonConfig = {
|
||||||
|
species: bossSpecies,
|
||||||
|
isBoss: true,
|
||||||
|
status: StatusEffect.SLEEP,
|
||||||
|
};
|
||||||
|
const config: EnemyPartyConfig = {
|
||||||
|
levelAdditiveMultiplier: 2,
|
||||||
|
pokemonConfigs: [pokemonConfig],
|
||||||
|
};
|
||||||
|
encounter.enemyPartyConfigs = [config];
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.withTitle(`${namespace}_title`)
|
||||||
|
.withDescription(`${namespace}_description`)
|
||||||
|
.withQuery(`${namespace}_query`)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}_option_1_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_1_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_1_selected_message`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Pick battle
|
||||||
|
// TODO: do we want special rewards for this?
|
||||||
|
// setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: true});
|
||||||
|
await initBattleWithEnemyConfig(
|
||||||
|
scene,
|
||||||
|
scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withSimpleOption(
|
||||||
|
{
|
||||||
|
buttonLabel: `${namespace}_option_2_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_2_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_2_selected_message`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
const instance = scene.currentBattle.mysteryEncounter;
|
||||||
|
let roll: integer;
|
||||||
|
scene.executeWithSeedOffset(() => {
|
||||||
|
roll = Utils.randSeedInt(16, 0);
|
||||||
|
}, scene.currentBattle.waveIndex);
|
||||||
|
|
||||||
|
// Half Snorlax exp to entire party
|
||||||
|
setEncounterExp(
|
||||||
|
scene,
|
||||||
|
scene.getParty().map((p) => p.id),
|
||||||
|
98
|
||||||
|
);
|
||||||
|
|
||||||
|
if (roll > 4) {
|
||||||
|
// Fall asleep and get a sitrus berry (75%)
|
||||||
|
const p = instance.primaryPokemon;
|
||||||
|
p.status = new Status(StatusEffect.SLEEP, 0, 3);
|
||||||
|
p.updateInfo(true);
|
||||||
|
// const sitrus = (modifierTypes.BERRY?.() as ModifierTypeGenerator).generateType(scene.getParty(), [BerryType.SITRUS]);
|
||||||
|
const sitrus = generateModifierTypeOption(
|
||||||
|
scene,
|
||||||
|
modifierTypes.BERRY,
|
||||||
|
[BerryType.SITRUS]
|
||||||
|
);
|
||||||
|
|
||||||
|
setEncounterRewards(scene, {
|
||||||
|
guaranteedModifierTypeOptions: [sitrus],
|
||||||
|
fillRemaining: false,
|
||||||
|
});
|
||||||
|
queueEncounterMessage(scene, `${namespace}_option_2_bad_result`);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
} else {
|
||||||
|
// Heal to full (25%)
|
||||||
|
for (const pokemon of scene.getParty()) {
|
||||||
|
pokemon.hp = pokemon.getMaxHp();
|
||||||
|
pokemon.resetStatus();
|
||||||
|
for (const move of pokemon.moveset) {
|
||||||
|
move.ppUsed = 0;
|
||||||
|
}
|
||||||
|
pokemon.updateInfo(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
queueEncounterMessage(scene, `${namespace}_option_2_good_result`);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DISABLED_OR_SPECIAL)
|
||||||
|
.withPrimaryPokemonRequirement(new MoveRequirement(STEALING_MOVES))
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}_option_3_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_3_tooltip`,
|
||||||
|
disabledTooltip: `${namespace}_option_3_disabled_tooltip`,
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
// Steal the Snorlax's Leftovers
|
||||||
|
const instance = scene.currentBattle.mysteryEncounter;
|
||||||
|
setEncounterRewards(scene, {
|
||||||
|
guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS],
|
||||||
|
fillRemaining: false,
|
||||||
|
});
|
||||||
|
queueEncounterMessage(scene, `${namespace}_option_3_good_result`);
|
||||||
|
// Snorlax exp to Pokemon that did the stealing
|
||||||
|
setEncounterExp(scene, [instance.primaryPokemon.id], 189);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
|
@ -1,106 +0,0 @@
|
||||||
import {
|
|
||||||
ModifierTypeOption,
|
|
||||||
modifierTypes
|
|
||||||
} from "#app/modifier/modifier-type";
|
|
||||||
import { BerryType } from "#enums/berry-type";
|
|
||||||
import { Moves } from "#enums/moves";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import BattleScene from "../../../battle-scene";
|
|
||||||
import * as Utils from "../../../utils";
|
|
||||||
import { getPokemonSpecies } from "../../pokemon-species";
|
|
||||||
import { Status, StatusEffect } from "../../status-effect";
|
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter";
|
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
|
||||||
import { MoveRequirement } from "../mystery-encounter-requirements";
|
|
||||||
import {
|
|
||||||
EnemyPartyConfig,
|
|
||||||
EnemyPokemonConfig, generateModifierType,
|
|
||||||
initBattleWithEnemyConfig,
|
|
||||||
leaveEncounterWithoutBattle, queueEncounterMessage,
|
|
||||||
setEncounterRewards
|
|
||||||
} from "../mystery-encounter-utils";
|
|
||||||
|
|
||||||
export const SleepingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|
||||||
.withEncounterType(MysteryEncounterType.SLEEPING_SNORLAX)
|
|
||||||
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
|
||||||
.withIntroSpriteConfigs([
|
|
||||||
{
|
|
||||||
spriteKey: Species.SNORLAX.toString(),
|
|
||||||
fileRoot: "pokemon",
|
|
||||||
hasShadow: true,
|
|
||||||
tint: 0.25,
|
|
||||||
repeat: true
|
|
||||||
}
|
|
||||||
])
|
|
||||||
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
|
||||||
.withCatchAllowed(true)
|
|
||||||
.withHideWildIntroMessage(true)
|
|
||||||
.withOnInit((scene: BattleScene) => {
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
console.log(encounter);
|
|
||||||
|
|
||||||
// Calculate boss mon
|
|
||||||
const bossSpecies = getPokemonSpecies(Species.SNORLAX);
|
|
||||||
const pokemonConfig: EnemyPokemonConfig = {
|
|
||||||
species: bossSpecies,
|
|
||||||
isBoss: true,
|
|
||||||
status: StatusEffect.SLEEP
|
|
||||||
};
|
|
||||||
const config: EnemyPartyConfig = {
|
|
||||||
levelAdditiveMultiplier: 2,
|
|
||||||
pokemonConfigs: [pokemonConfig]
|
|
||||||
};
|
|
||||||
encounter.enemyPartyConfigs = [config];
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Pick battle
|
|
||||||
// TODO: do we want special rewards for this?
|
|
||||||
// setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: true});
|
|
||||||
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]);
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
const instance = scene.currentBattle.mysteryEncounter;
|
|
||||||
let roll: integer;
|
|
||||||
scene.executeWithSeedOffset(() => {
|
|
||||||
roll = Utils.randSeedInt(16, 0);
|
|
||||||
}, scene.currentBattle.waveIndex);
|
|
||||||
console.log(roll);
|
|
||||||
if (roll > 4) {
|
|
||||||
// Fall asleep and get a sitrus berry (75%)
|
|
||||||
const p = instance.primaryPokemon;
|
|
||||||
p.status = new Status(StatusEffect.SLEEP, 0, 3);
|
|
||||||
p.updateInfo(true);
|
|
||||||
// const sitrus = (modifierTypes.BERRY?.() as ModifierTypeGenerator).generateType(scene.getParty(), [BerryType.SITRUS]);
|
|
||||||
const sitrus = generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]);
|
|
||||||
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [new ModifierTypeOption(sitrus, 0)], fillRemaining: false });
|
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_2_bad_result");
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
} else {
|
|
||||||
// Heal to full (25%)
|
|
||||||
for (const pokemon of scene.getParty()) {
|
|
||||||
pokemon.hp = pokemon.getMaxHp();
|
|
||||||
pokemon.resetStatus();
|
|
||||||
for (const move of pokemon.moveset) {
|
|
||||||
move.ppUsed = 0;
|
|
||||||
}
|
|
||||||
pokemon.updateInfo(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_2_good_result");
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.withOption(new MysteryEncounterOptionBuilder()
|
|
||||||
.withPrimaryPokemonRequirement(new MoveRequirement([Moves.PLUCK, Moves.COVET, Moves.KNOCK_OFF, Moves.THIEF, Moves.TRICK, Moves.SWITCHEROO]))
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Leave encounter with no rewards or exp
|
|
||||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: false });
|
|
||||||
queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_3_good_result");
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.build();
|
|
|
@ -0,0 +1,438 @@
|
||||||
|
import { Ability, allAbilities } from "#app/data/ability";
|
||||||
|
import { EnemyPartyConfig, getEncounterText, initBattleWithEnemyConfig, selectPokemonForOption, setEncounterRewards, } from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||||
|
import { getNatureName, Nature } from "#app/data/nature";
|
||||||
|
import { speciesStarters } from "#app/data/pokemon-species";
|
||||||
|
import { Stat } from "#app/data/pokemon-stat";
|
||||||
|
import { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import { pokemonInfo } from "#app/locales/en/pokemon-info";
|
||||||
|
import { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
|
import { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
|
import { AbilityAttr } from "#app/system/game-data";
|
||||||
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
import { randSeedShuffle } from "#app/utils";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import BattleScene from "../../../battle-scene";
|
||||||
|
import IMysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier, } from "../mystery-encounter";
|
||||||
|
import { EncounterOptionMode, MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||||
|
|
||||||
|
/** The i18n namespace for the encounter */
|
||||||
|
const namespace = "mysteryEncounter:training_session";
|
||||||
|
|
||||||
|
export const TrainingSessionEncounter: IMysteryEncounter =
|
||||||
|
MysteryEncounterBuilder.withEncounterType(
|
||||||
|
MysteryEncounterType.TRAINING_SESSION
|
||||||
|
)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||||
|
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
||||||
|
.withHideWildIntroMessage(true)
|
||||||
|
.withIntroSpriteConfigs([
|
||||||
|
{
|
||||||
|
spriteKey: "training_gear",
|
||||||
|
fileRoot: "mystery-encounters",
|
||||||
|
hasShadow: true,
|
||||||
|
y: 3,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withIntroDialogue([
|
||||||
|
{
|
||||||
|
text: `${namespace}_intro_message`,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.withTitle(`${namespace}_title`)
|
||||||
|
.withDescription(`${namespace}_description`)
|
||||||
|
.withQuery(`${namespace}_query`)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}_option_1_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_1_tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected_message`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
encounter.misc = {
|
||||||
|
playerPokemon: pokemon,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
|
||||||
|
|
||||||
|
// Spawn light training session with chosen pokemon
|
||||||
|
// Every 50 waves, add +1 boss segment, capping at 5
|
||||||
|
const segments = Math.min(
|
||||||
|
2 + Math.floor(scene.currentBattle.waveIndex / 50),
|
||||||
|
5
|
||||||
|
);
|
||||||
|
const modifiers = new ModifiersHolder();
|
||||||
|
const config = getEnemyConfig(
|
||||||
|
scene,
|
||||||
|
playerPokemon,
|
||||||
|
segments,
|
||||||
|
modifiers
|
||||||
|
);
|
||||||
|
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||||
|
|
||||||
|
const getIvName = (index: number) => {
|
||||||
|
switch (index) {
|
||||||
|
case Stat.HP:
|
||||||
|
return pokemonInfo.Stat["HPshortened"];
|
||||||
|
case Stat.ATK:
|
||||||
|
return pokemonInfo.Stat["ATKshortened"];
|
||||||
|
case Stat.DEF:
|
||||||
|
return pokemonInfo.Stat["DEFshortened"];
|
||||||
|
case Stat.SPATK:
|
||||||
|
return pokemonInfo.Stat["SPATKshortened"];
|
||||||
|
case Stat.SPDEF:
|
||||||
|
return pokemonInfo.Stat["SPDEFshortened"];
|
||||||
|
case Stat.SPD:
|
||||||
|
return pokemonInfo.Stat["SPDshortened"];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBeforeRewardsPhase = () => {
|
||||||
|
encounter.setDialogueToken("stat1", "-");
|
||||||
|
encounter.setDialogueToken("stat2", "-");
|
||||||
|
// Add the pokemon back to party with IV boost
|
||||||
|
const ivIndexes = [];
|
||||||
|
playerPokemon.ivs.forEach((iv, index) => {
|
||||||
|
if (iv < 31) {
|
||||||
|
ivIndexes.push({ iv: iv, index: index });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Improves 2 random non-maxed IVs
|
||||||
|
// +10 if IV is < 10, +5 if between 10-20, and +3 if > 20
|
||||||
|
// A 0-4 starting IV will cap in 6 encounters (assuming you always rolled that IV)
|
||||||
|
// 5-14 starting IV caps in 5 encounters
|
||||||
|
// 15-19 starting IV caps in 4 encounters
|
||||||
|
// 20-24 starting IV caps in 3 encounters
|
||||||
|
// 25-27 starting IV caps in 2 encounters
|
||||||
|
let improvedCount = 0;
|
||||||
|
while (ivIndexes.length > 0 && improvedCount < 2) {
|
||||||
|
randSeedShuffle(ivIndexes);
|
||||||
|
const ivToChange = ivIndexes.pop();
|
||||||
|
let newVal = ivToChange.iv;
|
||||||
|
if (improvedCount === 0) {
|
||||||
|
encounter.setDialogueToken(
|
||||||
|
"stat1",
|
||||||
|
getIvName(ivToChange.index)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
encounter.setDialogueToken(
|
||||||
|
"stat2",
|
||||||
|
getIvName(ivToChange.index)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Corrects required encounter breakpoints to be continuous for all IV values
|
||||||
|
if (ivToChange.iv <= 21 && ivToChange.iv - (1 % 5) === 0) {
|
||||||
|
newVal += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
newVal += ivToChange.iv <= 10 ? 10 : ivToChange.iv <= 20 ? 5 : 3;
|
||||||
|
newVal = Math.min(newVal, 31);
|
||||||
|
playerPokemon.ivs[ivToChange.index] = newVal;
|
||||||
|
improvedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (improvedCount > 0) {
|
||||||
|
playerPokemon.calculateStats();
|
||||||
|
scene.gameData.updateSpeciesDexIvs(
|
||||||
|
playerPokemon.species.getRootSpeciesId(true),
|
||||||
|
playerPokemon.ivs
|
||||||
|
);
|
||||||
|
scene.gameData.setPokemonCaught(playerPokemon, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add pokemon and mods back
|
||||||
|
scene.getParty().push(playerPokemon);
|
||||||
|
for (const mod of modifiers.value) {
|
||||||
|
scene.addModifier(mod, true, false, false, true);
|
||||||
|
}
|
||||||
|
scene.updateModifiers(true);
|
||||||
|
scene.queueMessage(
|
||||||
|
getEncounterText(scene, `${namespace}_battle_finished_1`),
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
setEncounterRewards(
|
||||||
|
scene,
|
||||||
|
{ fillRemaining: true },
|
||||||
|
null,
|
||||||
|
onBeforeRewardsPhase
|
||||||
|
);
|
||||||
|
|
||||||
|
return initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}_option_2_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_2_tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}_option_2_select_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected_message`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
// Open menu for selecting pokemon and Nature
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const natures = new Array(25).fill(null).map((val, i) => i as Nature);
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Return the options for nature selection
|
||||||
|
return natures.map((nature: Nature) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: getNatureName(nature, true, true, true, scene.uiTheme),
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and second option selected
|
||||||
|
encounter.setDialogueToken("nature", getNatureName(nature));
|
||||||
|
encounter.misc = {
|
||||||
|
playerPokemon: pokemon,
|
||||||
|
chosenNature: nature,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
|
||||||
|
|
||||||
|
// Spawn medium training session with chosen pokemon
|
||||||
|
// Every 40 waves, add +1 boss segment, capping at 6
|
||||||
|
const segments = Math.min(
|
||||||
|
2 + Math.floor(scene.currentBattle.waveIndex / 40),
|
||||||
|
6
|
||||||
|
);
|
||||||
|
const modifiers = new ModifiersHolder();
|
||||||
|
const config = getEnemyConfig(
|
||||||
|
scene,
|
||||||
|
playerPokemon,
|
||||||
|
segments,
|
||||||
|
modifiers
|
||||||
|
);
|
||||||
|
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||||
|
|
||||||
|
const onBeforeRewardsPhase = () => {
|
||||||
|
scene.queueMessage(
|
||||||
|
getEncounterText(scene, `${namespace}_battle_finished_2`),
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
// Add the pokemon back to party with Nature change
|
||||||
|
playerPokemon.setNature(encounter.misc.chosenNature);
|
||||||
|
scene.gameData.setPokemonCaught(playerPokemon, false);
|
||||||
|
|
||||||
|
// Add pokemon and mods back
|
||||||
|
scene.getParty().push(playerPokemon);
|
||||||
|
for (const mod of modifiers.value) {
|
||||||
|
scene.addModifier(mod, true, false, false, true);
|
||||||
|
}
|
||||||
|
scene.updateModifiers(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
setEncounterRewards(
|
||||||
|
scene,
|
||||||
|
{ fillRemaining: true },
|
||||||
|
null,
|
||||||
|
onBeforeRewardsPhase
|
||||||
|
);
|
||||||
|
|
||||||
|
return initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
new MysteryEncounterOptionBuilder()
|
||||||
|
.withOptionMode(EncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}_option_3_label`,
|
||||||
|
buttonTooltip: `${namespace}_option_3_tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}_option_3_select_prompt`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}_option_selected_message`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
// Open menu for selecting pokemon and ability to learn
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Return the options for ability selection
|
||||||
|
const speciesForm = !!pokemon.getFusionSpeciesForm()
|
||||||
|
? pokemon.getFusionSpeciesForm()
|
||||||
|
: pokemon.getSpeciesForm();
|
||||||
|
const abilityCount = speciesForm.getAbilityCount();
|
||||||
|
const abilities = new Array(abilityCount)
|
||||||
|
.fill(null)
|
||||||
|
.map((val, i) => allAbilities[speciesForm.getAbility(i)]);
|
||||||
|
return abilities.map((ability: Ability, index) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: ability.name,
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and ability selected
|
||||||
|
encounter.setDialogueToken("ability", ability.name);
|
||||||
|
encounter.misc = {
|
||||||
|
playerPokemon: pokemon,
|
||||||
|
abilityIndex: index,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
onHover: () => {
|
||||||
|
scene.ui.showText(ability.description);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter;
|
||||||
|
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
|
||||||
|
|
||||||
|
// Spawn hard training session with chosen pokemon
|
||||||
|
// Every 30 waves, add +1 boss segment, capping at 6
|
||||||
|
// Also starts with +1 to all stats
|
||||||
|
const segments = Math.min(
|
||||||
|
2 + Math.floor(scene.currentBattle.waveIndex / 30),
|
||||||
|
6
|
||||||
|
);
|
||||||
|
const modifiers = new ModifiersHolder();
|
||||||
|
const config = getEnemyConfig(
|
||||||
|
scene,
|
||||||
|
playerPokemon,
|
||||||
|
segments,
|
||||||
|
modifiers
|
||||||
|
);
|
||||||
|
config.pokemonConfigs[0].tags = [
|
||||||
|
BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON,
|
||||||
|
];
|
||||||
|
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||||
|
|
||||||
|
const onBeforeRewardsPhase = () => {
|
||||||
|
scene.queueMessage(
|
||||||
|
getEncounterText(scene, `${namespace}_battle_finished_3`),
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
// Add the pokemon back to party with ability change
|
||||||
|
const abilityIndex = encounter.misc.abilityIndex;
|
||||||
|
if (!!playerPokemon.getFusionSpeciesForm()) {
|
||||||
|
playerPokemon.fusionAbilityIndex = abilityIndex;
|
||||||
|
if (
|
||||||
|
speciesStarters.hasOwnProperty(
|
||||||
|
playerPokemon.fusionSpecies.speciesId
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
scene.gameData.starterData[
|
||||||
|
playerPokemon.fusionSpecies.speciesId
|
||||||
|
].abilityAttr |=
|
||||||
|
abilityIndex !== 1 || playerPokemon.fusionSpecies.ability2
|
||||||
|
? Math.pow(2, playerPokemon.fusionAbilityIndex)
|
||||||
|
: AbilityAttr.ABILITY_HIDDEN;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
playerPokemon.abilityIndex = abilityIndex;
|
||||||
|
if (
|
||||||
|
speciesStarters.hasOwnProperty(playerPokemon.species.speciesId)
|
||||||
|
) {
|
||||||
|
scene.gameData.starterData[
|
||||||
|
playerPokemon.species.speciesId
|
||||||
|
].abilityAttr |=
|
||||||
|
abilityIndex !== 1 || playerPokemon.species.ability2
|
||||||
|
? Math.pow(2, playerPokemon.abilityIndex)
|
||||||
|
: AbilityAttr.ABILITY_HIDDEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playerPokemon.getAbility();
|
||||||
|
playerPokemon.calculateStats();
|
||||||
|
scene.gameData.setPokemonCaught(playerPokemon, false);
|
||||||
|
|
||||||
|
// Add pokemon and mods back
|
||||||
|
scene.getParty().push(playerPokemon);
|
||||||
|
for (const mod of modifiers.value) {
|
||||||
|
scene.addModifier(mod, true, false, false, true);
|
||||||
|
}
|
||||||
|
scene.updateModifiers(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
setEncounterRewards(
|
||||||
|
scene,
|
||||||
|
{ fillRemaining: true },
|
||||||
|
null,
|
||||||
|
onBeforeRewardsPhase
|
||||||
|
);
|
||||||
|
|
||||||
|
return initBattleWithEnemyConfig(scene, config);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
function getEnemyConfig(
|
||||||
|
scene: BattleScene,
|
||||||
|
playerPokemon: PlayerPokemon,
|
||||||
|
segments: number,
|
||||||
|
modifiers: ModifiersHolder
|
||||||
|
): EnemyPartyConfig {
|
||||||
|
playerPokemon.resetSummonData();
|
||||||
|
|
||||||
|
// Passes modifiers by reference
|
||||||
|
modifiers.value = scene.findModifiers(
|
||||||
|
(m) =>
|
||||||
|
m instanceof PokemonHeldItemModifier &&
|
||||||
|
(m as PokemonHeldItemModifier).pokemonId === playerPokemon.id
|
||||||
|
) as PokemonHeldItemModifier[];
|
||||||
|
const modifierTypes = modifiers.value.map(
|
||||||
|
(mod) => mod.type
|
||||||
|
) as PokemonHeldItemModifierType[];
|
||||||
|
|
||||||
|
const data = new PokemonData(playerPokemon);
|
||||||
|
return {
|
||||||
|
pokemonConfigs: [
|
||||||
|
{
|
||||||
|
species: playerPokemon.species,
|
||||||
|
isBoss: true,
|
||||||
|
bossSegments: segments,
|
||||||
|
formIndex: playerPokemon.formIndex,
|
||||||
|
level: playerPokemon.level,
|
||||||
|
dataSource: data,
|
||||||
|
modifierTypes: modifierTypes,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModifiersHolder {
|
||||||
|
public value: PokemonHeldItemModifier[] = [];
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
}
|
|
@ -1,309 +0,0 @@
|
||||||
import { Ability, allAbilities } from "#app/data/ability";
|
|
||||||
import {
|
|
||||||
EnemyPartyConfig,
|
|
||||||
getEncounterText,
|
|
||||||
initBattleWithEnemyConfig,
|
|
||||||
selectPokemonForOption,
|
|
||||||
setEncounterRewards
|
|
||||||
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
|
||||||
import { getNatureName, Nature } from "#app/data/nature";
|
|
||||||
import { speciesStarters } from "#app/data/pokemon-species";
|
|
||||||
import { Stat } from "#app/data/pokemon-stat";
|
|
||||||
import { PlayerPokemon } from "#app/field/pokemon";
|
|
||||||
import { pokemonInfo } from "#app/locales/en/pokemon-info";
|
|
||||||
import { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
|
||||||
import { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
|
||||||
import { AbilityAttr } from "#app/system/game-data";
|
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
|
||||||
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
|
||||||
import { randSeedShuffle } from "#app/utils";
|
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import BattleScene from "../../../battle-scene";
|
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter";
|
|
||||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
|
||||||
|
|
||||||
export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|
||||||
.withEncounterType(MysteryEncounterType.TRAINING_SESSION)
|
|
||||||
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
|
||||||
.withIntroSpriteConfigs([
|
|
||||||
{
|
|
||||||
spriteKey: "training_gear",
|
|
||||||
fileRoot: "mystery-encounters",
|
|
||||||
hasShadow: true,
|
|
||||||
y: 3
|
|
||||||
}
|
|
||||||
])
|
|
||||||
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
|
||||||
.withHideWildIntroMessage(true)
|
|
||||||
.withOption(new MysteryEncounterOptionBuilder()
|
|
||||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
|
||||||
encounter.misc = {
|
|
||||||
playerPokemon: pokemon
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
return selectPokemonForOption(scene, onPokemonSelected);
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
|
|
||||||
|
|
||||||
// Spawn light training session with chosen pokemon
|
|
||||||
// Every 50 waves, add +1 boss segment, capping at 5
|
|
||||||
const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 50), 5);
|
|
||||||
const modifiers = new ModifiersHolder();
|
|
||||||
const config = getEnemyConfig(scene, playerPokemon, segments, modifiers);
|
|
||||||
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
|
||||||
|
|
||||||
const getIvName = (index: number) => {
|
|
||||||
switch (index) {
|
|
||||||
case Stat.HP:
|
|
||||||
return pokemonInfo.Stat["HPshortened"];
|
|
||||||
case Stat.ATK:
|
|
||||||
return pokemonInfo.Stat["ATKshortened"];
|
|
||||||
case Stat.DEF:
|
|
||||||
return pokemonInfo.Stat["DEFshortened"];
|
|
||||||
case Stat.SPATK:
|
|
||||||
return pokemonInfo.Stat["SPATKshortened"];
|
|
||||||
case Stat.SPDEF:
|
|
||||||
return pokemonInfo.Stat["SPDEFshortened"];
|
|
||||||
case Stat.SPD:
|
|
||||||
return pokemonInfo.Stat["SPDshortened"];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onBeforeRewardsPhase = () => {
|
|
||||||
encounter.setDialogueToken("stat1", "-");
|
|
||||||
encounter.setDialogueToken("stat2", "-");
|
|
||||||
// Add the pokemon back to party with IV boost
|
|
||||||
const ivIndexes = [];
|
|
||||||
playerPokemon.ivs.forEach((iv, index) => {
|
|
||||||
if (iv < 31) {
|
|
||||||
ivIndexes.push({ iv: iv, index: index });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Improves 2 random non-maxed IVs
|
|
||||||
// +10 if IV is < 10, +5 if between 10-20, and +3 if > 20
|
|
||||||
// A 0-4 starting IV will cap in 6 encounters (assuming you always rolled that IV)
|
|
||||||
// 5-14 starting IV caps in 5 encounters
|
|
||||||
// 15-19 starting IV caps in 4 encounters
|
|
||||||
// 20-24 starting IV caps in 3 encounters
|
|
||||||
// 25-27 starting IV caps in 2 encounters
|
|
||||||
let improvedCount = 0;
|
|
||||||
while (ivIndexes.length > 0 && improvedCount < 2) {
|
|
||||||
randSeedShuffle(ivIndexes);
|
|
||||||
const ivToChange = ivIndexes.pop();
|
|
||||||
let newVal = ivToChange.iv;
|
|
||||||
if (improvedCount === 0) {
|
|
||||||
encounter.setDialogueToken("stat1", getIvName(ivToChange.index));
|
|
||||||
} else {
|
|
||||||
encounter.setDialogueToken("stat2", getIvName(ivToChange.index));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Corrects required encounter breakpoints to be continuous for all IV values
|
|
||||||
if (ivToChange.iv <= 21 && ivToChange.iv - 1 % 5 === 0) {
|
|
||||||
newVal += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
newVal += ivToChange.iv <= 10 ? 10 : ivToChange.iv <= 20 ? 5 : 3;
|
|
||||||
newVal = Math.min(newVal, 31);
|
|
||||||
playerPokemon.ivs[ivToChange.index] = newVal;
|
|
||||||
improvedCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (improvedCount > 0) {
|
|
||||||
playerPokemon.calculateStats();
|
|
||||||
scene.gameData.updateSpeciesDexIvs(playerPokemon.species.getRootSpeciesId(true), playerPokemon.ivs);
|
|
||||||
scene.gameData.setPokemonCaught(playerPokemon, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add pokemon and mods back
|
|
||||||
scene.getParty().push(playerPokemon);
|
|
||||||
for (const mod of modifiers.value) {
|
|
||||||
scene.addModifier(mod, true, false, false, true);
|
|
||||||
}
|
|
||||||
scene.updateModifiers(true);
|
|
||||||
scene.queueMessage(getEncounterText(scene, "mysteryEncounter:training_session_battle_finished_1"), null, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
setEncounterRewards(scene, { fillRemaining: true }, null, onBeforeRewardsPhase);
|
|
||||||
|
|
||||||
return initBattleWithEnemyConfig(scene, config);
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.withOption(new MysteryEncounterOptionBuilder()
|
|
||||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
|
||||||
// Open menu for selecting pokemon and Nature
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
const natures = new Array(25).fill(null).map((val, i) => i as Nature);
|
|
||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
|
||||||
// Return the options for nature selection
|
|
||||||
return natures.map((nature: Nature) => {
|
|
||||||
const option: OptionSelectItem = {
|
|
||||||
label: getNatureName(nature, true, true, true, scene.uiTheme),
|
|
||||||
handler: () => {
|
|
||||||
// Pokemon and second option selected
|
|
||||||
encounter.setDialogueToken("nature", getNatureName(nature));
|
|
||||||
encounter.misc = {
|
|
||||||
playerPokemon: pokemon,
|
|
||||||
chosenNature: nature
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return option;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return selectPokemonForOption(scene, onPokemonSelected);
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
|
|
||||||
|
|
||||||
// Spawn medium training session with chosen pokemon
|
|
||||||
// Every 40 waves, add +1 boss segment, capping at 6
|
|
||||||
const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 40), 6);
|
|
||||||
const modifiers = new ModifiersHolder();
|
|
||||||
const config = getEnemyConfig(scene, playerPokemon, segments, modifiers);
|
|
||||||
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
|
||||||
|
|
||||||
const onBeforeRewardsPhase = () => {
|
|
||||||
scene.queueMessage(getEncounterText(scene, "mysteryEncounter:training_session_battle_finished_2"), null, true);
|
|
||||||
// Add the pokemon back to party with Nature change
|
|
||||||
playerPokemon.setNature(encounter.misc.chosenNature);
|
|
||||||
scene.gameData.setPokemonCaught(playerPokemon, false);
|
|
||||||
|
|
||||||
// Add pokemon and mods back
|
|
||||||
scene.getParty().push(playerPokemon);
|
|
||||||
for (const mod of modifiers.value) {
|
|
||||||
scene.addModifier(mod, true, false, false, true);
|
|
||||||
}
|
|
||||||
scene.updateModifiers(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
setEncounterRewards(scene, { fillRemaining: true }, null, onBeforeRewardsPhase);
|
|
||||||
|
|
||||||
return initBattleWithEnemyConfig(scene, config);
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.withOption(new MysteryEncounterOptionBuilder()
|
|
||||||
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
|
||||||
// Open menu for selecting pokemon and ability to learn
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
|
||||||
// Return the options for ability selection
|
|
||||||
const speciesForm = !!pokemon.getFusionSpeciesForm() ? pokemon.getFusionSpeciesForm() : pokemon.getSpeciesForm();
|
|
||||||
const abilityCount = speciesForm.getAbilityCount();
|
|
||||||
const abilities = new Array(abilityCount).fill(null).map((val, i) => allAbilities[speciesForm.getAbility(i)]);
|
|
||||||
return abilities.map((ability: Ability, index) => {
|
|
||||||
const option: OptionSelectItem = {
|
|
||||||
label: ability.name,
|
|
||||||
handler: () => {
|
|
||||||
// Pokemon and ability selected
|
|
||||||
encounter.setDialogueToken("ability", ability.name);
|
|
||||||
encounter.misc = {
|
|
||||||
playerPokemon: pokemon,
|
|
||||||
abilityIndex: index
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
onHover: () => {
|
|
||||||
scene.ui.showText(ability.description);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return option;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return selectPokemonForOption(scene, onPokemonSelected);
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter;
|
|
||||||
const playerPokemon: PlayerPokemon = encounter.misc.playerPokemon;
|
|
||||||
|
|
||||||
// Spawn hard training session with chosen pokemon
|
|
||||||
// Every 30 waves, add +1 boss segment, capping at 6
|
|
||||||
// Also starts with +1 to all stats
|
|
||||||
const segments = Math.min(2 + Math.floor(scene.currentBattle.waveIndex / 30), 6);
|
|
||||||
const modifiers = new ModifiersHolder();
|
|
||||||
const config = getEnemyConfig(scene, playerPokemon, segments, modifiers);
|
|
||||||
config.pokemonConfigs[0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
|
|
||||||
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
|
||||||
|
|
||||||
const onBeforeRewardsPhase = () => {
|
|
||||||
scene.queueMessage(getEncounterText(scene, "mysteryEncounter:training_session_battle_finished_3"), null, true);
|
|
||||||
// Add the pokemon back to party with ability change
|
|
||||||
const abilityIndex = encounter.misc.abilityIndex;
|
|
||||||
if (!!playerPokemon.getFusionSpeciesForm()) {
|
|
||||||
playerPokemon.fusionAbilityIndex = abilityIndex;
|
|
||||||
if (speciesStarters.hasOwnProperty(playerPokemon.fusionSpecies.speciesId)) {
|
|
||||||
scene.gameData.starterData[playerPokemon.fusionSpecies.speciesId].abilityAttr |= abilityIndex !== 1 || playerPokemon.fusionSpecies.ability2
|
|
||||||
? Math.pow(2, playerPokemon.fusionAbilityIndex)
|
|
||||||
: AbilityAttr.ABILITY_HIDDEN;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
playerPokemon.abilityIndex = abilityIndex;
|
|
||||||
if (speciesStarters.hasOwnProperty(playerPokemon.species.speciesId)) {
|
|
||||||
scene.gameData.starterData[playerPokemon.species.speciesId].abilityAttr |= abilityIndex !== 1 || playerPokemon.species.ability2
|
|
||||||
? Math.pow(2, playerPokemon.abilityIndex)
|
|
||||||
: AbilityAttr.ABILITY_HIDDEN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
playerPokemon.getAbility();
|
|
||||||
playerPokemon.calculateStats();
|
|
||||||
scene.gameData.setPokemonCaught(playerPokemon, false);
|
|
||||||
|
|
||||||
// Add pokemon and mods back
|
|
||||||
scene.getParty().push(playerPokemon);
|
|
||||||
for (const mod of modifiers.value) {
|
|
||||||
scene.addModifier(mod, true, false, false, true);
|
|
||||||
}
|
|
||||||
scene.updateModifiers(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
setEncounterRewards(scene, { fillRemaining: true }, null, onBeforeRewardsPhase);
|
|
||||||
|
|
||||||
return initBattleWithEnemyConfig(scene, config);
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon, segments: number, modifiers: ModifiersHolder): EnemyPartyConfig {
|
|
||||||
playerPokemon.resetSummonData();
|
|
||||||
|
|
||||||
// Passes modifiers by reference
|
|
||||||
modifiers.value = scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
|
||||||
&& (m as PokemonHeldItemModifier).pokemonId === playerPokemon.id) as PokemonHeldItemModifier[];
|
|
||||||
const modifierTypes = modifiers.value.map(mod => mod.type) as PokemonHeldItemModifierType[];
|
|
||||||
|
|
||||||
const data = new PokemonData(playerPokemon);
|
|
||||||
return {
|
|
||||||
pokemonConfigs: [
|
|
||||||
{
|
|
||||||
species: playerPokemon.species,
|
|
||||||
isBoss: true,
|
|
||||||
bossSegments: segments,
|
|
||||||
formIndex: playerPokemon.formIndex,
|
|
||||||
level: playerPokemon.level,
|
|
||||||
dataSource: data,
|
|
||||||
modifierTypes: modifierTypes
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class ModifiersHolder {
|
|
||||||
public value: PokemonHeldItemModifier[] = [];
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +1,4 @@
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import { MysteriousChallengersDialogue } from "#app/data/mystery-encounters/dialogue/mysterious-challengers-dialogue";
|
|
||||||
import { MysteriousChestDialogue } from "#app/data/mystery-encounters/dialogue/mysterious-chest-dialogue";
|
|
||||||
import { DarkDealDialogue } from "#app/data/mystery-encounters/dialogue/dark-deal-dialogue";
|
|
||||||
import { FightOrFlightDialogue } from "#app/data/mystery-encounters/dialogue/fight-or-flight-dialogue";
|
|
||||||
import { TrainingSessionDialogue } from "#app/data/mystery-encounters/dialogue/training-session-dialogue";
|
|
||||||
import { SleepingSnorlaxDialogue } from "./dialogue/sleeping-snorlax-dialogue";
|
|
||||||
import { DepartmentStoreSaleDialogue } from "#app/data/mystery-encounters/dialogue/department-store-sale-dialogue";
|
|
||||||
import { ShadyVitaminDealerDialogue } from "#app/data/mystery-encounters/dialogue/shady-vitamin-dealer";
|
|
||||||
import { TextStyle } from "#app/ui/text";
|
import { TextStyle } from "#app/ui/text";
|
||||||
import { LostAtSeaDialogue } from "./dialogue/lost-at-sea-dialogue";
|
|
||||||
|
|
||||||
export class TextDisplay {
|
export class TextDisplay {
|
||||||
speaker?: TemplateStringsArray | `mysteryEncounter:${string}`;
|
speaker?: TemplateStringsArray | `mysteryEncounter:${string}`;
|
||||||
|
@ -26,15 +16,15 @@ export class OptionTextDisplay {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EncounterOptionsDialogue {
|
export class EncounterOptionsDialogue {
|
||||||
title: TemplateStringsArray | `mysteryEncounter:${string}`;
|
title?: TemplateStringsArray | `mysteryEncounter:${string}`;
|
||||||
description: TemplateStringsArray | `mysteryEncounter:${string}`;
|
description?: TemplateStringsArray | `mysteryEncounter:${string}`;
|
||||||
query?: TemplateStringsArray | `mysteryEncounter:${string}`;
|
query?: TemplateStringsArray | `mysteryEncounter:${string}`;
|
||||||
options: [OptionTextDisplay, OptionTextDisplay, ...OptionTextDisplay[]]; // Options array with minimum 2 options
|
options?: [...OptionTextDisplay[]]; // Options array with minimum 2 options
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class MysteryEncounterDialogue {
|
export default class MysteryEncounterDialogue {
|
||||||
intro?: TextDisplay[];
|
intro?: TextDisplay[];
|
||||||
encounterOptionsDialogue: EncounterOptionsDialogue;
|
encounterOptionsDialogue?: EncounterOptionsDialogue;
|
||||||
outro?: TextDisplay[];
|
outro?: TextDisplay[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,17 +71,3 @@ export default class MysteryEncounterDialogue {
|
||||||
}
|
}
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const allMysteryEncounterDialogue: { [encounterType: number]: MysteryEncounterDialogue } = {};
|
|
||||||
|
|
||||||
export function initMysteryEncounterDialogue() {
|
|
||||||
allMysteryEncounterDialogue[MysteryEncounterType.MYSTERIOUS_CHALLENGERS] = MysteriousChallengersDialogue;
|
|
||||||
allMysteryEncounterDialogue[MysteryEncounterType.MYSTERIOUS_CHEST] = MysteriousChestDialogue;
|
|
||||||
allMysteryEncounterDialogue[MysteryEncounterType.DARK_DEAL] = DarkDealDialogue;
|
|
||||||
allMysteryEncounterDialogue[MysteryEncounterType.FIGHT_OR_FLIGHT] = FightOrFlightDialogue;
|
|
||||||
allMysteryEncounterDialogue[MysteryEncounterType.TRAINING_SESSION] = TrainingSessionDialogue;
|
|
||||||
allMysteryEncounterDialogue[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxDialogue;
|
|
||||||
allMysteryEncounterDialogue[MysteryEncounterType.DEPARTMENT_STORE_SALE] = DepartmentStoreSaleDialogue;
|
|
||||||
allMysteryEncounterDialogue[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerDialogue;
|
|
||||||
allMysteryEncounterDialogue[MysteryEncounterType.LOST_AT_SEA] = LostAtSeaDialogue;
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,10 +5,22 @@ import * as Utils from "../../utils";
|
||||||
import { Type } from "../type";
|
import { Type } from "../type";
|
||||||
import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "./mystery-encounter-requirements";
|
import { EncounterPokemonRequirement, EncounterSceneRequirement, MoneyRequirement, TypeRequirement } from "./mystery-encounter-requirements";
|
||||||
|
|
||||||
|
export enum EncounterOptionMode {
|
||||||
|
/** Default style */
|
||||||
|
DEFAULT,
|
||||||
|
/** Disabled on requirements not met, default style on requirements met */
|
||||||
|
DISABLED_OR_DEFAULT,
|
||||||
|
/** Default style on requirements not met, special style on requirements met */
|
||||||
|
DEFAULT_OR_SPECIAL,
|
||||||
|
/** Disabled on requirements not met, special style on requirements met */
|
||||||
|
DISABLED_OR_SPECIAL
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export type OptionPhaseCallback = (scene: BattleScene) => Promise<void | boolean>;
|
export type OptionPhaseCallback = (scene: BattleScene) => Promise<void | boolean>;
|
||||||
|
|
||||||
export default interface MysteryEncounterOption {
|
export default interface MysteryEncounterOption {
|
||||||
|
optionMode: EncounterOptionMode;
|
||||||
requirements?: EncounterSceneRequirement[];
|
requirements?: EncounterSceneRequirement[];
|
||||||
primaryPokemonRequirements?: EncounterPokemonRequirement[];
|
primaryPokemonRequirements?: EncounterPokemonRequirement[];
|
||||||
secondaryPokemonRequirements?: EncounterPokemonRequirement[];
|
secondaryPokemonRequirements?: EncounterPokemonRequirement[];
|
||||||
|
@ -34,12 +46,18 @@ export default class MysteryEncounterOption implements MysteryEncounterOption {
|
||||||
constructor(option: MysteryEncounterOption) {
|
constructor(option: MysteryEncounterOption) {
|
||||||
Object.assign(this, option);
|
Object.assign(this, option);
|
||||||
this.requirements = this.requirements ? this.requirements : [];
|
this.requirements = this.requirements ? this.requirements : [];
|
||||||
|
this.primaryPokemonRequirements = this.primaryPokemonRequirements ? this.primaryPokemonRequirements : [];
|
||||||
|
this.secondaryPokemonRequirements = this.secondaryPokemonRequirements ? this.secondaryPokemonRequirements : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
hasRequirements?() {
|
||||||
|
return this.requirements.length > 0 || this.primaryPokemonRequirements.length > 0 || this.secondaryPokemonRequirements.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsRequirements?(scene: BattleScene) {
|
meetsRequirements?(scene: BattleScene) {
|
||||||
return !this.requirements.some(requirement => !requirement.meetsRequirement(scene)) &&
|
return !this.requirements.some(requirement => !requirement.meetsRequirement(scene)) &&
|
||||||
this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene) &&
|
this.meetsSupportingRequirementAndSupportingPokemonSelected(scene) &&
|
||||||
this.meetsSupportingRequirementAndSupportingPokemonSelected(scene);
|
this.meetsPrimaryRequirementAndPrimaryPokemonSelected(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
meetsPrimaryRequirementAndPrimaryPokemonSelected?(scene: BattleScene) {
|
meetsPrimaryRequirementAndPrimaryPokemonSelected?(scene: BattleScene) {
|
||||||
|
@ -121,13 +139,20 @@ export default class MysteryEncounterOption implements MysteryEncounterOption {
|
||||||
|
|
||||||
|
|
||||||
export class MysteryEncounterOptionBuilder implements Partial<MysteryEncounterOption> {
|
export class MysteryEncounterOptionBuilder implements Partial<MysteryEncounterOption> {
|
||||||
|
optionMode?: EncounterOptionMode;
|
||||||
requirements?: EncounterSceneRequirement[] = [];
|
requirements?: EncounterSceneRequirement[] = [];
|
||||||
primaryPokemonRequirements?: EncounterPokemonRequirement[] = [];
|
primaryPokemonRequirements?: EncounterPokemonRequirement[] = [];
|
||||||
secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = [];
|
secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = [];
|
||||||
excludePrimaryFromSecondaryRequirements?: boolean;
|
excludePrimaryFromSecondaryRequirements?: boolean;
|
||||||
|
isDisabledOnRequirementsNotMet?: boolean;
|
||||||
onPreOptionPhase?: OptionPhaseCallback;
|
onPreOptionPhase?: OptionPhaseCallback;
|
||||||
onOptionPhase?: OptionPhaseCallback;
|
onOptionPhase?: OptionPhaseCallback;
|
||||||
onPostOptionPhase?: OptionPhaseCallback;
|
onPostOptionPhase?: OptionPhaseCallback;
|
||||||
|
dialogue?: OptionTextDisplay;
|
||||||
|
|
||||||
|
withOptionMode(optionMode: EncounterOptionMode): this & Pick<MysteryEncounterOption, "optionMode"> {
|
||||||
|
return Object.assign(this, { optionMode });
|
||||||
|
}
|
||||||
|
|
||||||
withSceneRequirement(requirement: EncounterSceneRequirement): this & Required<Pick<MysteryEncounterOption, "requirements">> {
|
withSceneRequirement(requirement: EncounterSceneRequirement): this & Required<Pick<MysteryEncounterOption, "requirements">> {
|
||||||
this.requirements.push(requirement);
|
this.requirements.push(requirement);
|
||||||
|
@ -178,4 +203,9 @@ export class MysteryEncounterOptionBuilder implements Partial<MysteryEncounterOp
|
||||||
this.excludePrimaryFromSecondaryRequirements = excludePrimaryFromSecondaryRequirements;
|
this.excludePrimaryFromSecondaryRequirements = excludePrimaryFromSecondaryRequirements;
|
||||||
return Object.assign(this, { secondaryPokemonRequirements: this.secondaryPokemonRequirements });
|
return Object.assign(this, { secondaryPokemonRequirements: this.secondaryPokemonRequirements });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withDialogue(dialogue: OptionTextDisplay) {
|
||||||
|
this.dialogue = dialogue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { TrainerConfig, trainerConfigs, TrainerSlot } from "../trainer-config";
|
||||||
import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon";
|
||||||
import Trainer, { TrainerVariant } from "../../field/trainer";
|
import Trainer, { TrainerVariant } from "../../field/trainer";
|
||||||
import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier";
|
import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier";
|
||||||
import { CustomModifierSettings, getModifierPoolForType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeGenerator, modifierTypes, PokemonHeldItemModifierType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
import { CustomModifierSettings, getModifierPoolForType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, PokemonHeldItemModifierType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
import { BattleEndPhase, EggLapsePhase, ExpPhase, ModifierRewardPhase, SelectModifierPhase, ShowPartyExpBarPhase, TrainerVictoryPhase } from "#app/phases";
|
import { BattleEndPhase, EggLapsePhase, ExpPhase, ModifierRewardPhase, SelectModifierPhase, ShowPartyExpBarPhase, TrainerVictoryPhase } from "#app/phases";
|
||||||
import { MysteryEncounterBattlePhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase";
|
import { MysteryEncounterBattlePhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phase";
|
||||||
import * as Utils from "../../utils";
|
import * as Utils from "../../utils";
|
||||||
|
@ -443,7 +443,7 @@ export function updatePlayerMoney(scene: BattleScene, changeValue: number, playS
|
||||||
* @param modifier
|
* @param modifier
|
||||||
* @param pregenArgs - can specify BerryType for berries, TM for TMs, AttackBoostType for item, etc.
|
* @param pregenArgs - can specify BerryType for berries, TM for TMs, AttackBoostType for item, etc.
|
||||||
*/
|
*/
|
||||||
export function generateModifierType(scene: BattleScene, modifier: () => ModifierType, pregenArgs?: any[]): ModifierType {
|
export function generateModifierTypeOption(scene: BattleScene, modifier: () => ModifierType, pregenArgs?: any[]): ModifierTypeOption {
|
||||||
const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifier);
|
const modifierId = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifier);
|
||||||
let result: ModifierType = modifierTypes[modifierId]?.();
|
let result: ModifierType = modifierTypes[modifierId]?.();
|
||||||
|
|
||||||
|
@ -463,7 +463,7 @@ export function generateModifierType(scene: BattleScene, modifier: () => Modifie
|
||||||
});
|
});
|
||||||
|
|
||||||
result = result instanceof ModifierTypeGenerator ? result.generateType(scene.getParty(), pregenArgs) : result;
|
result = result instanceof ModifierTypeGenerator ? result.generateType(scene.getParty(), pregenArgs) : result;
|
||||||
return result;
|
return new ModifierTypeOption(result, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -593,6 +593,7 @@ export function setEncounterRewards(scene: BattleScene, customShopRewards?: Cust
|
||||||
* 290 - trio legendaries
|
* 290 - trio legendaries
|
||||||
* 340 - box legendaries
|
* 340 - box legendaries
|
||||||
* 608 - Blissey (highest in game)
|
* 608 - Blissey (highest in game)
|
||||||
|
* https://bulbapedia.bulbagarden.net/wiki/List_of_Pok%C3%A9mon_by_effort_value_yield_(Generation_IX)
|
||||||
* @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue
|
* @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue
|
||||||
*/
|
*/
|
||||||
export function setEncounterExp(scene: BattleScene, participantId: integer | integer[], baseExpValue: number, useWaveIndex: boolean = true) {
|
export function setEncounterExp(scene: BattleScene, participantId: integer | integer[], baseExpValue: number, useWaveIndex: boolean = true) {
|
||||||
|
|
|
@ -7,9 +7,9 @@ import MysteryEncounterIntroVisuals, { MysteryEncounterSpriteConfig } from "../.
|
||||||
import * as Utils from "../../utils";
|
import * as Utils from "../../utils";
|
||||||
import { StatusEffect } from "../status-effect";
|
import { StatusEffect } from "../status-effect";
|
||||||
import MysteryEncounterDialogue, {
|
import MysteryEncounterDialogue, {
|
||||||
allMysteryEncounterDialogue
|
OptionTextDisplay
|
||||||
} from "./mystery-encounter-dialogue";
|
} from "./mystery-encounter-dialogue";
|
||||||
import MysteryEncounterOption, { MysteryEncounterOptionBuilder, OptionPhaseCallback } from "./mystery-encounter-option";
|
import MysteryEncounterOption, { EncounterOptionMode, MysteryEncounterOptionBuilder, OptionPhaseCallback } from "./mystery-encounter-option";
|
||||||
import {
|
import {
|
||||||
EncounterPokemonRequirement,
|
EncounterPokemonRequirement,
|
||||||
EncounterSceneRequirement,
|
EncounterSceneRequirement,
|
||||||
|
@ -35,7 +35,7 @@ export enum MysteryEncounterTier {
|
||||||
MASTER // Not currently used
|
MASTER // Not currently used
|
||||||
}
|
}
|
||||||
|
|
||||||
export default interface MysteryEncounter {
|
export default interface IMysteryEncounter {
|
||||||
/**
|
/**
|
||||||
* Required params
|
* Required params
|
||||||
*/
|
*/
|
||||||
|
@ -120,6 +120,7 @@ export default interface MysteryEncounter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic property to set any custom data required for the encounter
|
* Generic property to set any custom data required for the encounter
|
||||||
|
* Extremely useful for carrying state/data between onPreOptionPhase/onOptionPhase/onPostOptionPhase
|
||||||
*/
|
*/
|
||||||
misc?: any;
|
misc?: any;
|
||||||
}
|
}
|
||||||
|
@ -129,23 +130,18 @@ export default interface MysteryEncounter {
|
||||||
* These objects will be saved as part of session data any time the player is on a floor with an encounter
|
* These objects will be saved as part of session data any time the player is on a floor with an encounter
|
||||||
* Unless you know what you're doing, you should use MysteryEncounterBuilder to create an instance for this class
|
* Unless you know what you're doing, you should use MysteryEncounterBuilder to create an instance for this class
|
||||||
*/
|
*/
|
||||||
export default class MysteryEncounter implements MysteryEncounter {
|
export default class IMysteryEncounter implements IMysteryEncounter {
|
||||||
constructor(encounter: MysteryEncounter) {
|
constructor(encounter: IMysteryEncounter) {
|
||||||
if (!isNullOrUndefined(encounter)) {
|
if (!isNullOrUndefined(encounter)) {
|
||||||
Object.assign(this, encounter);
|
Object.assign(this, encounter);
|
||||||
}
|
}
|
||||||
this.encounterTier = this.encounterTier ? this.encounterTier : MysteryEncounterTier.COMMON;
|
this.encounterTier = this.encounterTier ? this.encounterTier : MysteryEncounterTier.COMMON;
|
||||||
this.dialogue = allMysteryEncounterDialogue[this.encounterType];
|
this.dialogue = this.dialogue ?? {};
|
||||||
this.encounterVariant = MysteryEncounterVariant.DEFAULT;
|
this.encounterVariant = MysteryEncounterVariant.DEFAULT;
|
||||||
this.requirements = this.requirements ? this.requirements : [];
|
this.requirements = this.requirements ? this.requirements : [];
|
||||||
this.hideBattleIntroMessage = !isNullOrUndefined(this.hideBattleIntroMessage) ? this.hideBattleIntroMessage : false;
|
this.hideBattleIntroMessage = !isNullOrUndefined(this.hideBattleIntroMessage) ? this.hideBattleIntroMessage : false;
|
||||||
this.hideIntroVisuals = !isNullOrUndefined(this.hideIntroVisuals) ? this.hideIntroVisuals : true;
|
this.hideIntroVisuals = !isNullOrUndefined(this.hideIntroVisuals) ? this.hideIntroVisuals : true;
|
||||||
|
|
||||||
// Populate options with respective dialogue
|
|
||||||
if (this.dialogue) {
|
|
||||||
this.options.forEach((o, i) => o.dialogue = this.dialogue.encounterOptionsDialogue.options[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset any dirty flags or encounter data
|
// Reset any dirty flags or encounter data
|
||||||
this.lockEncounterRewardTiers = true;
|
this.lockEncounterRewardTiers = true;
|
||||||
this.dialogueTokens = new Map<string, [RegExp, string]>;
|
this.dialogueTokens = new Map<string, [RegExp, string]>;
|
||||||
|
@ -276,6 +272,7 @@ export default class MysteryEncounter implements MysteryEncounter {
|
||||||
* For multiple support pokemon in the dialogue token, it will have to be overridden.
|
* For multiple support pokemon in the dialogue token, it will have to be overridden.
|
||||||
*/
|
*/
|
||||||
populateDialogueTokensFromRequirements?(scene: BattleScene) {
|
populateDialogueTokensFromRequirements?(scene: BattleScene) {
|
||||||
|
this.meetsRequirements(scene);
|
||||||
if (this.requirements?.length > 0) {
|
if (this.requirements?.length > 0) {
|
||||||
for (const req of this.requirements) {
|
for (const req of this.requirements) {
|
||||||
const dialogueToken = req.getDialogueToken(scene);
|
const dialogueToken = req.getDialogueToken(scene);
|
||||||
|
@ -304,6 +301,7 @@ export default class MysteryEncounter implements MysteryEncounter {
|
||||||
// Dialogue tokens for options
|
// Dialogue tokens for options
|
||||||
for (let i = 0; i < this.options.length; i++) {
|
for (let i = 0; i < this.options.length; i++) {
|
||||||
const opt = this.options[i];
|
const opt = this.options[i];
|
||||||
|
opt.meetsRequirements(scene);
|
||||||
const j = i + 1;
|
const j = i + 1;
|
||||||
if (opt.requirements?.length > 0) {
|
if (opt.requirements?.length > 0) {
|
||||||
for (const req of opt.requirements) {
|
for (const req of opt.requirements) {
|
||||||
|
@ -316,7 +314,7 @@ export default class MysteryEncounter implements MysteryEncounter {
|
||||||
for (const req of opt.primaryPokemonRequirements) {
|
for (const req of opt.primaryPokemonRequirements) {
|
||||||
if (!req.invertQuery) {
|
if (!req.invertQuery) {
|
||||||
const value = req.getDialogueToken(scene, opt.primaryPokemon);
|
const value = req.getDialogueToken(scene, opt.primaryPokemon);
|
||||||
this.setDialogueToken("option" + j + "Primary", value[1]);
|
this.setDialogueToken("option" + j + "Primary" + this.capitalizeFirstLetter(value[0]), value[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,7 +323,7 @@ export default class MysteryEncounter implements MysteryEncounter {
|
||||||
for (const req of opt.secondaryPokemonRequirements) {
|
for (const req of opt.secondaryPokemonRequirements) {
|
||||||
if (!req.invertQuery) {
|
if (!req.invertQuery) {
|
||||||
const value = req.getDialogueToken(scene, opt.secondaryPokemon[0]);
|
const value = req.getDialogueToken(scene, opt.secondaryPokemon[0]);
|
||||||
this.setDialogueToken("option" + j + "Secondary", value[1]);
|
this.setDialogueToken("option" + j + "Secondary" + this.capitalizeFirstLetter(value[0]), value[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,7 +339,7 @@ export default class MysteryEncounter implements MysteryEncounter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
||||||
encounterType?: MysteryEncounterType;
|
encounterType?: MysteryEncounterType;
|
||||||
options?: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]] = [null, null];
|
options?: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]] = [null, null];
|
||||||
spriteConfigs?: MysteryEncounterSpriteConfig[];
|
spriteConfigs?: MysteryEncounterSpriteConfig[];
|
||||||
|
@ -370,7 +368,7 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* @param encounterType
|
* @param encounterType
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
static withEncounterType(encounterType: MysteryEncounterType): MysteryEncounterBuilder & Pick<MysteryEncounter, "encounterType"> {
|
static withEncounterType(encounterType: MysteryEncounterType): MysteryEncounterBuilder & Pick<IMysteryEncounter, "encounterType"> {
|
||||||
return Object.assign(new MysteryEncounterBuilder(), { encounterType: encounterType });
|
return Object.assign(new MysteryEncounterBuilder(), { encounterType: encounterType });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,7 +381,7 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* @param option - MysteryEncounterOption to add, can use MysteryEncounterOptionBuilder to create instance
|
* @param option - MysteryEncounterOption to add, can use MysteryEncounterOptionBuilder to create instance
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withOption(option: MysteryEncounterOption): this & Pick<MysteryEncounter, "options"> {
|
withOption(option: MysteryEncounterOption): this & Pick<IMysteryEncounter, "options"> {
|
||||||
if (this.options[0] === null) {
|
if (this.options[0] === null) {
|
||||||
return Object.assign(this, { options: [option, this.options[0]] });
|
return Object.assign(this, { options: [option, this.options[0]] });
|
||||||
} else if (this.options[1] === null) {
|
} else if (this.options[1] === null) {
|
||||||
|
@ -400,11 +398,12 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* 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 complex use {@linkcode MysteryEncounterBuilder.withOption}
|
* If complex use {@linkcode MysteryEncounterBuilder.withOption}
|
||||||
*
|
*
|
||||||
* @param callback - OptionPhaseCallback
|
* @param dialogue - {@linkcode OptionTextDisplay}
|
||||||
|
* @param callback - {@linkcode OptionPhaseCallback}
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withOptionPhase(callback: OptionPhaseCallback) {
|
withSimpleOption(dialogue: OptionTextDisplay, callback: OptionPhaseCallback) {
|
||||||
return this.withOption(new MysteryEncounterOptionBuilder().withOptionPhase(callback).build());
|
return this.withOption(new MysteryEncounterOptionBuilder().withOptionMode(EncounterOptionMode.DEFAULT).withDialogue(dialogue).withOptionPhase(callback).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -413,10 +412,19 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* @param spriteConfigs
|
* @param spriteConfigs
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withIntroSpriteConfigs(spriteConfigs: MysteryEncounterSpriteConfig[]): this & Pick<MysteryEncounter, "spriteConfigs"> {
|
withIntroSpriteConfigs(spriteConfigs: MysteryEncounterSpriteConfig[]): this & Pick<IMysteryEncounter, "spriteConfigs"> {
|
||||||
return Object.assign(this, { spriteConfigs: spriteConfigs });
|
return Object.assign(this, { spriteConfigs: spriteConfigs });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
withIntroDialogue(dialogue: MysteryEncounterDialogue["intro"] = []) {
|
||||||
|
this.dialogue = {...this.dialogue, intro: dialogue };
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
withIntro({spriteConfigs, dialogue} : {spriteConfigs: MysteryEncounterSpriteConfig[], dialogue?: MysteryEncounterDialogue["intro"]}) {
|
||||||
|
return this.withIntroSpriteConfigs(spriteConfigs).withIntroDialogue(dialogue);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OPTIONAL
|
* OPTIONAL
|
||||||
*/
|
*/
|
||||||
|
@ -433,7 +441,7 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* @param encounterTier
|
* @param encounterTier
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withEncounterTier(encounterTier: MysteryEncounterTier): this & Required<Pick<MysteryEncounter, "encounterTier">> {
|
withEncounterTier(encounterTier: MysteryEncounterTier): this & Required<Pick<IMysteryEncounter, "encounterTier">> {
|
||||||
return Object.assign(this, { encounterTier: encounterTier });
|
return Object.assign(this, { encounterTier: encounterTier });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +452,7 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* @param requirement
|
* @param requirement
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withSceneRequirement(requirement: EncounterSceneRequirement): this & Required<Pick<MysteryEncounter, "requirements">> {
|
withSceneRequirement(requirement: EncounterSceneRequirement): this & Required<Pick<IMysteryEncounter, "requirements">> {
|
||||||
if (requirement instanceof EncounterPokemonRequirement) {
|
if (requirement instanceof EncounterPokemonRequirement) {
|
||||||
Error("Incorrectly added pokemon requirement as scene requirement.");
|
Error("Incorrectly added pokemon requirement as scene requirement.");
|
||||||
}
|
}
|
||||||
|
@ -474,23 +482,45 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
return this.withSceneRequirement(new PartySizeRequirement([min, max ?? min]));
|
return this.withSceneRequirement(new PartySizeRequirement([min, max ?? min]));
|
||||||
}
|
}
|
||||||
|
|
||||||
withPrimaryPokemonRequirement(requirement: EncounterPokemonRequirement): this & Required<Pick<MysteryEncounter, "primaryPokemonRequirements">> {
|
/**
|
||||||
|
* Add a primary pokemon requirement
|
||||||
|
*
|
||||||
|
* @param requirement {@linkcode EncounterPokemonRequirement}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
withPrimaryPokemonRequirement(requirement: EncounterPokemonRequirement): this & Required<Pick<IMysteryEncounter, "primaryPokemonRequirements">> {
|
||||||
this.primaryPokemonRequirements.push(requirement);
|
this.primaryPokemonRequirements.push(requirement);
|
||||||
return Object.assign(this, { primaryPokemonRequirements: this.primaryPokemonRequirements });
|
return Object.assign(this, { primaryPokemonRequirements: this.primaryPokemonRequirements });
|
||||||
}
|
}
|
||||||
|
|
||||||
withPrimaryPokemonStatusEffectRequirement(statusEffect: StatusEffect | StatusEffect[], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required<Pick<MysteryEncounter, "primaryPokemonRequirements">> {
|
/**
|
||||||
|
* Add a primary pokemon status effect requirement
|
||||||
|
*
|
||||||
|
* @param statusEffect the status effect/s to check
|
||||||
|
* @param minNumberOfPokemon minimum number of pokemon to have the effect
|
||||||
|
* @param invertQuery if true will invert the query
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
withPrimaryPokemonStatusEffectRequirement(statusEffect: StatusEffect | StatusEffect[], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required<Pick<IMysteryEncounter, "primaryPokemonRequirements">> {
|
||||||
return this.withPrimaryPokemonRequirement(new StatusEffectRequirement(statusEffect, minNumberOfPokemon, invertQuery));
|
return this.withPrimaryPokemonRequirement(new StatusEffectRequirement(statusEffect, minNumberOfPokemon, invertQuery));
|
||||||
}
|
}
|
||||||
|
|
||||||
withPrimaryPokemonHealthRatioRequirement(requiredHealthRange: [number, number], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required<Pick<MysteryEncounter, "primaryPokemonRequirements">> {
|
/**
|
||||||
|
* Add a primary pokemon health ratio requirement
|
||||||
|
*
|
||||||
|
* @param requiredHealthRange the health range to check
|
||||||
|
* @param minNumberOfPokemon minimum number of pokemon to have the health range
|
||||||
|
* @param invertQuery if true will invert the query
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
withPrimaryPokemonHealthRatioRequirement(requiredHealthRange: [number, number], minNumberOfPokemon: number = 1, invertQuery: boolean = false): this & Required<Pick<IMysteryEncounter, "primaryPokemonRequirements">> {
|
||||||
return this.withPrimaryPokemonRequirement(new HealthRatioRequirement(requiredHealthRange, minNumberOfPokemon, invertQuery));
|
return this.withPrimaryPokemonRequirement(new HealthRatioRequirement(requiredHealthRange, minNumberOfPokemon, invertQuery));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Maybe add an optional parameter for excluding primary pokemon from the support cast?
|
// TODO: Maybe add an optional parameter for excluding primary pokemon from the support cast?
|
||||||
// ex. if your only grass type pokemon, a snivy, is chosen as primary, if the support pokemon requires a grass type, the event won't trigger because
|
// ex. if your only grass type pokemon, a snivy, is chosen as primary, if the support pokemon requires a grass type, the event won't trigger because
|
||||||
// it's already been
|
// it's already been
|
||||||
withSecondaryPokemonRequirement(requirement: EncounterPokemonRequirement, excludePrimaryFromSecondaryRequirements: boolean = false): this & Required<Pick<MysteryEncounter, "secondaryPokemonRequirements">> {
|
withSecondaryPokemonRequirement(requirement: EncounterPokemonRequirement, excludePrimaryFromSecondaryRequirements: boolean = false): this & Required<Pick<IMysteryEncounter, "secondaryPokemonRequirements">> {
|
||||||
this.secondaryPokemonRequirements.push(requirement);
|
this.secondaryPokemonRequirements.push(requirement);
|
||||||
this.excludePrimaryFromSupportRequirements = excludePrimaryFromSecondaryRequirements;
|
this.excludePrimaryFromSupportRequirements = excludePrimaryFromSecondaryRequirements;
|
||||||
return Object.assign(this, { excludePrimaryFromSecondaryRequirements: this.excludePrimaryFromSupportRequirements, secondaryPokemonRequirements: this.secondaryPokemonRequirements });
|
return Object.assign(this, { excludePrimaryFromSecondaryRequirements: this.excludePrimaryFromSupportRequirements, secondaryPokemonRequirements: this.secondaryPokemonRequirements });
|
||||||
|
@ -506,7 +536,7 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* @param doEncounterRewards - synchronous callback function to perform during rewards phase of the encounter
|
* @param doEncounterRewards - synchronous callback function to perform during rewards phase of the encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withRewards(doEncounterRewards: (scene: BattleScene) => boolean): this & Required<Pick<MysteryEncounter, "doEncounterRewards">> {
|
withRewards(doEncounterRewards: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterRewards">> {
|
||||||
return Object.assign(this, { doEncounterRewards: doEncounterRewards });
|
return Object.assign(this, { doEncounterRewards: doEncounterRewards });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,7 +550,7 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* @param doEncounterExp - synchronous callback function to perform during rewards phase of the encounter
|
* @param doEncounterExp - synchronous callback function to perform during rewards phase of the encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withExp(doEncounterExp: (scene: BattleScene) => boolean): this & Required<Pick<MysteryEncounter, "doEncounterExp">> {
|
withExp(doEncounterExp: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "doEncounterExp">> {
|
||||||
return Object.assign(this, { doEncounterExp: doEncounterExp });
|
return Object.assign(this, { doEncounterExp: doEncounterExp });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,7 +561,7 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* @param onInit - synchronous callback function to perform as soon as the encounter is selected for the next phase
|
* @param onInit - synchronous callback function to perform as soon as the encounter is selected for the next phase
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withOnInit(onInit: (scene: BattleScene) => boolean): this & Required<Pick<MysteryEncounter, "onInit">> {
|
withOnInit(onInit: (scene: BattleScene) => boolean): this & Required<Pick<IMysteryEncounter, "onInit">> {
|
||||||
return Object.assign(this, { onInit: onInit });
|
return Object.assign(this, { onInit: onInit });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,7 +570,7 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* @param enemyPartyConfig
|
* @param enemyPartyConfig
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withEnemyPartyConfig(enemyPartyConfig: EnemyPartyConfig): this & Required<Pick<MysteryEncounter, "enemyPartyConfigs">> {
|
withEnemyPartyConfig(enemyPartyConfig: EnemyPartyConfig): this & Required<Pick<IMysteryEncounter, "enemyPartyConfigs">> {
|
||||||
this.enemyPartyConfigs.push(enemyPartyConfig);
|
this.enemyPartyConfigs.push(enemyPartyConfig);
|
||||||
return Object.assign(this, { enemyPartyConfigs: this.enemyPartyConfigs });
|
return Object.assign(this, { enemyPartyConfigs: this.enemyPartyConfigs });
|
||||||
}
|
}
|
||||||
|
@ -551,7 +581,7 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* @param catchAllowed - if true, allows enemy pokemon to be caught during the encounter
|
* @param catchAllowed - if true, allows enemy pokemon to be caught during the encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withCatchAllowed(catchAllowed: boolean): this & Required<Pick<MysteryEncounter, "catchAllowed">> {
|
withCatchAllowed(catchAllowed: boolean): this & Required<Pick<IMysteryEncounter, "catchAllowed">> {
|
||||||
return Object.assign(this, { catchAllowed: catchAllowed });
|
return Object.assign(this, { catchAllowed: catchAllowed });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,7 +589,7 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* @param hideBattleIntroMessage - if true, will not show the trainerAppeared/wildAppeared/bossAppeared message for an encounter
|
* @param hideBattleIntroMessage - if true, will not show the trainerAppeared/wildAppeared/bossAppeared message for an encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withHideWildIntroMessage(hideBattleIntroMessage: boolean): this & Required<Pick<MysteryEncounter, "hideBattleIntroMessage">> {
|
withHideWildIntroMessage(hideBattleIntroMessage: boolean): this & Required<Pick<IMysteryEncounter, "hideBattleIntroMessage">> {
|
||||||
return Object.assign(this, { hideBattleIntroMessage: hideBattleIntroMessage });
|
return Object.assign(this, { hideBattleIntroMessage: hideBattleIntroMessage });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -567,11 +597,88 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
||||||
* @param hideIntroVisuals - if false, will not hide the intro visuals that are displayed at the beginning of encounter
|
* @param hideIntroVisuals - if false, will not hide the intro visuals that are displayed at the beginning of encounter
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withHideIntroVisuals(hideIntroVisuals: boolean): this & Required<Pick<MysteryEncounter, "hideIntroVisuals">> {
|
withHideIntroVisuals(hideIntroVisuals: boolean): this & Required<Pick<IMysteryEncounter, "hideIntroVisuals">> {
|
||||||
return Object.assign(this, { hideIntroVisuals: hideIntroVisuals });
|
return Object.assign(this, { hideIntroVisuals: hideIntroVisuals });
|
||||||
}
|
}
|
||||||
|
|
||||||
build(this: MysteryEncounter) {
|
/**
|
||||||
return new MysteryEncounter(this);
|
* Add a title for the encounter
|
||||||
|
*
|
||||||
|
* @param title - title of the encounter
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
withTitle(title: TemplateStringsArray | `mysteryEncounter:${string}`) {
|
||||||
|
const encounterOptionsDialogue = this.dialogue.encounterOptionsDialogue ?? {};
|
||||||
|
|
||||||
|
this.dialogue = {
|
||||||
|
...this.dialogue,
|
||||||
|
encounterOptionsDialogue: {
|
||||||
|
...encounterOptionsDialogue,
|
||||||
|
title,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a description of the encounter
|
||||||
|
*
|
||||||
|
* @param description - description of the encounter
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
withDescription(description: TemplateStringsArray | `mysteryEncounter:${string}`) {
|
||||||
|
const encounterOptionsDialogue = this.dialogue.encounterOptionsDialogue ?? {};
|
||||||
|
|
||||||
|
this.dialogue = {
|
||||||
|
...this.dialogue,
|
||||||
|
encounterOptionsDialogue: {
|
||||||
|
...encounterOptionsDialogue,
|
||||||
|
description,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a query for the encounter
|
||||||
|
*
|
||||||
|
* @param query - query to use for the encounter
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
withQuery(query: TemplateStringsArray | `mysteryEncounter:${string}`) {
|
||||||
|
const encounterOptionsDialogue = this.dialogue.encounterOptionsDialogue ?? {};
|
||||||
|
|
||||||
|
this.dialogue = {
|
||||||
|
...this.dialogue,
|
||||||
|
encounterOptionsDialogue: {
|
||||||
|
...encounterOptionsDialogue,
|
||||||
|
query,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add outro dialogue/s for the encounter
|
||||||
|
*
|
||||||
|
* @param dialogue - outro dialogue/s
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
withOutroDialogue(dialogue: MysteryEncounterDialogue["outro"] = []) {
|
||||||
|
this.dialogue = {...this.dialogue, outro: dialogue };
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the mystery encounter
|
||||||
|
*
|
||||||
|
* @param this - MysteryEncounter
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
build(this: IMysteryEncounter) {
|
||||||
|
return new IMysteryEncounter(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import MysteryEncounter from "./mystery-encounter";
|
import IMysteryEncounter from "./mystery-encounter";
|
||||||
import { DarkDealEncounter } from "./encounters/dark-deal";
|
import { DarkDealEncounter } from "./encounters/dark-deal-encounter";
|
||||||
import { MysteriousChallengersEncounter } from "./encounters/mysterious-challengers";
|
import { MysteriousChallengersEncounter } from "./encounters/mysterious-challengers-encounter";
|
||||||
import { MysteriousChestEncounter } from "./encounters/mysterious-chest";
|
import { MysteriousChestEncounter } from "./encounters/mysterious-chest-encounter";
|
||||||
import { FightOrFlightEncounter } from "#app/data/mystery-encounters/encounters/fight-or-flight";
|
import { FightOrFlightEncounter } from "./encounters/fight-or-flight-encounter";
|
||||||
import { TrainingSessionEncounter } from "#app/data/mystery-encounters/encounters/training-session";
|
import { TrainingSessionEncounter } from "./encounters/training-session-encounter";
|
||||||
import { Biome } from "#enums/biome";
|
import { Biome } from "#enums/biome";
|
||||||
import { SleepingSnorlaxEncounter } from "./encounters/sleeping-snorlax";
|
import { SleepingSnorlaxEncounter } from "./encounters/sleeping-snorlax-encounter";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { DepartmentStoreSaleEncounter } from "#app/data/mystery-encounters/encounters/department-store-sale";
|
import { DepartmentStoreSaleEncounter } from "./encounters/department-store-sale-encounter";
|
||||||
import { ShadyVitaminDealerEncounter } from "#app/data/mystery-encounters/encounters/shady-vitamin-dealer";
|
import { ShadyVitaminDealerEncounter } from "./encounters/shady-vitamin-dealer-encounter";
|
||||||
import { LostAtSeaEncounter } from "./encounters/lost-at-sea-encounter";
|
import { LostAtSeaEncounter } from "./encounters/lost-at-sea-encounter";
|
||||||
|
|
||||||
// Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256
|
// Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256
|
||||||
|
@ -16,27 +16,151 @@ export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1;
|
||||||
export const WIGHT_INCREMENT_ON_SPAWN_MISS = 5;
|
export const WIGHT_INCREMENT_ON_SPAWN_MISS = 5;
|
||||||
export const AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 15;
|
export const AVERAGE_ENCOUNTERS_PER_RUN_TARGET = 15;
|
||||||
|
|
||||||
export const allMysteryEncounters: { [encounterType: number]: MysteryEncounter } = {};
|
export const EXTREME_ENCOUNTER_BIOMES = [
|
||||||
|
Biome.SEA,
|
||||||
|
Biome.SEABED,
|
||||||
|
Biome.BADLANDS,
|
||||||
|
Biome.DESERT,
|
||||||
|
Biome.ICE_CAVE,
|
||||||
|
Biome.VOLCANO,
|
||||||
|
Biome.WASTELAND,
|
||||||
|
Biome.ABYSS,
|
||||||
|
Biome.SPACE,
|
||||||
|
Biome.END
|
||||||
|
];
|
||||||
|
|
||||||
// Add MysteryEncounterType to biomes to enable it exclusively for those biomes
|
export const NON_EXTREME_ENCOUNTER_BIOMES = [
|
||||||
// To enable an encounter in all biomes, do not add to this map
|
Biome.TOWN,
|
||||||
|
Biome.PLAINS,
|
||||||
|
Biome.GRASS,
|
||||||
|
Biome.TALL_GRASS,
|
||||||
|
Biome.METROPOLIS,
|
||||||
|
Biome.FOREST,
|
||||||
|
Biome.SWAMP,
|
||||||
|
Biome.BEACH,
|
||||||
|
Biome.LAKE,
|
||||||
|
Biome.MOUNTAIN,
|
||||||
|
Biome.CAVE,
|
||||||
|
Biome.MEADOW,
|
||||||
|
Biome.POWER_PLANT,
|
||||||
|
Biome.GRAVEYARD,
|
||||||
|
Biome.DOJO,
|
||||||
|
Biome.FACTORY,
|
||||||
|
Biome.RUINS,
|
||||||
|
Biome.CONSTRUCTION_SITE,
|
||||||
|
Biome.JUNGLE,
|
||||||
|
Biome.FAIRY_CAVE,
|
||||||
|
Biome.TEMPLE,
|
||||||
|
Biome.SLUM,
|
||||||
|
Biome.SNOWY_FOREST,
|
||||||
|
Biome.ISLAND,
|
||||||
|
Biome.LABORATORY
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places where you could very reasonably expect to encounter a single human
|
||||||
|
*
|
||||||
|
* Diff from NON_EXTREME_ENCOUNTER_BIOMES:
|
||||||
|
* + BADLANDS
|
||||||
|
* + DESERT
|
||||||
|
* + ICE_CAVE
|
||||||
|
*/
|
||||||
|
export const HUMAN_TRANSITABLE_BIOMES = [
|
||||||
|
Biome.TOWN,
|
||||||
|
Biome.PLAINS,
|
||||||
|
Biome.GRASS,
|
||||||
|
Biome.TALL_GRASS,
|
||||||
|
Biome.METROPOLIS,
|
||||||
|
Biome.FOREST,
|
||||||
|
Biome.SWAMP,
|
||||||
|
Biome.BEACH,
|
||||||
|
Biome.LAKE,
|
||||||
|
Biome.MOUNTAIN,
|
||||||
|
Biome.BADLANDS,
|
||||||
|
Biome.CAVE,
|
||||||
|
Biome.DESERT,
|
||||||
|
Biome.ICE_CAVE,
|
||||||
|
Biome.MEADOW,
|
||||||
|
Biome.POWER_PLANT,
|
||||||
|
Biome.GRAVEYARD,
|
||||||
|
Biome.DOJO,
|
||||||
|
Biome.FACTORY,
|
||||||
|
Biome.RUINS,
|
||||||
|
Biome.CONSTRUCTION_SITE,
|
||||||
|
Biome.JUNGLE,
|
||||||
|
Biome.FAIRY_CAVE,
|
||||||
|
Biome.TEMPLE,
|
||||||
|
Biome.SLUM,
|
||||||
|
Biome.SNOWY_FOREST,
|
||||||
|
Biome.ISLAND,
|
||||||
|
Biome.LABORATORY
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places where you could expect a town or city, some form of large civilization
|
||||||
|
*/
|
||||||
|
export const CIVILIZATION_ENCOUNTER_BIOMES = [
|
||||||
|
Biome.TOWN,
|
||||||
|
Biome.PLAINS,
|
||||||
|
Biome.GRASS,
|
||||||
|
Biome.TALL_GRASS,
|
||||||
|
Biome.METROPOLIS,
|
||||||
|
Biome.BEACH,
|
||||||
|
Biome.LAKE,
|
||||||
|
Biome.MEADOW,
|
||||||
|
Biome.POWER_PLANT,
|
||||||
|
Biome.GRAVEYARD,
|
||||||
|
Biome.DOJO,
|
||||||
|
Biome.FACTORY,
|
||||||
|
Biome.CONSTRUCTION_SITE,
|
||||||
|
Biome.SLUM,
|
||||||
|
Biome.ISLAND
|
||||||
|
];
|
||||||
|
|
||||||
|
export const allMysteryEncounters: { [encounterType: number]: IMysteryEncounter } = {};
|
||||||
|
|
||||||
|
|
||||||
|
const extremeBiomeEncounters: MysteryEncounterType[] = [];
|
||||||
|
|
||||||
|
const nonExtremeBiomeEncounters: MysteryEncounterType[] = [
|
||||||
|
MysteryEncounterType.FIELD_TRIP
|
||||||
|
];
|
||||||
|
|
||||||
|
const humanTransitableBiomeEncounters: MysteryEncounterType[] = [
|
||||||
|
MysteryEncounterType.MYSTERIOUS_CHALLENGERS,
|
||||||
|
MysteryEncounterType.SHADY_VITAMIN_DEALER
|
||||||
|
];
|
||||||
|
|
||||||
|
const civilizationBiomeEncounters: MysteryEncounterType[] = [
|
||||||
|
MysteryEncounterType.DEPARTMENT_STORE_SALE
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To add an encounter to every biome possible, use this array
|
||||||
|
*/
|
||||||
|
const anyBiomeEncounters: MysteryEncounterType[] = [
|
||||||
|
MysteryEncounterType.FIGHT_OR_FLIGHT,
|
||||||
|
MysteryEncounterType.DARK_DEAL,
|
||||||
|
MysteryEncounterType.MYSTERIOUS_CHEST,
|
||||||
|
MysteryEncounterType.TRAINING_SESSION
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ENCOUNTER BIOME MAPPING
|
||||||
|
* To add an Encounter to a biome group, instead of cluttering the map, use the biome group arrays above
|
||||||
|
*
|
||||||
|
* Adding specific Encounters to the mysteryEncountersByBiome map is for specific cases and special circumstances
|
||||||
|
* that biome groups do not cover
|
||||||
|
*/
|
||||||
export const mysteryEncountersByBiome = new Map<Biome, MysteryEncounterType[]>([
|
export const mysteryEncountersByBiome = new Map<Biome, MysteryEncounterType[]>([
|
||||||
[Biome.TOWN, [
|
[Biome.TOWN, []],
|
||||||
MysteryEncounterType.DEPARTMENT_STORE_SALE
|
[Biome.PLAINS, []],
|
||||||
]],
|
|
||||||
[Biome.PLAINS, [
|
|
||||||
MysteryEncounterType.DEPARTMENT_STORE_SALE
|
|
||||||
]],
|
|
||||||
[Biome.GRASS, [
|
[Biome.GRASS, [
|
||||||
MysteryEncounterType.SLEEPING_SNORLAX,
|
MysteryEncounterType.SLEEPING_SNORLAX,
|
||||||
MysteryEncounterType.DEPARTMENT_STORE_SALE
|
|
||||||
]],
|
|
||||||
[Biome.TALL_GRASS, [
|
|
||||||
MysteryEncounterType.DEPARTMENT_STORE_SALE
|
|
||||||
]],
|
|
||||||
[Biome.METROPOLIS, [
|
|
||||||
MysteryEncounterType.DEPARTMENT_STORE_SALE
|
|
||||||
]],
|
]],
|
||||||
|
[Biome.TALL_GRASS, []],
|
||||||
|
[Biome.METROPOLIS, []],
|
||||||
[Biome.FOREST, [
|
[Biome.FOREST, [
|
||||||
MysteryEncounterType.SLEEPING_SNORLAX
|
MysteryEncounterType.SLEEPING_SNORLAX
|
||||||
]],
|
]],
|
||||||
|
@ -45,9 +169,7 @@ export const mysteryEncountersByBiome = new Map<Biome, MysteryEncounterType[]>([
|
||||||
MysteryEncounterType.LOST_AT_SEA
|
MysteryEncounterType.LOST_AT_SEA
|
||||||
]],
|
]],
|
||||||
[Biome.SWAMP, []],
|
[Biome.SWAMP, []],
|
||||||
[Biome.BEACH, [
|
[Biome.BEACH, []],
|
||||||
MysteryEncounterType.DEPARTMENT_STORE_SALE
|
|
||||||
]],
|
|
||||||
[Biome.LAKE, []],
|
[Biome.LAKE, []],
|
||||||
[Biome.SEABED, []],
|
[Biome.SEABED, []],
|
||||||
[Biome.MOUNTAIN, [
|
[Biome.MOUNTAIN, [
|
||||||
|
@ -59,31 +181,21 @@ export const mysteryEncountersByBiome = new Map<Biome, MysteryEncounterType[]>([
|
||||||
]],
|
]],
|
||||||
[Biome.DESERT, []],
|
[Biome.DESERT, []],
|
||||||
[Biome.ICE_CAVE, []],
|
[Biome.ICE_CAVE, []],
|
||||||
[Biome.MEADOW, [
|
[Biome.MEADOW, []],
|
||||||
MysteryEncounterType.DEPARTMENT_STORE_SALE
|
[Biome.POWER_PLANT, []],
|
||||||
]],
|
|
||||||
[Biome.POWER_PLANT, [
|
|
||||||
MysteryEncounterType.DEPARTMENT_STORE_SALE
|
|
||||||
]],
|
|
||||||
[Biome.VOLCANO, []],
|
[Biome.VOLCANO, []],
|
||||||
[Biome.GRAVEYARD, []],
|
[Biome.GRAVEYARD, []],
|
||||||
[Biome.DOJO, []],
|
[Biome.DOJO, []],
|
||||||
[Biome.FACTORY, [
|
[Biome.FACTORY, []],
|
||||||
MysteryEncounterType.DEPARTMENT_STORE_SALE
|
|
||||||
]],
|
|
||||||
[Biome.RUINS, []],
|
[Biome.RUINS, []],
|
||||||
[Biome.WASTELAND, []],
|
[Biome.WASTELAND, []],
|
||||||
[Biome.ABYSS, []],
|
[Biome.ABYSS, []],
|
||||||
[Biome.SPACE, []],
|
[Biome.SPACE, []],
|
||||||
[Biome.CONSTRUCTION_SITE, [
|
[Biome.CONSTRUCTION_SITE, []],
|
||||||
MysteryEncounterType.DEPARTMENT_STORE_SALE
|
|
||||||
]],
|
|
||||||
[Biome.JUNGLE, []],
|
[Biome.JUNGLE, []],
|
||||||
[Biome.FAIRY_CAVE, []],
|
[Biome.FAIRY_CAVE, []],
|
||||||
[Biome.TEMPLE, []],
|
[Biome.TEMPLE, []],
|
||||||
[Biome.SLUM, [
|
[Biome.SLUM, []],
|
||||||
MysteryEncounterType.DEPARTMENT_STORE_SALE
|
|
||||||
]],
|
|
||||||
[Biome.SNOWY_FOREST, []],
|
[Biome.SNOWY_FOREST, []],
|
||||||
[Biome.ISLAND, []],
|
[Biome.ISLAND, []],
|
||||||
[Biome.LABORATORY, []]
|
[Biome.LABORATORY, []]
|
||||||
|
@ -98,17 +210,52 @@ export function initMysteryEncounters() {
|
||||||
allMysteryEncounters[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxEncounter;
|
allMysteryEncounters[MysteryEncounterType.SLEEPING_SNORLAX] = SleepingSnorlaxEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.DEPARTMENT_STORE_SALE] = DepartmentStoreSaleEncounter;
|
allMysteryEncounters[MysteryEncounterType.DEPARTMENT_STORE_SALE] = DepartmentStoreSaleEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerEncounter;
|
allMysteryEncounters[MysteryEncounterType.SHADY_VITAMIN_DEALER] = ShadyVitaminDealerEncounter;
|
||||||
|
allMysteryEncounters[MysteryEncounterType.FIELD_TRIP] = FieldTripEncounter;
|
||||||
allMysteryEncounters[MysteryEncounterType.LOST_AT_SEA] = LostAtSeaEncounter;
|
allMysteryEncounters[MysteryEncounterType.LOST_AT_SEA] = LostAtSeaEncounter;
|
||||||
|
|
||||||
// Append encounters that can occur in any biome to biome map
|
// Add extreme encounters to biome map
|
||||||
const anyBiomeEncounters: MysteryEncounterType[] = Object.keys(MysteryEncounterType).filter(e => !isNaN(Number(e))).map(k => Number(k) as MysteryEncounterType);
|
extremeBiomeEncounters.forEach(encounter => {
|
||||||
mysteryEncountersByBiome.forEach(biomeEncounters => {
|
EXTREME_ENCOUNTER_BIOMES.forEach(biome => {
|
||||||
biomeEncounters.forEach(e => {
|
const encountersForBiome = mysteryEncountersByBiome.get(biome);
|
||||||
if (anyBiomeEncounters.includes(e)) {
|
if (!encountersForBiome.includes(encounter)) {
|
||||||
anyBiomeEncounters.splice(anyBiomeEncounters.indexOf(e), 1);
|
encountersForBiome.push(encounter);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// Add non-extreme encounters to biome map
|
||||||
|
nonExtremeBiomeEncounters.forEach(encounter => {
|
||||||
|
NON_EXTREME_ENCOUNTER_BIOMES.forEach(biome => {
|
||||||
|
const encountersForBiome = mysteryEncountersByBiome.get(biome);
|
||||||
|
if (!encountersForBiome.includes(encounter)) {
|
||||||
|
encountersForBiome.push(encounter);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// Add human encounters to biome map
|
||||||
|
humanTransitableBiomeEncounters.forEach(encounter => {
|
||||||
|
HUMAN_TRANSITABLE_BIOMES.forEach(biome => {
|
||||||
|
const encountersForBiome = mysteryEncountersByBiome.get(biome);
|
||||||
|
if (!encountersForBiome.includes(encounter)) {
|
||||||
|
encountersForBiome.push(encounter);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// Add civilization encounters to biome map
|
||||||
|
civilizationBiomeEncounters.forEach(encounter => {
|
||||||
|
CIVILIZATION_ENCOUNTER_BIOMES.forEach(biome => {
|
||||||
|
const encountersForBiome = mysteryEncountersByBiome.get(biome);
|
||||||
|
if (!encountersForBiome.includes(encounter)) {
|
||||||
|
encountersForBiome.push(encounter);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
mysteryEncountersByBiome.forEach(biomeEncounters => biomeEncounters.push(...anyBiomeEncounters));
|
// Add ANY biome encounters to biome map
|
||||||
|
mysteryEncountersByBiome.forEach(biomeEncounters => {
|
||||||
|
anyBiomeEncounters.forEach(encounter => {
|
||||||
|
if (!biomeEncounters.includes(encounter)) {
|
||||||
|
biomeEncounters.push(encounter);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
|
||||||
|
export const STEALING_MOVES = [
|
||||||
|
Moves.PLUCK,
|
||||||
|
Moves.COVET,
|
||||||
|
Moves.KNOCK_OFF,
|
||||||
|
Moves.THIEF,
|
||||||
|
Moves.TRICK,
|
||||||
|
Moves.SWITCHEROO
|
||||||
|
];
|
||||||
|
|
||||||
|
export const DISTRACTION_MOVES = [
|
||||||
|
Moves.FAKE_OUT,
|
||||||
|
Moves.FOLLOW_ME,
|
||||||
|
Moves.TAUNT,
|
||||||
|
Moves.ROAR,
|
||||||
|
Moves.TELEPORT,
|
||||||
|
Moves.CHARM,
|
||||||
|
Moves.FAKE_TEARS,
|
||||||
|
Moves.TICKLE,
|
||||||
|
Moves.CAPTIVATE,
|
||||||
|
Moves.RAGE_POWDER,
|
||||||
|
Moves.SUBSTITUTE,
|
||||||
|
Moves.SHED_TAIL
|
||||||
|
];
|
||||||
|
|
||||||
|
export const PROTECTING_MOVES = [
|
||||||
|
Moves.PROTECT,
|
||||||
|
Moves.WIDE_GUARD,
|
||||||
|
Moves.MAX_GUARD,
|
||||||
|
Moves.SAFEGUARD,
|
||||||
|
Moves.REFLECT,
|
||||||
|
Moves.BARRIER,
|
||||||
|
Moves.QUICK_GUARD,
|
||||||
|
Moves.FLOWER_SHIELD,
|
||||||
|
Moves.KINGS_SHIELD,
|
||||||
|
Moves.CRAFTY_SHIELD,
|
||||||
|
Moves.SPIKY_SHIELD,
|
||||||
|
Moves.OBSTRUCT,
|
||||||
|
Moves.DETECT
|
||||||
|
];
|
|
@ -7,5 +7,6 @@ export enum MysteryEncounterType {
|
||||||
TRAINING_SESSION,
|
TRAINING_SESSION,
|
||||||
DEPARTMENT_STORE_SALE,
|
DEPARTMENT_STORE_SALE,
|
||||||
SHADY_VITAMIN_DEALER,
|
SHADY_VITAMIN_DEALER,
|
||||||
|
FIELD_TRIP,
|
||||||
LOST_AT_SEA //might be generalized later on
|
LOST_AT_SEA //might be generalized later on
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { GameObjects } from "phaser";
|
import { GameObjects } from "phaser";
|
||||||
import BattleScene from "../battle-scene";
|
import BattleScene from "../battle-scene";
|
||||||
import MysteryEncounter from "../data/mystery-encounters/mystery-encounter";
|
import IMysteryEncounter from "../data/mystery-encounters/mystery-encounter";
|
||||||
|
|
||||||
type KnownFileRoot =
|
type KnownFileRoot =
|
||||||
| "trainer"
|
| "trainer"
|
||||||
|
@ -52,10 +52,10 @@ export class MysteryEncounterSpriteConfig {
|
||||||
* Note: intro visuals are not "Trainers" or any other specific game object, though they may contain trainer sprites
|
* Note: intro visuals are not "Trainers" or any other specific game object, though they may contain trainer sprites
|
||||||
*/
|
*/
|
||||||
export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Container {
|
export default class MysteryEncounterIntroVisuals extends Phaser.GameObjects.Container {
|
||||||
public encounter: MysteryEncounter;
|
public encounter: IMysteryEncounter;
|
||||||
public spriteConfigs: MysteryEncounterSpriteConfig[];
|
public spriteConfigs: MysteryEncounterSpriteConfig[];
|
||||||
|
|
||||||
constructor(scene: BattleScene, encounter: MysteryEncounter) {
|
constructor(scene: BattleScene, encounter: IMysteryEncounter) {
|
||||||
super(scene, -72, 76);
|
super(scene, -72, 76);
|
||||||
this.encounter = encounter;
|
this.encounter = encounter;
|
||||||
// Shallow copy configs to allow visual config updates at runtime without dirtying master copy of Encounter
|
// Shallow copy configs to allow visual config updates at runtime without dirtying master copy of Encounter
|
||||||
|
|
|
@ -22,7 +22,6 @@ import { initStatsKeys } from "./ui/game-stats-ui-handler";
|
||||||
import { initVouchers } from "./system/voucher";
|
import { initVouchers } from "./system/voucher";
|
||||||
import { Biome } from "#enums/biome";
|
import { Biome } from "#enums/biome";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import {initMysteryEncounterDialogue} from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
|
||||||
import {initMysteryEncounters} from "#app/data/mystery-encounters/mystery-encounters";
|
import {initMysteryEncounters} from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
|
|
||||||
export class LoadingScene extends SceneBase {
|
export class LoadingScene extends SceneBase {
|
||||||
|
@ -346,7 +345,6 @@ export class LoadingScene extends SceneBase {
|
||||||
initMoves();
|
initMoves();
|
||||||
initAbilities();
|
initAbilities();
|
||||||
initChallenges();
|
initChallenges();
|
||||||
initMysteryEncounterDialogue();
|
|
||||||
initMysteryEncounters();
|
initMysteryEncounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ export const mysteryEncounter = {
|
||||||
"unit_test_dialogue": "@ec{test}@ec{test} @ec{test@ec{test}} @ec{test1} @ec{test\} @ec{test\\} @ec{test\\\} {test}",
|
"unit_test_dialogue": "@ec{test}@ec{test} @ec{test@ec{test}} @ec{test1} @ec{test\} @ec{test\\} @ec{test\\\} {test}",
|
||||||
|
|
||||||
// Mystery Encounters -- Common Tier
|
// Mystery Encounters -- Common Tier
|
||||||
|
|
||||||
"mysterious_chest_intro_message": "You found...@d{32} a chest?",
|
"mysterious_chest_intro_message": "You found...@d{32} a chest?",
|
||||||
"mysterious_chest_title": "The Mysterious Chest",
|
"mysterious_chest_title": "The Mysterious Chest",
|
||||||
"mysterious_chest_description": "A beautifully ornamented chest stands on the ground. There must be something good inside... right?",
|
"mysterious_chest_description": "A beautifully ornamented chest stands on the ground. There must be something good inside... right?",
|
||||||
|
@ -42,14 +43,14 @@ export const mysteryEncounter = {
|
||||||
"fight_or_flight_option_1_tooltip": "(-) Hard Battle\n(+) New Item",
|
"fight_or_flight_option_1_tooltip": "(-) Hard Battle\n(+) New Item",
|
||||||
"fight_or_flight_option_2_label": "Steal the item",
|
"fight_or_flight_option_2_label": "Steal the item",
|
||||||
"fight_or_flight_option_2_tooltip": "@[SUMMARY_GREEN]{(35%) Steal Item}\n@[SUMMARY_BLUE]{(65%) Harder Battle}",
|
"fight_or_flight_option_2_tooltip": "@[SUMMARY_GREEN]{(35%) Steal Item}\n@[SUMMARY_BLUE]{(65%) Harder Battle}",
|
||||||
"fight_or_flight_option_2_steal_tooltip": "@[SUMMARY_GREEN]{(?) Use a Pokémon Move}",
|
"fight_or_flight_option_2_steal_tooltip": "(+) @ec{option2PrimaryName} uses @ec{option2PrimaryMove}",
|
||||||
"fight_or_flight_option_3_label": "Leave",
|
"fight_or_flight_option_3_label": "Leave",
|
||||||
"fight_or_flight_option_3_tooltip": "(-) No Rewards",
|
"fight_or_flight_option_3_tooltip": "(-) No Rewards",
|
||||||
"fight_or_flight_option_1_selected_message": "You approach the\nPokémon without fear.",
|
"fight_or_flight_option_1_selected_message": "You approach the\nPokémon without fear.",
|
||||||
"fight_or_flight_option_2_good_result": `.@d{32}.@d{32}.@d{32}
|
"fight_or_flight_option_2_good_result": `.@d{32}.@d{32}.@d{32}
|
||||||
$You manage to sneak your way\npast and grab the item!`,
|
$You manage to sneak your way\npast and grab the item!`,
|
||||||
"fight_or_flight_option_2_steal_result": `.@d{32}.@d{32}.@d{32}
|
"fight_or_flight_option_2_steal_result": `.@d{32}.@d{32}.@d{32}
|
||||||
$Your @ec{thiefPokemon} helps you out and uses @ec{move}!
|
$Your @ec{option2PrimaryName} helps you out and uses @ec{option2PrimaryMove}!
|
||||||
$ You nabbed the item!`,
|
$ You nabbed the item!`,
|
||||||
"fight_or_flight_option_2_bad_result": `.@d{32}.@d{32}.@d{32}
|
"fight_or_flight_option_2_bad_result": `.@d{32}.@d{32}.@d{32}
|
||||||
$The Pokémon catches you\nas you try to sneak around!`,
|
$The Pokémon catches you\nas you try to sneak around!`,
|
||||||
|
@ -82,6 +83,7 @@ export const mysteryEncounter = {
|
||||||
"shady_vitamin_dealer_title": "The Vitamin Dealer",
|
"shady_vitamin_dealer_title": "The Vitamin Dealer",
|
||||||
"shady_vitamin_dealer_description": "The man opens his jacket to reveal some Pokémon vitamins. The numbers he quotes seem like a really good deal. Almost too good...\nHe offers two package deals to choose from.",
|
"shady_vitamin_dealer_description": "The man opens his jacket to reveal some Pokémon vitamins. The numbers he quotes seem like a really good deal. Almost too good...\nHe offers two package deals to choose from.",
|
||||||
"shady_vitamin_dealer_query": "Which deal will choose?",
|
"shady_vitamin_dealer_query": "Which deal will choose?",
|
||||||
|
"shady_vitamin_dealer_invalid_selection": "Pokémon must be healthy enough.",
|
||||||
"shady_vitamin_dealer_option_1_label": "The Cheap Deal",
|
"shady_vitamin_dealer_option_1_label": "The Cheap Deal",
|
||||||
"shady_vitamin_dealer_option_1_tooltip": "(-) Pay @ec{option1Money}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins",
|
"shady_vitamin_dealer_option_1_tooltip": "(-) Pay @ec{option1Money}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins",
|
||||||
"shady_vitamin_dealer_option_2_label": "The Pricey Deal",
|
"shady_vitamin_dealer_option_2_label": "The Pricey Deal",
|
||||||
|
@ -94,9 +96,33 @@ export const mysteryEncounter = {
|
||||||
$Your @ec{selectedPokemon} takes some damage\nand becomes badly poisoned...`,
|
$Your @ec{selectedPokemon} takes some damage\nand becomes badly poisoned...`,
|
||||||
"shady_vitamin_dealer_poison": `But the medicine had some side effects!
|
"shady_vitamin_dealer_poison": `But the medicine had some side effects!
|
||||||
$Your @ec{selectedPokemon} becomes poisoned...`,
|
$Your @ec{selectedPokemon} becomes poisoned...`,
|
||||||
|
"shady_vitamin_dealer_no_bad_effects": "Looks like there were no side-effects this time.",
|
||||||
"shady_vitamin_dealer_option_3_label": "Leave",
|
"shady_vitamin_dealer_option_3_label": "Leave",
|
||||||
"shady_vitamin_dealer_option_3_tooltip": "(-) No Rewards",
|
"shady_vitamin_dealer_option_3_tooltip": "(-) No Rewards",
|
||||||
"shady_vitamin_dealer_outro_good": "Looks like there were no side-effects this time.",
|
|
||||||
|
"field_trip_intro_message": "It's a teacher and some school children!",
|
||||||
|
"field_trip_speaker": "Teacher",
|
||||||
|
"field_trip_intro_dialogue": `Hello, there! Would you be able to\nspare a minute for my students?
|
||||||
|
$I'm teaching them about Pokémon moves\nand would love to show them a demonstration.
|
||||||
|
$Would you mind showing us one of\nthe moves your Pokémon can use?`,
|
||||||
|
"field_trip_title": "Field Trip",
|
||||||
|
"field_trip_description": "A teacher is requesting a move demonstration from a Pokémon. Depending on the move you choose, she might have something useful for you in exchange.",
|
||||||
|
"field_trip_query": "Which move category will you show off?",
|
||||||
|
"field_trip_option_1_label": "A Physical Move",
|
||||||
|
"field_trip_option_1_tooltip": "(+) Physical Item Rewards",
|
||||||
|
"field_trip_option_2_label": "A Special Move",
|
||||||
|
"field_trip_option_2_tooltip": "(+) Special Item Rewards",
|
||||||
|
"field_trip_option_3_label": "A Status Move",
|
||||||
|
"field_trip_option_3_tooltip": "(+) Status Item Rewards",
|
||||||
|
"field_trip_second_option_prompt": "Choose a move for your Pokémon to use.",
|
||||||
|
"field_trip_option_selected": "@ec{pokeName} shows off an awesome display of @ec{move}!",
|
||||||
|
"field_trip_option_incorrect": `...
|
||||||
|
$That isn't a @ec{moveCategory} move!
|
||||||
|
$I'm sorry, but I can't give you anything.`,
|
||||||
|
"field_trip_lesson_learned": `Looks like you learned a valuable lesson?
|
||||||
|
$Your Pokémon also gained some knowledge.`,
|
||||||
|
"field_trip_outro_good": "Thank you so much for your kindness!\nI hope the items I had were helpful!",
|
||||||
|
"field_trip_outro_bad": "Come along children, we'll\nfind a better demonstration elsewhere.",
|
||||||
|
|
||||||
// Mystery Encounters -- Uncommon Tier
|
// Mystery Encounters -- Uncommon Tier
|
||||||
|
|
||||||
|
@ -114,6 +140,7 @@ export const mysteryEncounter = {
|
||||||
"mysterious_challengers_outro_win": "The mysterious challenger was defeated!",
|
"mysterious_challengers_outro_win": "The mysterious challenger was defeated!",
|
||||||
|
|
||||||
// Mystery Encounters -- Rare Tier
|
// Mystery Encounters -- Rare Tier
|
||||||
|
|
||||||
"training_session_intro_message": "You've come across some\ntraining tools and supplies.",
|
"training_session_intro_message": "You've come across some\ntraining tools and supplies.",
|
||||||
"training_session_title": "Training Session",
|
"training_session_title": "Training Session",
|
||||||
"training_session_description": "These supplies look like they could be used to train a member of your party! There are a few ways you could train your Pokémon, by battling against it with the rest of your team.",
|
"training_session_description": "These supplies look like they could be used to train a member of your party! There are a few ways you could train your Pokémon, by battling against it with the rest of your team.",
|
||||||
|
@ -146,8 +173,8 @@ export const mysteryEncounter = {
|
||||||
"dark_deal_title": "Dark Deal",
|
"dark_deal_title": "Dark Deal",
|
||||||
"dark_deal_description": "The disturbing fellow holds up some Pokéballs.\n\"I'll make it worth your while! You can have these strong Pokéballs as payment, All I need is a Pokémon from your team! Hehe...\"",
|
"dark_deal_description": "The disturbing fellow holds up some Pokéballs.\n\"I'll make it worth your while! You can have these strong Pokéballs as payment, All I need is a Pokémon from your team! Hehe...\"",
|
||||||
"dark_deal_query": "What will you do?",
|
"dark_deal_query": "What will you do?",
|
||||||
"dark_deal_option_1_label": "Accept", // Give player 10 rogue balls. Remove a random Pokémon from player's party. Fight a legendary Pokémon as a boss
|
"dark_deal_option_1_label": "Accept",
|
||||||
"dark_deal_option_1_tooltip": "(+) 5 Rogue Balls\n(?) Enhance a Random Pokémon", // Give player 10 rogue balls. Remove a random Pokémon from player's party. Fight a legendary Pokémon as a boss
|
"dark_deal_option_1_tooltip": "(+) 5 Rogue Balls\n(?) Enhance a Random Pokémon",
|
||||||
"dark_deal_option_2_label": "Refuse",
|
"dark_deal_option_2_label": "Refuse",
|
||||||
"dark_deal_option_2_tooltip": "(-) No Rewards",
|
"dark_deal_option_2_tooltip": "(-) No Rewards",
|
||||||
"dark_deal_option_1_selected": `Let's see, that @ec{pokeName} will do nicely!
|
"dark_deal_option_1_selected": `Let's see, that @ec{pokeName} will do nicely!
|
||||||
|
@ -162,14 +189,14 @@ export const mysteryEncounter = {
|
||||||
"sleeping_snorlax_intro_message": `As you walk down a narrow pathway, you see a towering silhouette blocking your path.
|
"sleeping_snorlax_intro_message": `As you walk down a narrow pathway, you see a towering silhouette blocking your path.
|
||||||
$You get closer to see a Snorlax sleeping peacefully.\nIt seems like there's no way around it.`,
|
$You get closer to see a Snorlax sleeping peacefully.\nIt seems like there's no way around it.`,
|
||||||
"sleeping_snorlax_title": "Sleeping Snorlax",
|
"sleeping_snorlax_title": "Sleeping Snorlax",
|
||||||
"sleeping_snorlax_description": "You could attack it to try and get it to move, or simply wait for it to wake up.",
|
"sleeping_snorlax_description": "You could attack it to try and get it to move, or simply wait for it to wake up. Who knows how long that could take, though...",
|
||||||
"sleeping_snorlax_query": "What will you do?",
|
"sleeping_snorlax_query": "What will you do?",
|
||||||
"sleeping_snorlax_option_1_label": "Fight it",
|
"sleeping_snorlax_option_1_label": "Fight it",
|
||||||
"sleeping_snorlax_option_1_tooltip": "(-) Fight Sleeping Snorlax",
|
"sleeping_snorlax_option_1_tooltip": "(-) Fight Sleeping Snorlax",
|
||||||
"sleeping_snorlax_option_2_label": "Wait for it to move",
|
"sleeping_snorlax_option_2_label": "Wait for it to move",
|
||||||
"sleeping_snorlax_option_2_tooltip": "@[SUMMARY_BLUE]{(75%) Wait a short time}\n@[SUMMARY_BLUE]{(25%) Wait a long time}",
|
"sleeping_snorlax_option_2_tooltip": "@[SUMMARY_BLUE]{(75%) Wait a short time}\n@[SUMMARY_BLUE]{(25%) Wait a long time}",
|
||||||
"sleeping_snorlax_option_3_label": "Steal",
|
"sleeping_snorlax_option_3_label": "Steal its item",
|
||||||
"sleeping_snorlax_option_3_tooltip": "(+) Leftovers",
|
"sleeping_snorlax_option_3_tooltip": "(+) @ec{option3PrimaryName} uses @ec{option3PrimaryMove}\n(+) Leftovers",
|
||||||
"sleeping_snorlax_option_3_disabled_tooltip": "Your Pokémon need to know certain moves to choose this",
|
"sleeping_snorlax_option_3_disabled_tooltip": "Your Pokémon need to know certain moves to choose this",
|
||||||
"sleeping_snorlax_option_1_selected_message": "You approach the\nPokémon without fear.",
|
"sleeping_snorlax_option_1_selected_message": "You approach the\nPokémon without fear.",
|
||||||
"sleeping_snorlax_option_2_selected_message": `.@d{32}.@d{32}.@d{32}
|
"sleeping_snorlax_option_2_selected_message": `.@d{32}.@d{32}.@d{32}
|
||||||
|
@ -179,7 +206,6 @@ export const mysteryEncounter = {
|
||||||
$But on the bright side, the Snorlax left something behind...
|
$But on the bright side, the Snorlax left something behind...
|
||||||
$@s{item_fanfare}You gained a Berry!`,
|
$@s{item_fanfare}You gained a Berry!`,
|
||||||
"sleeping_snorlax_option_3_good_result": "Your @ec{option3PrimaryName} uses @ec{option3PrimaryMove}! @s{item_fanfare}It steals Leftovers off the sleeping Snorlax and you make out like bandits!",
|
"sleeping_snorlax_option_3_good_result": "Your @ec{option3PrimaryName} uses @ec{option3PrimaryMove}! @s{item_fanfare}It steals Leftovers off the sleeping Snorlax and you make out like bandits!",
|
||||||
// "sleeping_snorlax_outro_win": "The mysterious challengers were defeated!",
|
|
||||||
|
|
||||||
lostAtSea,
|
lostAtSea,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -8,18 +8,17 @@ import { PokeballCounts } from "./battle-scene";
|
||||||
import { PokeballType } from "./data/pokeball";
|
import { PokeballType } from "./data/pokeball";
|
||||||
import { Gender } from "./data/gender";
|
import { Gender } from "./data/gender";
|
||||||
import { StatusEffect } from "./data/status-effect";
|
import { StatusEffect } from "./data/status-effect";
|
||||||
import { SpeciesStatBoosterItem, modifierTypes } from "./modifier/modifier-type";
|
import { modifierTypes, SpeciesStatBoosterItem } from "./modifier/modifier-type";
|
||||||
import { VariantTier } from "./enums/variant-tiers";
|
import { VariantTier } from "./enums/variant-tiers";
|
||||||
import { EggTier } from "#enums/egg-type";
|
import { EggTier } from "#enums/egg-type";
|
||||||
import { allSpecies } from "./data/pokemon-species"; // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
import { Biome } from "#enums/biome";
|
import { Biome } from "#enums/biome";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { TimeOfDay } from "#enums/time-of-day";
|
import { TimeOfDay } from "#enums/time-of-day";
|
||||||
import {MysteryEncounterType} from "#enums/mystery-encounter-type"; // eslint-disable-line @typescript-eslint/no-unused-vars
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type"; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
import {MysteryEncounterTier} from "#app/data/mystery-encounters/mystery-encounter"; // eslint-disable-line @typescript-eslint/no-unused-vars
|
import { MysteryEncounterTier } from "#app/data/mystery-encounters/mystery-encounter"; // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides for testing different in game situations
|
* Overrides for testing different in game situations
|
||||||
|
|
|
@ -5363,6 +5363,7 @@ export class SelectModifierPhase extends BattlePhase {
|
||||||
this.scene.ui.revertMode();
|
this.scene.ui.revertMode();
|
||||||
this.scene.ui.setMode(Mode.MESSAGE);
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
super.end();
|
super.end();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
modifierType = typeOptions[cursor].type;
|
modifierType = typeOptions[cursor].type;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { Mode } from "../ui/ui";
|
||||||
import {
|
import {
|
||||||
getEncounterText
|
getEncounterText
|
||||||
} from "../data/mystery-encounters/mystery-encounter-utils";
|
} from "../data/mystery-encounters/mystery-encounter-utils";
|
||||||
import { CheckSwitchPhase, NewBattlePhase, PostSummonPhase, ReturnPhase, ScanIvsPhase, SelectModifierPhase, SummonPhase, ToggleDoublePositionPhase } from "../phases";
|
import { CheckSwitchPhase, NewBattlePhase, ReturnPhase, ScanIvsPhase, SelectModifierPhase, SummonPhase, ToggleDoublePositionPhase } from "../phases";
|
||||||
import MysteryEncounterOption from "../data/mystery-encounters/mystery-encounter-option";
|
import MysteryEncounterOption from "../data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MysteryEncounterVariant } from "../data/mystery-encounters/mystery-encounter";
|
import { MysteryEncounterVariant } from "../data/mystery-encounters/mystery-encounter";
|
||||||
import { getCharVariantFromDialogue } from "../data/dialogue";
|
import { getCharVariantFromDialogue } from "../data/dialogue";
|
||||||
|
@ -285,22 +285,9 @@ export class MysteryEncounterBattlePhase extends Phase {
|
||||||
const enemyField = scene.getEnemyField();
|
const enemyField = scene.getEnemyField();
|
||||||
const encounterVariant = scene.currentBattle.mysteryEncounter.encounterVariant;
|
const encounterVariant = scene.currentBattle.mysteryEncounter.encounterVariant;
|
||||||
|
|
||||||
|
// PostSummon and ShinySparkle phases are handled by SummonPhase
|
||||||
|
|
||||||
if (encounterVariant !== MysteryEncounterVariant.TRAINER_BATTLE) {
|
if (encounterVariant !== MysteryEncounterVariant.TRAINER_BATTLE) {
|
||||||
enemyField.map(p => this.scene.pushConditionalPhase(new PostSummonPhase(this.scene, p.getBattlerIndex()), () => {
|
|
||||||
// if there is not a player party, we can't continue
|
|
||||||
if (!this.scene.getParty()?.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// how many player pokemon are on the field ?
|
|
||||||
const pokemonsOnFieldCount = this.scene.getParty().filter(p => p.isOnField()).length;
|
|
||||||
// if it's a 2vs1, there will never be a 2nd pokemon on our field even
|
|
||||||
const requiredPokemonsOnField = Math.min(this.scene.getParty().filter((p) => !p.isFainted()).length, 2);
|
|
||||||
// if it's a double, there should be 2, otherwise 1
|
|
||||||
if (this.scene.currentBattle.double) {
|
|
||||||
return pokemonsOnFieldCount === requiredPokemonsOnField;
|
|
||||||
}
|
|
||||||
return pokemonsOnFieldCount === 1;
|
|
||||||
}));
|
|
||||||
const ivScannerModifier = this.scene.findModifier(m => m instanceof IvScannerModifier);
|
const ivScannerModifier = this.scene.findModifier(m => m instanceof IvScannerModifier);
|
||||||
if (ivScannerModifier) {
|
if (ivScannerModifier) {
|
||||||
enemyField.map(p => this.scene.pushPhase(new ScanIvsPhase(this.scene, p.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6))));
|
enemyField.map(p => this.scene.pushPhase(new ScanIvsPhase(this.scene, p.getBattlerIndex(), Math.min(ivScannerModifier.getStackCount() * 2, 6))));
|
||||||
|
|
|
@ -41,7 +41,7 @@ import { Moves } from "#enums/moves";
|
||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { MysteryEncounterData } from "../data/mystery-encounters/mystery-encounter-data";
|
import { MysteryEncounterData } from "../data/mystery-encounters/mystery-encounter-data";
|
||||||
import MysteryEncounter from "../data/mystery-encounters/mystery-encounter";
|
import IMysteryEncounter from "../data/mystery-encounters/mystery-encounter";
|
||||||
|
|
||||||
export const defaultStarterSpecies: Species[] = [
|
export const defaultStarterSpecies: Species[] = [
|
||||||
Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE,
|
Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE,
|
||||||
|
@ -124,7 +124,7 @@ export interface SessionSaveData {
|
||||||
gameVersion: string;
|
gameVersion: string;
|
||||||
timestamp: integer;
|
timestamp: integer;
|
||||||
challenges: ChallengeData[];
|
challenges: ChallengeData[];
|
||||||
mysteryEncounter: MysteryEncounter;
|
mysteryEncounter: IMysteryEncounter;
|
||||||
mysteryEncounterData: MysteryEncounterData;
|
mysteryEncounterData: MysteryEncounterData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1155,7 +1155,7 @@ export class GameData {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (k === "mysteryEncounter") {
|
if (k === "mysteryEncounter") {
|
||||||
return new MysteryEncounter(v);
|
return new IMysteryEncounter(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (k === "mysteryEncounterData") {
|
if (k === "mysteryEncounterData") {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { StatusEffect } from "#app/data/status-effect";
|
import { StatusEffect } from "#app/data/status-effect";
|
||||||
import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
import IMysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MessagePhase } from "#app/phases";
|
import { MessagePhase } from "#app/phases";
|
||||||
import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
|
import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
|
||||||
import { Type } from "#app/data/type";
|
import { Type } from "#app/data/type";
|
||||||
|
@ -273,7 +273,7 @@ describe("Mystery Encounter Utils", () => {
|
||||||
|
|
||||||
describe("getTextWithEncounterDialogueTokens", () => {
|
describe("getTextWithEncounterDialogueTokens", () => {
|
||||||
it("injects dialogue tokens and color styling", () => {
|
it("injects dialogue tokens and color styling", () => {
|
||||||
scene.currentBattle.mysteryEncounter = new MysteryEncounter(null);
|
scene.currentBattle.mysteryEncounter = new IMysteryEncounter(null);
|
||||||
scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value");
|
scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value");
|
||||||
|
|
||||||
const result = getEncounterText(scene, "mysteryEncounter:unit_test_dialogue");
|
const result = getEncounterText(scene, "mysteryEncounter:unit_test_dialogue");
|
||||||
|
@ -281,7 +281,7 @@ describe("Mystery Encounter Utils", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can perform nested dialogue token injection", () => {
|
it("can perform nested dialogue token injection", () => {
|
||||||
scene.currentBattle.mysteryEncounter = new MysteryEncounter(null);
|
scene.currentBattle.mysteryEncounter = new IMysteryEncounter(null);
|
||||||
scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value");
|
scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value");
|
||||||
scene.currentBattle.mysteryEncounter.setDialogueToken("testvalue", "new");
|
scene.currentBattle.mysteryEncounter.setDialogueToken("testvalue", "new");
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ describe("Mystery Encounter Utils", () => {
|
||||||
|
|
||||||
describe("queueEncounterMessage", () => {
|
describe("queueEncounterMessage", () => {
|
||||||
it("queues a message with encounter dialogue tokens", async () => {
|
it("queues a message with encounter dialogue tokens", async () => {
|
||||||
scene.currentBattle.mysteryEncounter = new MysteryEncounter(null);
|
scene.currentBattle.mysteryEncounter = new IMysteryEncounter(null);
|
||||||
scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value");
|
scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value");
|
||||||
const spy = vi.spyOn(game.scene, "queueMessage");
|
const spy = vi.spyOn(game.scene, "queueMessage");
|
||||||
const phaseSpy = vi.spyOn(game.scene, "unshiftPhase");
|
const phaseSpy = vi.spyOn(game.scene, "unshiftPhase");
|
||||||
|
@ -305,7 +305,7 @@ describe("Mystery Encounter Utils", () => {
|
||||||
|
|
||||||
describe("showEncounterText", () => {
|
describe("showEncounterText", () => {
|
||||||
it("showText with dialogue tokens", async () => {
|
it("showText with dialogue tokens", async () => {
|
||||||
scene.currentBattle.mysteryEncounter = new MysteryEncounter(null);
|
scene.currentBattle.mysteryEncounter = new IMysteryEncounter(null);
|
||||||
scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value");
|
scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value");
|
||||||
const spy = vi.spyOn(game.scene.ui, "showText");
|
const spy = vi.spyOn(game.scene.ui, "showText");
|
||||||
|
|
||||||
|
@ -316,7 +316,7 @@ describe("Mystery Encounter Utils", () => {
|
||||||
|
|
||||||
describe("showEncounterDialogue", () => {
|
describe("showEncounterDialogue", () => {
|
||||||
it("showText with dialogue tokens", async () => {
|
it("showText with dialogue tokens", async () => {
|
||||||
scene.currentBattle.mysteryEncounter = new MysteryEncounter(null);
|
scene.currentBattle.mysteryEncounter = new IMysteryEncounter(null);
|
||||||
scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value");
|
scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value");
|
||||||
const spy = vi.spyOn(game.scene.ui, "showDialogue");
|
const spy = vi.spyOn(game.scene.ui, "showDialogue");
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ import { initVouchers } from "#app/system/voucher";
|
||||||
import { initAchievements } from "#app/system/achv";
|
import { initAchievements } from "#app/system/achv";
|
||||||
import { initStatsKeys } from "#app/ui/game-stats-ui-handler";
|
import { initStatsKeys } from "#app/ui/game-stats-ui-handler";
|
||||||
import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters";
|
import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
import { initMysteryEncounterDialogue } from "#app/data/mystery-encounters/mystery-encounter-dialogue";
|
|
||||||
import { beforeAll, beforeEach, vi } from "vitest";
|
import { beforeAll, beforeEach, vi } from "vitest";
|
||||||
import * as overrides from "#app/overrides";
|
import * as overrides from "#app/overrides";
|
||||||
|
|
||||||
|
@ -28,7 +27,6 @@ initSpecies();
|
||||||
initMoves();
|
initMoves();
|
||||||
initAbilities();
|
initAbilities();
|
||||||
initLoggedInUser();
|
initLoggedInUser();
|
||||||
initMysteryEncounterDialogue();
|
|
||||||
initMysteryEncounters();
|
initMysteryEncounters();
|
||||||
|
|
||||||
global.testFailed = false;
|
global.testFailed = false;
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import BattleScene from "../battle-scene";
|
import BattleScene from "../battle-scene";
|
||||||
import {addBBCodeTextObject, getBBCodeFrag, TextStyle} from "./text";
|
import { addBBCodeTextObject, getBBCodeFrag, TextStyle } from "./text";
|
||||||
import {Mode} from "./ui";
|
import { Mode } from "./ui";
|
||||||
import UiHandler from "./ui-handler";
|
import UiHandler from "./ui-handler";
|
||||||
import {Button} from "#enums/buttons";
|
import { Button } from "#enums/buttons";
|
||||||
import {addWindow, WindowVariant} from "./ui-theme";
|
import { addWindow, WindowVariant } from "./ui-theme";
|
||||||
import {MysteryEncounterPhase} from "../phases/mystery-encounter-phase";
|
import { MysteryEncounterPhase } from "../phases/mystery-encounter-phase";
|
||||||
import {PartyUiMode} from "./party-ui-handler";
|
import { PartyUiMode } from "./party-ui-handler";
|
||||||
import MysteryEncounterOption from "../data/mystery-encounters/mystery-encounter-option";
|
import MysteryEncounterOption, { EncounterOptionMode } from "../data/mystery-encounters/mystery-encounter-option";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import {isNullOrUndefined} from "../utils";
|
import { isNullOrUndefined } from "../utils";
|
||||||
import {getPokeballAtlasKey} from "../data/pokeball";
|
import { getPokeballAtlasKey } from "../data/pokeball";
|
||||||
import {getEncounterText} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
import { getEncounterText } from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||||
|
|
||||||
export default class MysteryEncounterUiHandler extends UiHandler {
|
export default class MysteryEncounterUiHandler extends UiHandler {
|
||||||
private cursorContainer: Phaser.GameObjects.Container;
|
private cursorContainer: Phaser.GameObjects.Container;
|
||||||
|
@ -100,6 +100,7 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
||||||
|
|
||||||
if (button === Button.CANCEL || button === Button.ACTION) {
|
if (button === Button.CANCEL || button === Button.ACTION) {
|
||||||
if (button === Button.ACTION) {
|
if (button === Button.ACTION) {
|
||||||
|
const selected = this.filteredEncounterOptions[cursor];
|
||||||
if (cursor === this.viewPartyIndex) {
|
if (cursor === this.viewPartyIndex) {
|
||||||
// Handle view party
|
// Handle view party
|
||||||
success = true;
|
success = true;
|
||||||
|
@ -110,10 +111,9 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
||||||
this.unblockInput();
|
this.unblockInput();
|
||||||
}, 300);
|
}, 300);
|
||||||
});
|
});
|
||||||
} else if (this.blockInput || !this.optionsMeetsReqs[cursor]) {
|
} else if (this.blockInput || (!this.optionsMeetsReqs[cursor] && (selected.optionMode === EncounterOptionMode.DISABLED_OR_DEFAULT || selected.optionMode === EncounterOptionMode.DISABLED_OR_SPECIAL))) {
|
||||||
success = false;
|
success = false;
|
||||||
} else {
|
} else {
|
||||||
const selected = this.filteredEncounterOptions[cursor];
|
|
||||||
if ((this.scene.getCurrentPhase() as MysteryEncounterPhase).handleOptionSelect(selected, cursor)) {
|
if ((this.scene.getCurrentPhase() as MysteryEncounterPhase).handleOptionSelect(selected, cursor)) {
|
||||||
success = true;
|
success = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -253,7 +253,8 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
||||||
if (this.blockInput) {
|
if (this.blockInput) {
|
||||||
this.blockInput = false;
|
this.blockInput = false;
|
||||||
for (let i = 0; i < this.optionsContainer.length - 1; i++) {
|
for (let i = 0; i < this.optionsContainer.length - 1; i++) {
|
||||||
if (!this.optionsMeetsReqs[i]) {
|
const optionMode = this.filteredEncounterOptions[i].optionMode;
|
||||||
|
if (!this.optionsMeetsReqs[i] && (optionMode === EncounterOptionMode.DISABLED_OR_DEFAULT || optionMode === EncounterOptionMode.DISABLED_OR_SPECIAL)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
(this.optionsContainer.getAt(i) as Phaser.GameObjects.Text).setAlpha(1);
|
(this.optionsContainer.getAt(i) as Phaser.GameObjects.Text).setAlpha(1);
|
||||||
|
@ -307,6 +308,8 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
||||||
|
|
||||||
// Options Window
|
// Options Window
|
||||||
for (let i = 0; i < this.filteredEncounterOptions.length; i++) {
|
for (let i = 0; i < this.filteredEncounterOptions.length; i++) {
|
||||||
|
const option = this.filteredEncounterOptions[i];
|
||||||
|
|
||||||
let optionText;
|
let optionText;
|
||||||
switch (this.filteredEncounterOptions.length) {
|
switch (this.filteredEncounterOptions.length) {
|
||||||
case 2:
|
case 2:
|
||||||
|
@ -319,15 +322,22 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
||||||
optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, i < 2 ? 0 : 16, "-", TextStyle.WINDOW, { wordWrap: { width: 558 }, fontSize: "80px", lineSpacing: -8 });
|
optionText = addBBCodeTextObject(this.scene, i % 2 === 0 ? 0 : 100, i < 2 ? 0 : 16, "-", TextStyle.WINDOW, { wordWrap: { width: 558 }, fontSize: "80px", lineSpacing: -8 });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const option = mysteryEncounter.dialogue.encounterOptionsDialogue.options[i];
|
|
||||||
const text = getEncounterText(this.scene, option.buttonLabel, option.style ? option.style : TextStyle.WINDOW);
|
this.optionsMeetsReqs.push(option.meetsRequirements(this.scene));
|
||||||
|
const optionDialogue = option.dialogue;
|
||||||
|
let text: string;
|
||||||
|
if (option.hasRequirements() && this.optionsMeetsReqs[i] && (option.optionMode === EncounterOptionMode.DEFAULT_OR_SPECIAL || option.optionMode === EncounterOptionMode.DISABLED_OR_SPECIAL)) {
|
||||||
|
// Options with special requirements that are met are automatically colored green
|
||||||
|
text = getEncounterText(this.scene, optionDialogue.buttonLabel, TextStyle.SUMMARY_GREEN);
|
||||||
|
} else {
|
||||||
|
text = getEncounterText(this.scene, optionDialogue.buttonLabel, optionDialogue.style ? optionDialogue.style : TextStyle.WINDOW);
|
||||||
|
}
|
||||||
|
|
||||||
if (text) {
|
if (text) {
|
||||||
optionText.setText(text);
|
optionText.setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.optionsMeetsReqs.push(this.filteredEncounterOptions[i].meetsRequirements(this.scene));
|
if (!this.optionsMeetsReqs[i] && (option.optionMode === EncounterOptionMode.DISABLED_OR_DEFAULT || option.optionMode === EncounterOptionMode.DISABLED_OR_SPECIAL)) {
|
||||||
|
|
||||||
if (!this.optionsMeetsReqs[i]) {
|
|
||||||
optionText.setAlpha(0.5);
|
optionText.setAlpha(0.5);
|
||||||
}
|
}
|
||||||
if (this.blockInput) {
|
if (this.blockInput) {
|
||||||
|
@ -412,13 +422,13 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mysteryEncounter = this.scene.currentBattle.mysteryEncounter;
|
let text: string;
|
||||||
let text;
|
const cursorOption = this.filteredEncounterOptions[cursor];
|
||||||
const option = mysteryEncounter.dialogue.encounterOptionsDialogue.options[cursor];
|
const optionDialogue = cursorOption.dialogue;
|
||||||
if (!this.optionsMeetsReqs[cursor] && option.disabledTooltip) {
|
if (!this.optionsMeetsReqs[cursor] && (cursorOption.optionMode === EncounterOptionMode.DISABLED_OR_DEFAULT || cursorOption.optionMode === EncounterOptionMode.DISABLED_OR_SPECIAL) && optionDialogue.disabledTooltip) {
|
||||||
text = getEncounterText(this.scene, option.disabledTooltip, TextStyle.TOOLTIP_CONTENT);
|
text = getEncounterText(this.scene, optionDialogue.disabledTooltip, TextStyle.TOOLTIP_CONTENT);
|
||||||
} else {
|
} else {
|
||||||
text = getEncounterText(this.scene, option.buttonTooltip, TextStyle.TOOLTIP_CONTENT);
|
text = getEncounterText(this.scene, optionDialogue.buttonTooltip, TextStyle.TOOLTIP_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-color options green/blue for good/bad by looking for (+)/(-)
|
// Auto-color options green/blue for good/bad by looking for (+)/(-)
|
||||||
|
|
Loading…
Reference in New Issue