Merge branch 'mystery-battle-events' into event/getting-lost-at-the-sea

This commit is contained in:
Felix Staud 2024-07-12 14:08:52 -07:00
commit b95e9cbfa4
10 changed files with 96 additions and 104 deletions

View File

@ -162,11 +162,7 @@ export const TrainingSessionEncounter: IMysteryEncounter =
scene.addModifier(mod, true, false, false, true); scene.addModifier(mod, true, false, false, true);
} }
scene.updateModifiers(true); scene.updateModifiers(true);
scene.queueMessage( scene.queueMessage(getEncounterText(scene, `${namespace}_battle_finished_1`), null, true);
getEncounterText(scene, `${namespace}_battle_finished_1`),
null,
true
);
}; };
setEncounterRewards( setEncounterRewards(
@ -337,11 +333,7 @@ export const TrainingSessionEncounter: IMysteryEncounter =
scene.removePokemonFromPlayerParty(playerPokemon, false); scene.removePokemonFromPlayerParty(playerPokemon, false);
const onBeforeRewardsPhase = () => { const onBeforeRewardsPhase = () => {
scene.queueMessage( scene.queueMessage(getEncounterText(scene, `${namespace}_battle_finished_3`), null, true);
getEncounterText(scene, `${namespace}_battle_finished_3`),
null,
true
);
// Add the pokemon back to party with ability change // Add the pokemon back to party with ability change
const abilityIndex = encounter.misc.abilityIndex; const abilityIndex = encounter.misc.abilityIndex;
if (!!playerPokemon.getFusionSpeciesForm()) { if (!!playerPokemon.getFusionSpeciesForm()) {

View File

@ -1,24 +1,24 @@
import { TextStyle } from "#app/ui/text"; import { TextStyle } from "#app/ui/text";
export class TextDisplay { export class TextDisplay {
speaker?: TemplateStringsArray | `mysteryEncounter:${string}`; speaker?: string;
text: TemplateStringsArray | `mysteryEncounter:${string}`; text: string;
style?: TextStyle; style?: TextStyle;
} }
export class OptionTextDisplay { export class OptionTextDisplay {
buttonLabel: TemplateStringsArray | `mysteryEncounter:${string}`; buttonLabel: string;
buttonTooltip?: TemplateStringsArray | `mysteryEncounter:${string}`; buttonTooltip?: string;
disabledTooltip?: TemplateStringsArray | `mysteryEncounter:${string}`; disabledTooltip?: string;
secondOptionPrompt?: TemplateStringsArray | `mysteryEncounter:${string}`; secondOptionPrompt?: string;
selected?: TextDisplay[]; selected?: TextDisplay[];
style?: TextStyle; style?: TextStyle;
} }
export class EncounterOptionsDialogue { export class EncounterOptionsDialogue {
title?: TemplateStringsArray | `mysteryEncounter:${string}`; title?: string;
description?: TemplateStringsArray | `mysteryEncounter:${string}`; description?: string;
query?: TemplateStringsArray | `mysteryEncounter:${string}`; query?: string;
options?: [...OptionTextDisplay[]]; // Options array with minimum 2 options options?: [...OptionTextDisplay[]]; // Options array with minimum 2 options
} }

View File

@ -36,13 +36,14 @@ export abstract class EncounterPokemonRequirement implements EncounterRequiremen
abstract meetsRequirement(scene: BattleScene): boolean; abstract meetsRequirement(scene: BattleScene): boolean;
// Returns all party members that are compatible with this requirement. For non pokemon related requirements, the entire party is returned.. /**
* Returns all party members that are compatible with this requirement. For non pokemon related requirements, the entire party is returned.
* @param partyPokemon
*/
queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] { queryParty(partyPokemon: PlayerPokemon[]): PlayerPokemon[] {
return []; return [];
} }
// Doesn't require the "@ec" as prefix, just the string; populates the token with the attribute
// ex. @ec{primarySpecies} if strPrefix is simply "primary"
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
return ["", ""]; return ["", ""];
} }
@ -237,8 +238,7 @@ export class MoneyRequirement extends EncounterSceneRequirement {
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] { getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
const value = this?.scalingMultiplier > 0 ? scene.getWaveMoneyAmount(this.scalingMultiplier).toString() : this.requiredMoney.toString(); const value = this?.scalingMultiplier > 0 ? scene.getWaveMoneyAmount(this.scalingMultiplier).toString() : this.requiredMoney.toString();
// Colors money text return ["money", value];
return ["money", "@[MONEY]{₽" + value + "}"];
} }
} }

View File

@ -1,32 +1,32 @@
import i18next, { ParseKeys } from "i18next";
import { BattleType } from "#app/battle"; import { BattleType } from "#app/battle";
import BattleScene from "../../battle-scene"; import { biomeLinks } from "#app/data/biomes";
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "../pokemon-species"; import { WIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
import { MysteryEncounterVariant } from "./mystery-encounter"; import { Type } from "#app/data/type";
import { Status, StatusEffect } from "../status-effect";
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 { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier"; import { ExpBalanceModifier, ExpShareModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "#app/modifier/modifier";
import { CustomModifierSettings, getModifierPoolForType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, PokemonHeldItemModifierType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; import { CustomModifierSettings, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeGenerator, ModifierTypeOption, PokemonHeldItemModifierType, getModifierPoolForType, modifierTypes, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
import * as Overrides from "#app/overrides";
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 { isNullOrUndefined } from "#app/utils";
import { TrainerType } from "#enums/trainer-type";
import { Species } from "#enums/species";
import { Type } from "#app/data/type";
import { BattlerTagType } from "#enums/battler-tag-type";
import PokemonData from "#app/system/pokemon-data"; import PokemonData from "#app/system/pokemon-data";
import { Biome } from "#enums/biome";
import { biomeLinks } from "#app/data/biomes";
import { Mode } from "#app/ui/ui";
import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler";
import { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; import { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { WIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters"; import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler";
import { getTextWithColors, TextStyle } from "#app/ui/text"; import { TextStyle, getTextWithColors } from "#app/ui/text";
import * as Overrides from "#app/overrides"; import { Mode } from "#app/ui/ui";
import { isNullOrUndefined } from "#app/utils";
import { BattlerTagType } from "#enums/battler-tag-type";
import { Biome } from "#enums/biome";
import { Species } from "#enums/species";
import { TrainerType } from "#enums/trainer-type";
import { UiTheme } from "#enums/ui-theme"; import { UiTheme } from "#enums/ui-theme";
import i18next from "i18next";
import BattleScene from "../../battle-scene";
import Trainer, { TrainerVariant } from "../../field/trainer";
import * as Utils from "../../utils";
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "../pokemon-species";
import { Status, StatusEffect } from "../status-effect";
import { TrainerConfig, TrainerSlot, trainerConfigs } from "../trainer-config";
import { MysteryEncounterVariant } from "./mystery-encounter";
/** /**
* *
@ -158,12 +158,14 @@ export function koPlayerPokemon(pokemon: PlayerPokemon) {
pokemon.updateInfo(); pokemon.updateInfo();
} }
export function getEncounterText(scene: BattleScene, textKey: TemplateStringsArray | `mysteryEncounter:${string}`, primaryStyle?: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string { export function getEncounterText(scene: BattleScene, textKey: string, primaryStyle?: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string {
if (isNullOrUndefined(textKey)) { if (isNullOrUndefined(textKey)) {
return null; return null;
} }
let textString: string = getTextWithDialogueTokens(scene, textKey); const stringArray = [`${textKey}`] as any;
stringArray.raw = [`${textKey}`];
let textString: string = getTextWithDialogueTokens(scene, stringArray);
// Can only color the text if a Primary Style is defined // Can only color the text if a Primary Style is defined
// primaryStyle is applied to all text that does not have its own specified style // primaryStyle is applied to all text that does not have its own specified style
@ -174,22 +176,12 @@ export function getEncounterText(scene: BattleScene, textKey: TemplateStringsArr
return textString; return textString;
} }
function getTextWithDialogueTokens(scene: BattleScene, textKey: TemplateStringsArray | `mysteryEncounter:${string}`): string { function getTextWithDialogueTokens(scene: BattleScene, textKey: TemplateStringsArray): string {
if (isNullOrUndefined(textKey)) { if (isNullOrUndefined(textKey)) {
return null; return null;
} }
let textString: string = i18next.t(textKey as ParseKeys); return i18next.t(textKey, scene.currentBattle?.mysteryEncounter?.dialogueTokens);
// Apply dialogue tokens
const dialogueTokens = scene.currentBattle?.mysteryEncounter?.dialogueTokens;
if (dialogueTokens) {
dialogueTokens.forEach((value) => {
textString = textString.replace(value[0], value[1]);
});
}
return textString;
} }
/** /**
@ -197,7 +189,7 @@ function getTextWithDialogueTokens(scene: BattleScene, textKey: TemplateStringsA
* @param scene * @param scene
* @param contentKey * @param contentKey
*/ */
export function queueEncounterMessage(scene: BattleScene, contentKey: TemplateStringsArray | `mysteryEncounter:${string}`): void { export function queueEncounterMessage(scene: BattleScene, contentKey: string): void {
const text: string = getEncounterText(scene, contentKey); const text: string = getEncounterText(scene, contentKey);
scene.queueMessage(text, null, true); scene.queueMessage(text, null, true);
} }
@ -207,7 +199,7 @@ export function queueEncounterMessage(scene: BattleScene, contentKey: TemplateSt
* @param scene * @param scene
* @param contentKey * @param contentKey
*/ */
export function showEncounterText(scene: BattleScene, contentKey: TemplateStringsArray | `mysteryEncounter:${string}`): Promise<void> { export function showEncounterText(scene: BattleScene, contentKey: string): Promise<void> {
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
const text: string = getEncounterText(scene, contentKey); const text: string = getEncounterText(scene, contentKey);
scene.ui.showText(text, null, () => resolve(), 0, true); scene.ui.showText(text, null, () => resolve(), 0, true);
@ -221,7 +213,7 @@ export function showEncounterText(scene: BattleScene, contentKey: TemplateString
* @param speakerContentKey * @param speakerContentKey
* @param callback * @param callback
*/ */
export function showEncounterDialogue(scene: BattleScene, textContentKey: TemplateStringsArray | `mysteryEncounter:${string}`, speakerContentKey: TemplateStringsArray | `mysteryEncounter:${string}`, callback?: Function) { export function showEncounterDialogue(scene: BattleScene, textContentKey: string, speakerContentKey: string, callback?: Function) {
const text: string = getEncounterText(scene, textContentKey); const text: string = getEncounterText(scene, textContentKey);
const speaker: string = getEncounterText(scene, speakerContentKey); const speaker: string = getEncounterText(scene, speakerContentKey);
scene.ui.showDialogue(text, speaker, null, callback, 0, 0); scene.ui.showDialogue(text, speaker, null, callback, 0, 0);

View File

@ -95,7 +95,7 @@ export default interface IMysteryEncounter {
* Can be set for uses programatic dialogue during an encounter (storing the name of one of the party's pokemon, etc.) * Can be set for uses programatic dialogue during an encounter (storing the name of one of the party's pokemon, etc.)
* Example use: see MYSTERIOUS_CHEST * Example use: see MYSTERIOUS_CHEST
*/ */
dialogueTokens?: Map<string, [RegExp, string]>; dialogueTokens?: Record<string, string>;
/** /**
* Should be set depending upon option selected as part of an encounter * Should be set depending upon option selected as part of an encounter
* For example, if there is no battle as part of the encounter/selected option, should be set to NO_BATTLE * For example, if there is no battle as part of the encounter/selected option, should be set to NO_BATTLE
@ -144,7 +144,7 @@ export default class IMysteryEncounter implements IMysteryEncounter {
// 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 = {};
this.enemyPartyConfigs = []; this.enemyPartyConfigs = [];
this.introVisuals = null; this.introVisuals = null;
this.misc = null; this.misc = null;
@ -331,7 +331,7 @@ export default class IMysteryEncounter implements IMysteryEncounter {
} }
setDialogueToken?(key: string, value: string) { setDialogueToken?(key: string, value: string) {
this.dialogueTokens.set(key, [new RegExp("@ec\{" + key + "\\}", "gi"), value]); this.dialogueTokens[key] = value;
} }
private capitalizeFirstLetter?(str: string) { private capitalizeFirstLetter?(str: string) {
@ -350,7 +350,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
primaryPokemonRequirements?: EncounterPokemonRequirement[] = []; primaryPokemonRequirements?: EncounterPokemonRequirement[] = [];
secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = []; secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = [];
excludePrimaryFromSupportRequirements?: boolean; excludePrimaryFromSupportRequirements?: boolean;
dialogueTokens?: Map<string, [RegExp, string]>; dialogueTokens?: Record<string, string>;
doEncounterExp?: (scene: BattleScene) => boolean; doEncounterExp?: (scene: BattleScene) => boolean;
doEncounterRewards?: (scene: BattleScene) => boolean; doEncounterRewards?: (scene: BattleScene) => boolean;
onInit?: (scene: BattleScene) => boolean; onInit?: (scene: BattleScene) => boolean;
@ -607,7 +607,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @param title - title of the encounter * @param title - title of the encounter
* @returns * @returns
*/ */
withTitle(title: TemplateStringsArray | `mysteryEncounter:${string}`) { withTitle(title: string) {
const encounterOptionsDialogue = this.dialogue.encounterOptionsDialogue ?? {}; const encounterOptionsDialogue = this.dialogue.encounterOptionsDialogue ?? {};
this.dialogue = { this.dialogue = {
@ -627,7 +627,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @param description - description of the encounter * @param description - description of the encounter
* @returns * @returns
*/ */
withDescription(description: TemplateStringsArray | `mysteryEncounter:${string}`) { withDescription(description: string) {
const encounterOptionsDialogue = this.dialogue.encounterOptionsDialogue ?? {}; const encounterOptionsDialogue = this.dialogue.encounterOptionsDialogue ?? {};
this.dialogue = { this.dialogue = {
@ -647,7 +647,7 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
* @param query - query to use for the encounter * @param query - query to use for the encounter
* @returns * @returns
*/ */
withQuery(query: TemplateStringsArray | `mysteryEncounter:${string}`) { withQuery(query: string) {
const encounterOptionsDialogue = this.dialogue.encounterOptionsDialogue ?? {}; const encounterOptionsDialogue = this.dialogue.encounterOptionsDialogue ?? {};
this.dialogue = { this.dialogue = {

View File

@ -5,7 +5,8 @@ import { lostAtSea } from "./mystery-encounters/lost-at-sea";
* '$' will be treated as a new line for Message and Dialogue strings * '$' will be treated as a new line for Message and Dialogue strings
* '@d{<number>}' will add a time delay to text animation for Message and Dialogue strings * '@d{<number>}' will add a time delay to text animation for Message and Dialogue strings
* *
* '@ec{<token>}' will auto-inject the matching token value for the specified Encounter * '{{<token>}}' will auto-inject the matching token value for the specified Encounter that is stored in dialogueTokens
* (see [i18next interpolations](https://www.i18next.com/translation-function/interpolation))
* *
* '@[<TextStyle>]{<text>}' will auto-color the given text to a specified TextStyle (e.g. TextStyle.SUMMARY_GREEN) * '@[<TextStyle>]{<text>}' will auto-color the given text to a specified TextStyle (e.g. TextStyle.SUMMARY_GREEN)
* *
@ -14,7 +15,7 @@ import { lostAtSea } from "./mystery-encounters/lost-at-sea";
*/ */
export const mysteryEncounter = { export const mysteryEncounter = {
// DO NOT REMOVE // DO NOT REMOVE
"unit_test_dialogue": "@ec{test}@ec{test} @ec{test@ec{test}} @ec{test1} @ec{test\} @ec{test\\} @ec{test\\\} {test}", "unit_test_dialogue": "{{test}}{{test}} {{test{{test}}}} {{test1}} {{test\}} {{test\\}} {{test\\\}} {test}}",
// Mystery Encounters -- Common Tier // Mystery Encounters -- Common Tier
@ -33,7 +34,7 @@ export const mysteryEncounter = {
"mysterious_chest_option_1_great_result": "A couple great tools and items!", "mysterious_chest_option_1_great_result": "A couple great tools and items!",
"mysterious_chest_option_1_amazing_result": "Whoa! An amazing item!", "mysterious_chest_option_1_amazing_result": "Whoa! An amazing item!",
"mysterious_chest_option_1_bad_result": `Oh no!@d{32}\nThe chest was trapped! "mysterious_chest_option_1_bad_result": `Oh no!@d{32}\nThe chest was trapped!
$Your @ec{pokeName} jumps in front of you\nbut is KOed in the process.`, $Your {{pokeName}} jumps in front of you\nbut is KOed in the process.`,
"fight_or_flight_intro_message": "Something shiny is sparkling\non the ground near that Pokémon!", "fight_or_flight_intro_message": "Something shiny is sparkling\non the ground near that Pokémon!",
"fight_or_flight_title": "Fight or Flight", "fight_or_flight_title": "Fight or Flight",
@ -43,18 +44,18 @@ 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": "(+) @ec{option2PrimaryName} uses @ec{option2PrimaryMove}", "fight_or_flight_option_2_steal_tooltip": "(+) {{option2PrimaryName}} uses {{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{option2PrimaryName} helps you out and uses @ec{option2PrimaryMove}! $Your {{option2PrimaryName}} helps you out and uses {{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!`,
"fight_or_flight_boss_enraged": "The opposing @ec{enemyPokemon} has become enraged!", "fight_or_flight_boss_enraged": "The opposing {{enemyPokemon}} has become enraged!",
"fight_or_flight_option_3_selected": "You leave the strong Pokémon\nwith its prize and continue on.", "fight_or_flight_option_3_selected": "You leave the strong Pokémon\nwith its prize and continue on.",
"department_store_sale_intro_message": "It's a lady with a ton of shopping bags.", "department_store_sale_intro_message": "It's a lady with a ton of shopping bags.",
@ -85,17 +86,17 @@ export const mysteryEncounter = {
"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_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 {{option1Money, money}}\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",
"shady_vitamin_dealer_option_2_tooltip": "(-) Pay @ec{option2Money}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins", "shady_vitamin_dealer_option_2_tooltip": "(-) Pay {{option2Money, money}}\n(-) Side Effects?\n(+) Chosen Pokémon Gains 2 Random Vitamins",
"shady_vitamin_dealer_option_selected": `The man hands you two bottles and quickly disappears. "shady_vitamin_dealer_option_selected": `The man hands you two bottles and quickly disappears.
$@ec{selectedPokemon} gained @ec{boost1} and @ec{boost2} boosts!`, \${{selectedPokemon}} gained {{boost1}} and {{boost2}} boosts!`,
"shady_vitamin_dealer_damage_only": `But the medicine had some side effects! "shady_vitamin_dealer_damage_only": `But the medicine had some side effects!
$Your @ec{selectedPokemon} takes some damage...`, $Your {{selectedPokemon}} takes some damage...`,
"shady_vitamin_dealer_bad_poison": `But the medicine had some side effects! "shady_vitamin_dealer_bad_poison": `But the medicine had some side effects!
$Your @ec{selectedPokemon} takes some damage\nand becomes badly poisoned...`, $Your {{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 {{selectedPokemon}} becomes poisoned...`,
"shady_vitamin_dealer_no_bad_effects": "Looks like there were no side-effects this time.", "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",
@ -115,9 +116,9 @@ export const mysteryEncounter = {
"field_trip_option_3_label": "A Status Move", "field_trip_option_3_label": "A Status Move",
"field_trip_option_3_tooltip": "(+) Status Item Rewards", "field_trip_option_3_tooltip": "(+) Status Item Rewards",
"field_trip_second_option_prompt": "Choose a move for your Pokémon to use.", "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_selected": "{{pokeName}} shows off an awesome display of {{move}}!",
"field_trip_option_incorrect": `... "field_trip_option_incorrect": `...
$That isn't a @ec{moveCategory} move! $That isn't a {{moveCategory}} move!
$I'm sorry, but I can't give you anything.`, $I'm sorry, but I can't give you anything.`,
"field_trip_lesson_learned": `Looks like you learned a valuable lesson? "field_trip_lesson_learned": `Looks like you learned a valuable lesson?
$Your Pokémon also gained some knowledge.`, $Your Pokémon also gained some knowledge.`,
@ -153,13 +154,13 @@ export const mysteryEncounter = {
"training_session_option_3_label": "Heavy Training", "training_session_option_3_label": "Heavy Training",
"training_session_option_3_tooltip": "(-) Harsh Battle\n(+) Change Pokémon's Ability", "training_session_option_3_tooltip": "(-) Harsh Battle\n(+) Change Pokémon's Ability",
"training_session_option_3_select_prompt": "Select a new ability\nto train your Pokémon in.", "training_session_option_3_select_prompt": "Select a new ability\nto train your Pokémon in.",
"training_session_option_selected_message": "@ec{selectedPokemon} moves across\nthe clearing to face you...", "training_session_option_selected_message": "{{selectedPokemon}} moves across\nthe clearing to face you...",
"training_session_battle_finished_1": `@ec{selectedPokemon} returns, feeling\nworn out but accomplished! "training_session_battle_finished_1": `{{selectedPokemon}} returns, feeling\nworn out but accomplished!
$Its @ec{stat1} and @ec{stat2} IVs were improved!`, $Its {{stat1}} and {{stat2}} IVs were improved!`,
"training_session_battle_finished_2": `@ec{selectedPokemon} returns, feeling\nworn out but accomplished! "training_session_battle_finished_2": `{{selectedPokemon}} returns, feeling\nworn out but accomplished!
$Its nature was changed to @ec{nature}!`, $Its nature was changed to {{nature}}!`,
"training_session_battle_finished_3": `@ec{selectedPokemon} returns, feeling\nworn out but accomplished! "training_session_battle_finished_3": `{{selectedPokemon}} returns, feeling\nworn out but accomplished!
$Its ability was changed to @ec{ability}!`, $Its ability was changed to {{ability}}!`,
"training_session_outro_win": "That was a successful training session!", "training_session_outro_win": "That was a successful training session!",
// Mystery Encounters -- Super Rare Tier // Mystery Encounters -- Super Rare Tier
@ -177,10 +178,10 @@ export const mysteryEncounter = {
"dark_deal_option_1_tooltip": "(+) 5 Rogue Balls\n(?) Enhance a Random Pokémon", "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 {{pokeName}} will do nicely!
$Remember, I'm not responsible\nif anything bad happens!@d{32} Hehe...`, $Remember, I'm not responsible\nif anything bad happens!@d{32} Hehe...`,
"dark_deal_option_1_selected_message": `The man hands you 5 Rogue Balls. "dark_deal_option_1_selected_message": `The man hands you 5 Rogue Balls.
$@ec{pokeName} hops into the strange machine... \${{pokeName}} hops into the strange machine...
$Flashing lights and weird noises\nstart coming from the machine! $Flashing lights and weird noises\nstart coming from the machine!
$...@d{96} Something emerges\nfrom the device, raging wildly!`, $...@d{96} Something emerges\nfrom the device, raging wildly!`,
"dark_deal_option_2_selected": "Not gonna help a poor fellow out?\nPah!", "dark_deal_option_2_selected": "Not gonna help a poor fellow out?\nPah!",
@ -196,16 +197,16 @@ export const mysteryEncounter = {
"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 its item", "sleeping_snorlax_option_3_label": "Steal its item",
"sleeping_snorlax_option_3_tooltip": "(+) @ec{option3PrimaryName} uses @ec{option3PrimaryMove}\n(+) Leftovers", "sleeping_snorlax_option_3_tooltip": "(+) {{option3PrimaryName}} uses {{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}
$You wait for a time, but the Snorlax's yawns make your party sleepy.`, $You wait for a time, but the Snorlax's yawns make your party sleepy.`,
"sleeping_snorlax_option_2_good_result": "When you all awaken, the Snorlax is no where to be found - but your Pokémon are all healed!", "sleeping_snorlax_option_2_good_result": "When you all awaken, the Snorlax is no where to be found - but your Pokémon are all healed!",
"sleeping_snorlax_option_2_bad_result": `Your @ec{primaryName} is still asleep... "sleeping_snorlax_option_2_bad_result": `Your {{primaryName}} is still asleep...
$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 {{option3PrimaryName}} uses {{option3PrimaryMove}}! @s{item_fanfare}It steals Leftovers off the sleeping Snorlax and you make out like bandits!",
lostAtSea, lostAtSea,
} as const; } as const;

View File

@ -78,7 +78,7 @@ export class MysteryEncounterPhase extends Phase {
this.end(); this.end();
}; };
const optionSelectDialogue = this.scene.currentBattle?.mysteryEncounter?.dialogue?.encounterOptionsDialogue?.options?.[optionIndex]; const optionSelectDialogue = this.scene.currentBattle?.mysteryEncounter?.options?.[optionIndex]?.dialogue;
if (optionSelectDialogue?.selected?.length > 0) { if (optionSelectDialogue?.selected?.length > 0) {
// Handle intermediate dialogue (between player selection event and the onOptionSelect logic) // Handle intermediate dialogue (between player selection event and the onOptionSelect logic)
this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.setMode(Mode.MESSAGE);

View File

@ -136,6 +136,14 @@ export async function initI18n(): Promise<void> {
postProcess: ["korean-postposition"], postProcess: ["korean-postposition"],
}); });
// Input: {{myMoneyValue, money}}
// Output: @[MONEY]{₽100,000,000} (useful for BBCode coloring of text)
// If you don't want the BBCode tag applied, just use 'number' formatter
i18next.services.formatter.add("money", (value, lng, options) => {
const numberFormattedString = Intl.NumberFormat(lng, options).format(value);
return `@[MONEY]{₽${numberFormattedString}}`;
});
await initFonts(); await initFonts();
} }

View File

@ -277,7 +277,7 @@ describe("Mystery Encounter Utils", () => {
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");
expect(result).toEqual("valuevalue @ec{testvalue} @ec{test1} value @ec{test\\} @ec{test\\} {test}"); expect(result).toEqual("valuevalue {{testvalue}} {{test1}} {{test}} {{test\\}} {{test\\}} {test}}");
}); });
it("can perform nested dialogue token injection", () => { it("can perform nested dialogue token injection", () => {
@ -286,7 +286,7 @@ describe("Mystery Encounter Utils", () => {
scene.currentBattle.mysteryEncounter.setDialogueToken("testvalue", "new"); scene.currentBattle.mysteryEncounter.setDialogueToken("testvalue", "new");
const result = getEncounterText(scene, "mysteryEncounter:unit_test_dialogue"); const result = getEncounterText(scene, "mysteryEncounter:unit_test_dialogue");
expect(result).toEqual("valuevalue new @ec{test1} value @ec{test\\} @ec{test\\} {test}"); expect(result).toEqual("valuevalue {{testvalue}} {{test1}} {{test}} {{test\\}} {{test\\}} {test}}");
}); });
}); });
@ -298,7 +298,7 @@ describe("Mystery Encounter Utils", () => {
const phaseSpy = vi.spyOn(game.scene, "unshiftPhase"); const phaseSpy = vi.spyOn(game.scene, "unshiftPhase");
queueEncounterMessage(scene, "mysteryEncounter:unit_test_dialogue"); queueEncounterMessage(scene, "mysteryEncounter:unit_test_dialogue");
expect(spy).toHaveBeenCalledWith("valuevalue @ec{testvalue} @ec{test1} value @ec{test\\} @ec{test\\} {test}", null, true); expect(spy).toHaveBeenCalledWith("valuevalue {{testvalue}} {{test1}} {{test}} {{test\\}} {{test\\}} {test}}", null, true);
expect(phaseSpy).toHaveBeenCalledWith(expect.any(MessagePhase)); expect(phaseSpy).toHaveBeenCalledWith(expect.any(MessagePhase));
}); });
}); });
@ -310,7 +310,7 @@ describe("Mystery Encounter Utils", () => {
const spy = vi.spyOn(game.scene.ui, "showText"); const spy = vi.spyOn(game.scene.ui, "showText");
showEncounterText(scene, "mysteryEncounter:unit_test_dialogue"); showEncounterText(scene, "mysteryEncounter:unit_test_dialogue");
expect(spy).toHaveBeenCalledWith("valuevalue @ec{testvalue} @ec{test1} value @ec{test\\} @ec{test\\} {test}", null, expect.any(Function), 0, true); expect(spy).toHaveBeenCalledWith("valuevalue {{testvalue}} {{test1}} {{test}} {{test\\}} {{test\\}} {test}}", null, expect.any(Function), 0, true);
}); });
}); });
@ -321,7 +321,7 @@ describe("Mystery Encounter Utils", () => {
const spy = vi.spyOn(game.scene.ui, "showDialogue"); const spy = vi.spyOn(game.scene.ui, "showDialogue");
showEncounterDialogue(scene, "mysteryEncounter:unit_test_dialogue", "mysteryEncounter:unit_test_dialogue"); showEncounterDialogue(scene, "mysteryEncounter:unit_test_dialogue", "mysteryEncounter:unit_test_dialogue");
expect(spy).toHaveBeenCalledWith("valuevalue @ec{testvalue} @ec{test1} value @ec{test\\} @ec{test\\} {test}", "valuevalue @ec{testvalue} @ec{test1} value @ec{test\\} @ec{test\\} {test}", null, undefined, 0, 0); expect(spy).toHaveBeenCalledWith("valuevalue {{testvalue}} {{test1}} {{test}} {{test\\}} {{test\\}} {test}}", "valuevalue {{testvalue}} {{test1}} {{test}} {{test\\}} {{test\\}} {test}}", null, undefined, 0, 0);
}); });
}); });

View File

@ -289,13 +289,12 @@ export default class PhaseInterceptor {
* @param phase - The phase to start. * @param phase - The phase to start.
*/ */
setMode(mode: Mode, ...args: any[]): Promise<void> { setMode(mode: Mode, ...args: any[]): Promise<void> {
const currentPhase = this. const currentPhase = this.scene.getCurrentPhase();
scene.getCurrentPhase();
const instance = this.scene.ui; const instance = this.scene.ui;
console.log("setMode", mode, args); console.log("setMode", mode, args);
const ret = this.originalSetMode.apply(instance, [mode, ...args]); const ret = this.originalSetMode.apply(instance, [mode, ...args]);
if (!this.phases[currentPhase.constructor.name]) { if (!this.phases[currentPhase.constructor.name]) {
throw new Error(`missing ${currentPhase.constructor.name} in phaseInterceptior PHASES list`); throw new Error(`missing ${currentPhase.constructor.name} in phaseInterceptor PHASES list`);
} }
if (this.phases[currentPhase.constructor.name].endBySetMode) { if (this.phases[currentPhase.constructor.name].endBySetMode) {
this.inProgress?.callback(); this.inProgress?.callback();