WIP: add option to use dialogues in builder

This commit is contained in:
Felix Staud 2024-07-11 12:56:15 -07:00
parent 253447136a
commit 74125ef1cf
8 changed files with 195 additions and 111 deletions

View File

@ -1,44 +1,44 @@
import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue"; import MysteryEncounterDialogue from "#app/data/mystery-encounters/mystery-encounter-dialogue";
export const DarkDealDialogue: MysteryEncounterDialogue = { export const DarkDealDialogue: MysteryEncounterDialogue = {
intro: [ // intro: [
{ // {
text: "mysteryEncounter:dark_deal_intro_message" // text: "mysteryEncounter:dark_deal_intro_message"
}, // },
{ // {
speaker: "mysteryEncounter:dark_deal_speaker", // speaker: "mysteryEncounter:dark_deal_speaker",
text: "mysteryEncounter:dark_deal_intro_dialogue" // text: "mysteryEncounter:dark_deal_intro_dialogue"
} // }
], // ],
encounterOptionsDialogue: { encounterOptionsDialogue: {
title: "mysteryEncounter:dark_deal_title", title: "mysteryEncounter:dark_deal_title",
description: "mysteryEncounter:dark_deal_description", description: "mysteryEncounter:dark_deal_description",
query: "mysteryEncounter:dark_deal_query", query: "mysteryEncounter:dark_deal_query",
options: [ // options: [
{ // {
buttonLabel: "mysteryEncounter:dark_deal_option_1_label", // buttonLabel: "mysteryEncounter:dark_deal_option_1_label",
buttonTooltip: "mysteryEncounter:dark_deal_option_1_tooltip", // buttonTooltip: "mysteryEncounter:dark_deal_option_1_tooltip",
selected: [ // selected: [
{ // {
speaker: "mysteryEncounter:dark_deal_speaker", // speaker: "mysteryEncounter:dark_deal_speaker",
text: "mysteryEncounter:dark_deal_option_1_selected" // text: "mysteryEncounter:dark_deal_option_1_selected"
}, // },
{ // {
text: "mysteryEncounter:dark_deal_option_1_selected_message" // text: "mysteryEncounter:dark_deal_option_1_selected_message"
} // }
] // ]
}, // },
{ // {
buttonLabel: "mysteryEncounter:dark_deal_option_2_label", // buttonLabel: "mysteryEncounter:dark_deal_option_2_label",
buttonTooltip: "mysteryEncounter:dark_deal_option_2_tooltip", // buttonTooltip: "mysteryEncounter:dark_deal_option_2_tooltip",
selected: [ // selected: [
{ // {
speaker: "mysteryEncounter:dark_deal_speaker", // speaker: "mysteryEncounter:dark_deal_speaker",
text: "mysteryEncounter:dark_deal_option_2_selected" // text: "mysteryEncounter:dark_deal_option_2_selected"
} // }
] // ]
} // }
] // ]
}, },
outro: [ outro: [
{ {

View File

@ -7,14 +7,18 @@ import BattleScene from "../../../battle-scene";
import { AddPokeballModifierType } from "../../../modifier/modifier-type"; import { AddPokeballModifierType } from "../../../modifier/modifier-type";
import { PokeballType } from "../../pokeball"; import { PokeballType } from "../../pokeball";
import { getPokemonSpecies } from "../../pokemon-species"; import { getPokemonSpecies } from "../../pokemon-species";
import MysteryEncounter, { MysteryEncounterBuilder, MysteryEncounterTier } from "../mystery-encounter"; import MysteryEncounter, {
MysteryEncounterBuilder,
MysteryEncounterTier,
} from "../mystery-encounter";
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option"; import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
import { import {
EnemyPartyConfig, EnemyPokemonConfig, EnemyPartyConfig,
EnemyPokemonConfig,
getRandomPlayerPokemon, getRandomPlayerPokemon,
getRandomSpeciesByStarterTier, getRandomSpeciesByStarterTier,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
leaveEncounterWithoutBattle leaveEncounterWithoutBattle,
} from "../mystery-encounter-utils"; } from "../mystery-encounter-utils";
// Exclude Ultra Beasts, Paradox, Necrozma, Eternatus, and egg-locked mythicals // Exclude Ultra Beasts, Paradox, Necrozma, Eternatus, and egg-locked mythicals
@ -63,71 +67,132 @@ const excludedBosses = [
Species.ARCEUS, Species.ARCEUS,
Species.VICTINI, Species.VICTINI,
Species.MELTAN, Species.MELTAN,
Species.PECHARUNT Species.PECHARUNT,
]; ];
export const DarkDealEncounter: MysteryEncounter = MysteryEncounterBuilder export const DarkDealEncounter: MysteryEncounter =
.withEncounterType(MysteryEncounterType.DARK_DEAL) MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DARK_DEAL)
.withEncounterTier(MysteryEncounterTier.ROGUE) .withEncounterTier(MysteryEncounterTier.ROGUE)
.withIntroSpriteConfigs([ .withIntroSpriteConfigs([
{ {
spriteKey: "mad_scientist_m", spriteKey: "mad_scientist_m",
fileRoot: "mystery-encounters", fileRoot: "mystery-encounters",
hasShadow: true hasShadow: true,
}, },
{ {
spriteKey: "dark_deal_porygon", spriteKey: "dark_deal_porygon",
fileRoot: "mystery-encounters", fileRoot: "mystery-encounters",
hasShadow: true, hasShadow: true,
repeat: true repeat: true,
} },
]) ])
.withSceneWaveRangeRequirement(30, 180) // waves 30 to 180 .withIntroDialogue([
.withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party {
.withCatchAllowed(true) text: "mysteryEncounter:dark_deal_intro_message",
.withOption(new MysteryEncounterOptionBuilder() },
.withPreOptionPhase(async (scene: BattleScene) => { {
// Removes random pokemon (including fainted) from party and adds name to dialogue data tokens speaker: "mysteryEncounter:dark_deal_speaker",
// Will never return last battle able mon and instead pick fainted/unable to battle text: "mysteryEncounter:dark_deal_intro_dialogue",
const removedPokemon = getRandomPlayerPokemon(scene, false, true); },
scene.removePokemonFromPlayerParty(removedPokemon); ])
.withSceneWaveRangeRequirement(30, 180) // waves 30 to 180
.withScenePartySizeRequirement(2, 6) // Must have at least 2 pokemon in party
.withCatchAllowed(true)
.withOption(
new MysteryEncounterOptionBuilder()
.withDialogue({
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",
},
],
})
.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); scene.currentBattle.mysteryEncounter.setDialogueToken(
"pokeName",
removedPokemon.name
);
// Store removed pokemon types // Store removed pokemon types
scene.currentBattle.mysteryEncounter.misc = [removedPokemon.species.type1]; scene.currentBattle.mysteryEncounter.misc = [
if (removedPokemon.species.type2) { removedPokemon.species.type1,
scene.currentBattle.mysteryEncounter.misc.push(removedPokemon.species.type2); ];
} if (removedPokemon.species.type2) {
}) scene.currentBattle.mysteryEncounter.misc.push(
.withOptionPhase(async (scene: BattleScene) => { removedPokemon.species.type2
// Give the player 5 Rogue Balls );
scene.unshiftPhase(new ModifierRewardPhase(scene, () => new AddPokeballModifierType("rb", PokeballType.ROGUE_BALL, 5))); }
})
.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 // Start encounter with random legendary (7-10 starter strength) that has level additive
const bossTypes = scene.currentBattle.mysteryEncounter.misc as Type[]; const bossTypes = scene.currentBattle.mysteryEncounter.misc as Type[];
// Starter egg tier, 35/50/10/5 %odds for tiers 6/7/8/9+ // Starter egg tier, 35/50/10/5 %odds for tiers 6/7/8/9+
const roll = randSeedInt(100); const roll = randSeedInt(100);
const starterTier: number | [number, number] = roll > 65 ? 6 : roll > 15 ? 7 : roll > 5 ? 8 : [9, 10]; const starterTier: number | [number, number] =
const bossSpecies = getPokemonSpecies(getRandomSpeciesByStarterTier(starterTier, excludedBosses, bossTypes)); roll > 65 ? 6 : roll > 15 ? 7 : roll > 5 ? 8 : [9, 10];
const pokemonConfig: EnemyPokemonConfig = { const bossSpecies = getPokemonSpecies(
species: bossSpecies, getRandomSpeciesByStarterTier(
isBoss: true starterTier,
}; excludedBosses,
if (!isNullOrUndefined(bossSpecies.forms) && bossSpecies.forms.length > 0) { bossTypes
pokemonConfig.formIndex = 0; )
} );
const config: EnemyPartyConfig = { const pokemonConfig: EnemyPokemonConfig = {
levelAdditiveMultiplier: 0.75, species: bossSpecies,
pokemonConfigs: [pokemonConfig] isBoss: true,
}; };
return initBattleWithEnemyConfig(scene, config); if (
}) !isNullOrUndefined(bossSpecies.forms) &&
.build() bossSpecies.forms.length > 0
) ) {
.withOptionPhase(async (scene: BattleScene) => { pokemonConfig.formIndex = 0;
// Leave encounter with no rewards or exp }
leaveEncounterWithoutBattle(scene, true); const config: EnemyPartyConfig = {
return true; levelAdditiveMultiplier: 0.75,
}) pokemonConfigs: [pokemonConfig],
.build(); };
return initBattleWithEnemyConfig(scene, config);
})
.build()
)
.withOption(
new MysteryEncounterOptionBuilder()
.withDialogue({
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",
},
],
})
.withOptionPhase(async (scene: BattleScene) => {
// Leave encounter with no rewards or exp
leaveEncounterWithoutBattle(scene, true);
return true;
})
.build()
)
.build();

View File

@ -28,7 +28,7 @@ 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 {

View File

@ -127,6 +127,7 @@ export class MysteryEncounterOptionBuilder implements Partial<MysteryEncounterOp
onPreOptionPhase?: OptionPhaseCallback; onPreOptionPhase?: OptionPhaseCallback;
onOptionPhase?: OptionPhaseCallback; onOptionPhase?: OptionPhaseCallback;
onPostOptionPhase?: OptionPhaseCallback; onPostOptionPhase?: OptionPhaseCallback;
dialogue?: OptionTextDisplay;
withSceneRequirement(requirement: EncounterSceneRequirement): this & Required<Pick<MysteryEncounterOption, "requirements">> { withSceneRequirement(requirement: EncounterSceneRequirement): this & Required<Pick<MysteryEncounterOption, "requirements">> {
this.requirements.push(requirement); this.requirements.push(requirement);
@ -163,4 +164,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;
}
} }

View File

@ -135,15 +135,17 @@ export default class MysteryEncounter implements MysteryEncounter {
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 = Object.assign((this.dialogue ?? {}), allMysteryEncounterDialogue[this.encounterType]);
// this.dialogue = allMysteryEncounterDialogue[this.encounterType];
console.log(`${MysteryEncounterType[encounter.encounterType]} Encounter 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 // Populate options with respective dialogue
if (this.dialogue) { if (this.dialogue?.encounterOptionsDialogue) {
this.options.forEach((o, i) => o.dialogue = this.dialogue.encounterOptionsDialogue.options[i]); // 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
@ -408,9 +410,20 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
* @returns * @returns
*/ */
withIntroSpriteConfigs(spriteConfigs: MysteryEncounterSpriteConfig[]): this & Pick<MysteryEncounter, "spriteConfigs"> { withIntroSpriteConfigs(spriteConfigs: MysteryEncounterSpriteConfig[]): this & Pick<MysteryEncounter, "spriteConfigs"> {
console.debug("with intro sprite configs: ", spriteConfigs);
return Object.assign(this, { spriteConfigs: spriteConfigs }); return Object.assign(this, { spriteConfigs: spriteConfigs });
} }
withIntroDialogue(dialogue: MysteryEncounterDialogue["intro"] = []): this {
console.debug("with intro dialogue: ", dialogue);
this.dialogue = {...this.dialogue, intro: dialogue };
return this;
}
withIntro({spriteConfigs, dialogue} : {spriteConfigs: MysteryEncounterSpriteConfig[], dialogue?: MysteryEncounterDialogue["intro"]}) {
return this.withIntroSpriteConfigs(spriteConfigs).withIntroDialogue(dialogue);
}
/** /**
* OPTIONAL * OPTIONAL
*/ */

View File

@ -35,7 +35,7 @@ export const SEED_OVERRIDE: string = "";
export const WEATHER_OVERRIDE: WeatherType = WeatherType.NONE; export const WEATHER_OVERRIDE: WeatherType = WeatherType.NONE;
export const DOUBLE_BATTLE_OVERRIDE: boolean = false; export const DOUBLE_BATTLE_OVERRIDE: boolean = false;
export const SINGLE_BATTLE_OVERRIDE: boolean = false; export const SINGLE_BATTLE_OVERRIDE: boolean = false;
export const STARTING_WAVE_OVERRIDE: integer = 0; export const STARTING_WAVE_OVERRIDE: integer = 33;
export const STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN; export const STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN;
export const ARENA_TINT_OVERRIDE: TimeOfDay = null; export const ARENA_TINT_OVERRIDE: TimeOfDay = null;
// Multiplies XP gained by this value including 0. Set to null to ignore the override // Multiplies XP gained by this value including 0. Set to null to ignore the override
@ -118,9 +118,9 @@ export const EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0;
*/ */
// 1 to 256, set to null to ignore // 1 to 256, set to null to ignore
export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = null; export const MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = 10000;
export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null; export const MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null;
export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null; export const MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.DARK_DEAL;
/** /**
* MODIFIER / ITEM OVERRIDES * MODIFIER / ITEM OVERRIDES

View File

@ -828,7 +828,7 @@ export class EncounterPhase extends BattlePhase {
if (mysteryEncounter.onInit) { if (mysteryEncounter.onInit) {
mysteryEncounter.onInit(this.scene); mysteryEncounter.onInit(this.scene);
} }
mysteryEncounter.populateDialogueTokensFromRequirements(this.scene); // mysteryEncounter.populateDialogueTokensFromRequirements(this.scene);
}, this.scene.currentBattle.waveIndex); }, this.scene.currentBattle.waveIndex);
// Add intro visuals for mystery encounter // Add intro visuals for mystery encounter

View File

@ -319,13 +319,13 @@ 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 option = this.filteredEncounterOptions[i];
const text = getEncounterText(this.scene, option.buttonLabel, option.style ? option.style : TextStyle.WINDOW); const text = getEncounterText(this.scene, option.dialogue?.buttonLabel, option.dialogue?.style ? option.dialogue?.style : TextStyle.WINDOW);
if (text) { if (text) {
optionText.setText(text); optionText.setText(text);
} }
this.optionsMeetsReqs.push(this.filteredEncounterOptions[i].meetsRequirements(this.scene)); this.optionsMeetsReqs.push(option.meetsRequirements(this.scene));
if (!this.optionsMeetsReqs[i]) { if (!this.optionsMeetsReqs[i]) {
optionText.setAlpha(0.5); optionText.setAlpha(0.5);