add encounter exp utility and small cleanups/refactors
This commit is contained in:
parent
cf43589260
commit
deb1e95e34
|
@ -2681,7 +2681,7 @@ export default class BattleScene extends SceneBase {
|
|||
const tier = val[1];
|
||||
if (tier === MysteryEncounterTier.COMMON) {
|
||||
tierWeights[0] = tierWeights[0] - 6;
|
||||
} else if (tier === MysteryEncounterTier.UNCOMMON) {
|
||||
} else if (tier === MysteryEncounterTier.GREAT) {
|
||||
tierWeights[1] = tierWeights[1] - 4;
|
||||
}
|
||||
});
|
||||
|
@ -2691,7 +2691,7 @@ export default class BattleScene extends SceneBase {
|
|||
const commonThreshold = totalWeight - tierWeights[0];
|
||||
const uncommonThreshold = totalWeight - tierWeights[0] - tierWeights[1];
|
||||
const rareThreshold = totalWeight - tierWeights[0] - tierWeights[1] - tierWeights[2];
|
||||
let tier = tierValue > commonThreshold ? MysteryEncounterTier.COMMON : tierValue > uncommonThreshold ? MysteryEncounterTier.UNCOMMON : tierValue > rareThreshold ? MysteryEncounterTier.RARE : MysteryEncounterTier.SUPER_RARE;
|
||||
let tier = tierValue > commonThreshold ? MysteryEncounterTier.COMMON : tierValue > uncommonThreshold ? MysteryEncounterTier.GREAT : tierValue > rareThreshold ? MysteryEncounterTier.ULTRA : MysteryEncounterTier.ROGUE;
|
||||
|
||||
if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE)) {
|
||||
tier = Overrides.MYSTERY_ENCOUNTER_TIER_OVERRIDE;
|
||||
|
|
|
@ -239,7 +239,7 @@ export class MoneyRequirement extends EncounterSceneRequirement {
|
|||
getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||
const value = this?.scalingMultiplier > 0 ? scene.getWaveMoneyAmount(this.scalingMultiplier).toString() : this.requiredMoney.toString();
|
||||
// Colors money text
|
||||
return ["money", "@ecCol[MONEY]{₽" + value + "}"];
|
||||
return ["money", "@[MONEY]{₽" + value + "}"];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,10 +24,10 @@ export enum MysteryEncounterVariant {
|
|||
|
||||
export enum MysteryEncounterTier {
|
||||
COMMON,
|
||||
UNCOMMON,
|
||||
RARE,
|
||||
SUPER_RARE,
|
||||
ULTRA_RARE // Not currently used
|
||||
GREAT,
|
||||
ULTRA,
|
||||
ROGUE,
|
||||
MASTER // Not currently used
|
||||
}
|
||||
|
||||
export default interface MysteryEncounter {
|
||||
|
@ -44,6 +44,7 @@ export default interface MysteryEncounter {
|
|||
hideBattleIntroMessage?: boolean;
|
||||
hideIntroVisuals?: boolean;
|
||||
catchAllowed?: boolean;
|
||||
doEncounterExp?: (scene: BattleScene) => boolean;
|
||||
doEncounterRewards?: (scene: BattleScene) => boolean;
|
||||
onInit?: (scene: BattleScene) => boolean;
|
||||
|
||||
|
@ -347,6 +348,7 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
|||
secondaryPokemonRequirements ?: EncounterPokemonRequirement[] = [];
|
||||
excludePrimaryFromSupportRequirements?: boolean;
|
||||
dialogueTokens?: Map<string, [RegExp, string]>;
|
||||
doEncounterExp?: (scene: BattleScene) => boolean;
|
||||
doEncounterRewards?: (scene: BattleScene) => boolean;
|
||||
onInit?: (scene: BattleScene) => boolean;
|
||||
hideBattleIntroMessage?: boolean;
|
||||
|
@ -448,8 +450,7 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
|||
*
|
||||
* NOTE: If rewards are dependent on options selected, runtime data, etc.,
|
||||
* It may be better to programmatically set doEncounterRewards elsewhere.
|
||||
* For instance, doEncounterRewards could instead be set inside the onOptionPhase() callback function for a MysteryEncounterOption
|
||||
* Check other existing mystery encounters for examples on how to use this
|
||||
* There is a helper function in mystery-encounter utils, setEncounterRewards(), which can be called programmatically to set rewards
|
||||
* @param doEncounterRewards - synchronous callback function to perform during rewards phase of the encounter
|
||||
* @returns
|
||||
*/
|
||||
|
@ -457,6 +458,20 @@ export class MysteryEncounterBuilder implements Partial<MysteryEncounter> {
|
|||
return Object.assign(this, { doEncounterRewards: doEncounterRewards });
|
||||
}
|
||||
|
||||
/**
|
||||
* Can set custom encounter exp via this callback function
|
||||
* If exp always deterministic for an encounter, this is a good way to set them
|
||||
*
|
||||
* NOTE: If rewards are dependent on options selected, runtime data, etc.,
|
||||
* It may be better to programmatically set doEncounterExp elsewhere.
|
||||
* There is a helper function in mystery-encounter utils, setEncounterExp(), which can be called programmatically to set rewards
|
||||
* @param doEncounterExp - synchronous callback function to perform during rewards phase of the encounter
|
||||
* @returns
|
||||
*/
|
||||
withExp(doEncounterExp: (scene: BattleScene) => boolean): this & Required<Pick<MysteryEncounter, "doEncounterExp">> {
|
||||
return Object.assign(this, { doEncounterExp: doEncounterExp });
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to perform init logic before intro visuals are shown and before the MysteryEncounterPhase begins
|
||||
* Useful for performing things like procedural generation of intro sprites, etc.
|
||||
|
|
|
@ -69,7 +69,7 @@ const excludedBosses = [
|
|||
|
||||
export const DarkDealEncounter: MysteryEncounter = new MysteryEncounterBuilder()
|
||||
.withEncounterType(MysteryEncounterType.DARK_DEAL)
|
||||
.withEncounterTier(MysteryEncounterTier.ULTRA_RARE)
|
||||
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
||||
.withIntroSpriteConfigs([
|
||||
{
|
||||
spriteKey: "mad_scientist_m",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import BattleScene from "../../battle-scene";
|
||||
import {
|
||||
leaveEncounterWithoutBattle,
|
||||
setCustomEncounterRewards,
|
||||
leaveEncounterWithoutBattle, setEncounterExp,
|
||||
setEncounterRewards,
|
||||
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||
import MysteryEncounter, {MysteryEncounterBuilder, MysteryEncounterTier} from "../mystery-encounter";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
|
@ -49,7 +49,8 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = new MysteryEncount
|
|||
i++;
|
||||
}
|
||||
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false});
|
||||
setEncounterExp(scene, scene.getParty().map(p => p.id), 300);
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false});
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
})
|
||||
.build())
|
||||
|
@ -69,7 +70,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = new MysteryEncount
|
|||
i++;
|
||||
}
|
||||
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false});
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false});
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
})
|
||||
.build())
|
||||
|
@ -89,7 +90,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = new MysteryEncount
|
|||
i++;
|
||||
}
|
||||
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false});
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false});
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
})
|
||||
.build())
|
||||
|
@ -113,7 +114,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = new MysteryEncount
|
|||
i++;
|
||||
}
|
||||
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false});
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false});
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
})
|
||||
.build())
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
EnemyPartyConfig,
|
||||
initBattleWithEnemyConfig,
|
||||
leaveEncounterWithoutBattle, queueEncounterMessage,
|
||||
setCustomEncounterRewards,
|
||||
setEncounterRewards,
|
||||
showEncounterText
|
||||
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||
import MysteryEncounter, {MysteryEncounterBuilder, MysteryEncounterTier} from "../mystery-encounter";
|
||||
|
@ -103,7 +103,7 @@ export const FightOrFlightEncounter: MysteryEncounter = new MysteryEncounterBuil
|
|||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
// Pick battle
|
||||
const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption;
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false});
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false});
|
||||
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter.enemyPartyConfigs[0]);
|
||||
})
|
||||
.build())
|
||||
|
@ -112,7 +112,7 @@ export const FightOrFlightEncounter: MysteryEncounter = new MysteryEncounterBuil
|
|||
// Pick steal
|
||||
const encounter = scene.currentBattle.mysteryEncounter;
|
||||
const item = scene.currentBattle.mysteryEncounter.misc as ModifierTypeOption;
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false});
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false});
|
||||
|
||||
// If player has a stealing move, they succeed automatically
|
||||
const moveRequirement = new MoveRequirement(validMovesForSteal);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import BattleScene from "../../battle-scene";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import {modifierTypes} from "#app/modifier/modifier-type";
|
||||
import { EnemyPartyConfig, initBattleWithEnemyConfig, setCustomEncounterRewards } from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||
import { EnemyPartyConfig, initBattleWithEnemyConfig, setEncounterRewards } from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import MysteryEncounter, {MysteryEncounterBuilder, MysteryEncounterTier} from "../mystery-encounter";
|
||||
import { MysteryEncounterOptionBuilder } from "../mystery-encounter-option";
|
||||
|
@ -17,7 +17,7 @@ import {PartyMemberStrength} from "#enums/party-member-strength";
|
|||
|
||||
export const MysteriousChallengersEncounter: MysteryEncounter = new MysteryEncounterBuilder()
|
||||
.withEncounterType(MysteryEncounterType.MYSTERIOUS_CHALLENGERS)
|
||||
.withEncounterTier(MysteryEncounterTier.UNCOMMON)
|
||||
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||
.withIntroSpriteConfigs([]) // These are set in onInit()
|
||||
.withSceneRequirement(new WaveCountRequirement([10, 180])) // waves 10 to 180
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
|
@ -103,7 +103,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = new MysteryEncou
|
|||
// Spawn standard trainer battle with memory mushroom reward
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM], fillRemaining: true });
|
||||
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;
|
||||
|
@ -119,7 +119,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = new MysteryEncou
|
|||
// Spawn hard fight with ULTRA/GREAT reward (can improve with luck)
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1];
|
||||
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], fillRemaining: true });
|
||||
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;
|
||||
|
@ -138,7 +138,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = new MysteryEncou
|
|||
// To avoid player level snowballing from picking this option
|
||||
encounter.expMultiplier = 0.9;
|
||||
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT], fillRemaining: true });
|
||||
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;
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
koPlayerPokemon,
|
||||
leaveEncounterWithoutBattle,
|
||||
queueEncounterMessage,
|
||||
setCustomEncounterRewards,
|
||||
setEncounterRewards,
|
||||
showEncounterText
|
||||
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||
import MysteryEncounter, {MysteryEncounterBuilder, MysteryEncounterTier} from "../mystery-encounter";
|
||||
|
@ -42,25 +42,25 @@ export const MysteriousChestEncounter: MysteryEncounter = new MysteryEncounterBu
|
|||
const roll = randSeedInt(100);
|
||||
if (roll > 60) {
|
||||
// Choose between 2 COMMON / 2 GREAT tier items (40%)
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.COMMON, ModifierTier.COMMON, ModifierTier.GREAT, ModifierTier.GREAT]});
|
||||
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%)
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.ULTRA]});
|
||||
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%)
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE]});
|
||||
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%)
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.MASTER]});
|
||||
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.MASTER]});
|
||||
// Display result message then proceed to rewards
|
||||
queueEncounterMessage(scene, "mysteryEncounter:mysterious_chest_option_1_amazing_result");
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
|
|
|
@ -7,7 +7,12 @@ import {Status, StatusEffect} from "../status-effect";
|
|||
import {TrainerConfig, trainerConfigs, TrainerSlot} from "../trainer-config";
|
||||
import Pokemon, {FieldPosition, PlayerPokemon} from "#app/field/pokemon";
|
||||
import Trainer, {TrainerVariant} from "../../field/trainer";
|
||||
import {PokemonExpBoosterModifier} from "#app/modifier/modifier";
|
||||
import {
|
||||
ExpBalanceModifier,
|
||||
ExpShareModifier,
|
||||
MultipleParticipantExpBonusModifier,
|
||||
PokemonExpBoosterModifier
|
||||
} from "#app/modifier/modifier";
|
||||
import {
|
||||
CustomModifierSettings,
|
||||
getModifierPoolForType,
|
||||
|
@ -19,7 +24,14 @@ import {
|
|||
PokemonHeldItemModifierType,
|
||||
regenerateModifierPoolThresholds
|
||||
} from "#app/modifier/modifier-type";
|
||||
import {BattleEndPhase, EggLapsePhase, ModifierRewardPhase, TrainerVictoryPhase} from "#app/phases";
|
||||
import {
|
||||
BattleEndPhase,
|
||||
EggLapsePhase,
|
||||
ExpPhase,
|
||||
ModifierRewardPhase,
|
||||
ShowPartyExpBarPhase,
|
||||
TrainerVictoryPhase
|
||||
} from "#app/phases";
|
||||
import {MysteryEncounterBattlePhase, MysteryEncounterRewardsPhase} from "#app/phases/mystery-encounter-phase";
|
||||
import * as Utils from "../../utils";
|
||||
import {isNullOrUndefined} from "#app/utils";
|
||||
|
@ -35,7 +47,9 @@ 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 {WIGHT_INCREMENT_ON_SPAWN_MISS} from "#app/data/mystery-encounters/mystery-encounters";
|
||||
import {getBBCodeFrag, TextStyle} from "#app/ui/text";
|
||||
import {getTextWithColors, TextStyle} from "#app/ui/text";
|
||||
import * as Overrides from "#app/overrides";
|
||||
import {UiTheme} from "#enums/ui-theme";
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -167,17 +181,29 @@ export function koPlayerPokemon(pokemon: PlayerPokemon) {
|
|||
pokemon.updateInfo();
|
||||
}
|
||||
|
||||
export function getTextWithEncounterDialogueTokensAndColor(scene: BattleScene, textKey: TemplateStringsArray | `mysteryEncounter:${string}`, primaryStyle: TextStyle = TextStyle.MESSAGE): string {
|
||||
export function getEncounterText(scene: BattleScene, textKey: TemplateStringsArray | `mysteryEncounter:${string}`, primaryStyle?: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string {
|
||||
if (isNullOrUndefined(textKey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let textString: string = getTextWithDialogueTokens(scene, textKey);
|
||||
|
||||
// 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
|
||||
if (primaryStyle) {
|
||||
textString = getTextWithColors(textString, primaryStyle, uiTheme);
|
||||
}
|
||||
|
||||
return textString;
|
||||
}
|
||||
|
||||
function getTextWithDialogueTokens(scene: BattleScene, textKey: TemplateStringsArray | `mysteryEncounter:${string}`): string {
|
||||
if (isNullOrUndefined(textKey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let textString: string = i18next.t(textKey);
|
||||
|
||||
// Apply primary styling before anything else, if it exists
|
||||
textString = getBBCodeFrag(textString, primaryStyle) + "[/color][/shadow]";
|
||||
const primaryStyleString = [...textString.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))][0];
|
||||
|
||||
// Apply dialogue tokens
|
||||
const dialogueTokens = scene.currentBattle?.mysteryEncounter?.dialogueTokens;
|
||||
if (dialogueTokens) {
|
||||
|
@ -186,16 +212,6 @@ export function getTextWithEncounterDialogueTokensAndColor(scene: BattleScene, t
|
|||
});
|
||||
}
|
||||
|
||||
// Set custom colors
|
||||
// Looks for any pattern like this: @ecCol[SUMMARY_BLUE]{my text to color}
|
||||
// Resulting in: "my text to color" string with TextStyle.SUMMARY_BLUE
|
||||
textString = textString.replace(/@ecCol\[([^{]*)\]{([^}]*)}/gi, (substring, textStyle: string, textToColor: string) => {
|
||||
return "[/color][/shadow]" + getBBCodeFrag(textToColor, TextStyle[textStyle]) + "[/color][/shadow]" + primaryStyleString;
|
||||
});
|
||||
|
||||
// Remove extra style block at the end
|
||||
textString = textString.replace(/\[color=[^\[]*\]\[shadow=[^\[]*\]\[\/color\]\[\/shadow\]/gi, "");
|
||||
|
||||
return textString;
|
||||
}
|
||||
|
||||
|
@ -205,7 +221,7 @@ export function getTextWithEncounterDialogueTokensAndColor(scene: BattleScene, t
|
|||
* @param contentKey
|
||||
*/
|
||||
export function queueEncounterMessage(scene: BattleScene, contentKey: TemplateStringsArray | `mysteryEncounter:${string}`): void {
|
||||
const text: string = getTextWithEncounterDialogueTokensAndColor(scene, contentKey, TextStyle.MESSAGE);
|
||||
const text: string = getEncounterText(scene, contentKey);
|
||||
scene.queueMessage(text, null, true);
|
||||
}
|
||||
|
||||
|
@ -216,7 +232,7 @@ export function queueEncounterMessage(scene: BattleScene, contentKey: TemplateSt
|
|||
*/
|
||||
export function showEncounterText(scene: BattleScene, contentKey: TemplateStringsArray | `mysteryEncounter:${string}`): Promise<void> {
|
||||
return new Promise<void>(resolve => {
|
||||
const text: string = getTextWithEncounterDialogueTokensAndColor(scene, contentKey, TextStyle.MESSAGE);
|
||||
const text: string = getEncounterText(scene, contentKey);
|
||||
scene.ui.showText(text, null, () => resolve(), 0, true);
|
||||
});
|
||||
}
|
||||
|
@ -229,8 +245,8 @@ export function showEncounterText(scene: BattleScene, contentKey: TemplateString
|
|||
* @param callback
|
||||
*/
|
||||
export function showEncounterDialogue(scene: BattleScene, textContentKey: TemplateStringsArray | `mysteryEncounter:${string}`, speakerContentKey: TemplateStringsArray | `mysteryEncounter:${string}`, callback?: Function) {
|
||||
const text: string = getTextWithEncounterDialogueTokensAndColor(scene, textContentKey, TextStyle.MESSAGE);
|
||||
const speaker: string = getTextWithEncounterDialogueTokensAndColor(scene, speakerContentKey);
|
||||
const text: string = getEncounterText(scene, textContentKey);
|
||||
const speaker: string = getEncounterText(scene, speakerContentKey);
|
||||
scene.ui.showDialogue(text, speaker, null, callback, 0, 0);
|
||||
}
|
||||
|
||||
|
@ -246,6 +262,7 @@ export class EnemyPokemonConfig {
|
|||
tags?: BattlerTagType[];
|
||||
mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void;
|
||||
status?: StatusEffect;
|
||||
passive?: boolean;
|
||||
}
|
||||
|
||||
export class EnemyPartyConfig {
|
||||
|
@ -341,6 +358,11 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
|||
|
||||
const enemyPokemon = scene.getEnemyParty()[e];
|
||||
|
||||
// Make sure basic data is clean
|
||||
enemyPokemon.hp = enemyPokemon.getMaxHp();
|
||||
enemyPokemon.status = null;
|
||||
enemyPokemon.passive = false;
|
||||
|
||||
if (e < (doubleBattle ? 2 : 1)) {
|
||||
enemyPokemon.setX(-66 + enemyPokemon.getFieldPositionOffset()[0]);
|
||||
enemyPokemon.resetSummonData();
|
||||
|
@ -353,7 +375,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
|||
if (e < partyConfig?.pokemonConfigs?.length) {
|
||||
const config = partyConfig?.pokemonConfigs?.[e];
|
||||
|
||||
// Generate new id in case using data source
|
||||
// Generate new id, reset status and HP in case using data source
|
||||
if (config.dataSource) {
|
||||
enemyPokemon.id = Utils.randSeedInt(4294967296);
|
||||
}
|
||||
|
@ -372,6 +394,11 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
|||
enemyPokemon.setBoss(true, segments);
|
||||
}
|
||||
|
||||
// Set Passive
|
||||
if (partyConfig.pokemonConfigs[e].passive) {
|
||||
enemyPokemon.passive = true;
|
||||
}
|
||||
|
||||
// Set Status
|
||||
if (partyConfig.pokemonConfigs[e].status) {
|
||||
// Default to cureturn 3 for sleep
|
||||
|
@ -462,40 +489,6 @@ export function generateModifierType(scene: BattleScene, modifier: () => Modifie
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will initialize reward phases to follow the mystery encounter
|
||||
* Can have shop displayed or skipped
|
||||
* @param scene - Battle Scene
|
||||
* @param customShopRewards - adds a shop phase with the specified rewards / reward tiers
|
||||
* @param nonShopRewards - will add a non-shop reward phase for each specified item/modifier (can happen in addition to a shop)
|
||||
* @param preRewardsCallback - can execute an arbitrary callback before the new phases if necessary (useful for updating items/party/injecting new phases before MysteryEncounterRewardsPhase)
|
||||
*/
|
||||
export function setCustomEncounterRewards(scene: BattleScene, customShopRewards?: CustomModifierSettings, nonShopRewards?: ModifierTypeFunc[], preRewardsCallback?: Function) {
|
||||
scene.currentBattle.mysteryEncounter.doEncounterRewards = (scene: BattleScene) => {
|
||||
if (preRewardsCallback) {
|
||||
preRewardsCallback();
|
||||
}
|
||||
|
||||
if (customShopRewards) {
|
||||
scene.unshiftPhase(new SelectModifierPhase(scene, 0, null, customShopRewards));
|
||||
} else {
|
||||
scene.tryRemovePhase(p => p instanceof SelectModifierPhase);
|
||||
}
|
||||
|
||||
if (nonShopRewards?.length > 0) {
|
||||
nonShopRewards.forEach((reward) => {
|
||||
scene.unshiftPhase(new ModifierRewardPhase(scene, reward));
|
||||
});
|
||||
} else {
|
||||
while (!isNullOrUndefined(scene.findPhase(p => p instanceof ModifierRewardPhase))) {
|
||||
scene.tryRemovePhase(p => p instanceof ModifierRewardPhase);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param scene
|
||||
|
@ -557,7 +550,7 @@ export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (p
|
|||
if (!textPromptKey) {
|
||||
displayOptions();
|
||||
} else {
|
||||
const secondOptionSelectPrompt = getTextWithEncounterDialogueTokensAndColor(scene, textPromptKey, TextStyle.MESSAGE);
|
||||
const secondOptionSelectPrompt = getEncounterText(scene, textPromptKey, TextStyle.MESSAGE);
|
||||
scene.ui.showText(secondOptionSelectPrompt, null, displayOptions, null, true);
|
||||
}
|
||||
});
|
||||
|
@ -575,49 +568,140 @@ export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (p
|
|||
}
|
||||
|
||||
/**
|
||||
* Will initialize exp phases to follow the mystery encounter (in addition to any combat or other exp earned)
|
||||
* Exp earned will be a simple function that linearly scales with wave index, that can be increased or decreased by the expMultiplier
|
||||
* Exp Share will have no effect (so no accounting for what mon is "on the field")
|
||||
* Exp Balance will still function as normal
|
||||
* Will initialize reward phases to follow the mystery encounter
|
||||
* Can have shop displayed or skipped
|
||||
* @param scene - Battle Scene
|
||||
* @param expMultiplier - default is 100, can be increased or decreased as desired
|
||||
* @param customShopRewards - adds a shop phase with the specified rewards / reward tiers
|
||||
* @param nonShopRewards - will add a non-shop reward phase for each specified item/modifier (can happen in addition to a shop)
|
||||
* @param preRewardsCallback - can execute an arbitrary callback before the new phases if necessary (useful for updating items/party/injecting new phases before MysteryEncounterRewardsPhase)
|
||||
*/
|
||||
export function setEncounterExp(scene: BattleScene, expMultiplier: number = 100) {
|
||||
//const expBalanceModifier = scene.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier;
|
||||
const expVal = scene.currentBattle.waveIndex * expMultiplier;
|
||||
const pokemonExp = new Utils.NumberHolder(expVal);
|
||||
const partyMemberExp = [];
|
||||
export function setEncounterRewards(scene: BattleScene, customShopRewards?: CustomModifierSettings, nonShopRewards?: ModifierTypeFunc[], preRewardsCallback?: Function) {
|
||||
scene.currentBattle.mysteryEncounter.doEncounterRewards = (scene: BattleScene) => {
|
||||
if (preRewardsCallback) {
|
||||
preRewardsCallback();
|
||||
}
|
||||
|
||||
const party = scene.getParty();
|
||||
party.forEach(pokemon => {
|
||||
scene.applyModifiers(PokemonExpBoosterModifier, true, pokemon, pokemonExp);
|
||||
partyMemberExp.push(Math.floor(pokemonExp.value));
|
||||
});
|
||||
if (customShopRewards) {
|
||||
scene.unshiftPhase(new SelectModifierPhase(scene, 0, null, customShopRewards));
|
||||
} else {
|
||||
scene.tryRemovePhase(p => p instanceof SelectModifierPhase);
|
||||
}
|
||||
|
||||
// TODO
|
||||
//if (expBalanceModifier) {
|
||||
// let totalLevel = 0;
|
||||
// let totalExp = 0;
|
||||
// expPartyMembers.forEach((expPartyMember, epm) => {
|
||||
// totalExp += partyMemberExp[epm];
|
||||
// totalLevel += expPartyMember.level;
|
||||
// });
|
||||
if (nonShopRewards?.length > 0) {
|
||||
nonShopRewards.forEach((reward) => {
|
||||
scene.unshiftPhase(new ModifierRewardPhase(scene, reward));
|
||||
});
|
||||
} else {
|
||||
while (!isNullOrUndefined(scene.findPhase(p => p instanceof ModifierRewardPhase))) {
|
||||
scene.tryRemovePhase(p => p instanceof ModifierRewardPhase);
|
||||
}
|
||||
}
|
||||
|
||||
// const medianLevel = Math.floor(totalLevel / expPartyMembers.length);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
// const recipientExpPartyMemberIndexes = [];
|
||||
// expPartyMembers.forEach((expPartyMember, epm) => {
|
||||
// if (expPartyMember.level <= medianLevel) {
|
||||
// recipientExpPartyMemberIndexes.push(epm);
|
||||
// }
|
||||
// });
|
||||
/**
|
||||
* Will initialize exp phases into the phase queue (these are in addition to any combat or other exp earned)
|
||||
* Exp Share and Exp Balance will still function as normal
|
||||
* @param scene - Battle Scene
|
||||
* @param participantIds - ids of party pokemon that get full exp value. Other party members will receive Exp Share amounts
|
||||
* @param baseExpValue - gives exp equivalent to a pokemon of the wave index's level.
|
||||
* Guidelines:
|
||||
* 36 - Sunkern (lowest in game)
|
||||
* 62-64 - regional starter base evos
|
||||
* 100 - Scyther
|
||||
* 170 - Spiritomb
|
||||
* 250 - Gengar
|
||||
* 290 - trio legendaries
|
||||
* 340 - box legendaries
|
||||
* 608 - Blissey (highest in game)
|
||||
* @param useWaveIndex - set to false when directly passing the the full exp value instead of baseExpValue
|
||||
*/
|
||||
export function setEncounterExp(scene: BattleScene, participantIds: integer[], baseExpValue: number, useWaveIndex: boolean = true) {
|
||||
scene.currentBattle.mysteryEncounter.doEncounterExp = (scene: BattleScene) => {
|
||||
const party = scene.getParty();
|
||||
const expShareModifier = scene.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier;
|
||||
const expBalanceModifier = scene.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier;
|
||||
const multipleParticipantExpBonusModifier = scene.findModifier(m => m instanceof MultipleParticipantExpBonusModifier) as MultipleParticipantExpBonusModifier;
|
||||
const nonFaintedPartyMembers = party.filter(p => p.hp);
|
||||
const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < scene.getMaxExpLevel());
|
||||
const partyMemberExp = [];
|
||||
let expValue = baseExpValue * (useWaveIndex ? scene.currentBattle.waveIndex : 1);
|
||||
|
||||
// const splitExp = Math.floor(totalExp / recipientExpPartyMemberIndexes.length);
|
||||
if (participantIds?.length > 0) {
|
||||
if (scene.currentBattle.mysteryEncounter.encounterVariant === MysteryEncounterVariant.TRAINER_BATTLE) {
|
||||
expValue = Math.floor(expValue * 1.5);
|
||||
}
|
||||
for (const partyMember of nonFaintedPartyMembers) {
|
||||
const pId = partyMember.id;
|
||||
const participated = participantIds.includes(pId);
|
||||
if (participated) {
|
||||
partyMember.addFriendship(2);
|
||||
}
|
||||
if (!expPartyMembers.includes(partyMember)) {
|
||||
continue;
|
||||
}
|
||||
if (!participated && !expShareModifier) {
|
||||
partyMemberExp.push(0);
|
||||
continue;
|
||||
}
|
||||
let expMultiplier = 0;
|
||||
if (participated) {
|
||||
expMultiplier += (1 / participantIds.length);
|
||||
if (participantIds.length > 1 && multipleParticipantExpBonusModifier) {
|
||||
expMultiplier += multipleParticipantExpBonusModifier.getStackCount() * 0.2;
|
||||
}
|
||||
} else if (expShareModifier) {
|
||||
expMultiplier += (expShareModifier.getStackCount() * 0.2) / participantIds.length;
|
||||
}
|
||||
if (partyMember.pokerus) {
|
||||
expMultiplier *= 1.5;
|
||||
}
|
||||
if (Overrides.XP_MULTIPLIER_OVERRIDE !== null) {
|
||||
expMultiplier = Overrides.XP_MULTIPLIER_OVERRIDE;
|
||||
}
|
||||
const pokemonExp = new Utils.NumberHolder(expValue * expMultiplier);
|
||||
scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, pokemonExp);
|
||||
partyMemberExp.push(Math.floor(pokemonExp.value));
|
||||
}
|
||||
|
||||
// expPartyMembers.forEach((_partyMember, pm) => {
|
||||
// partyMemberExp[pm] = Phaser.Math.Linear(partyMemberExp[pm], recipientExpPartyMemberIndexes.indexOf(pm) > -1 ? splitExp : 0, 0.2 * expBalanceModifier.getStackCount());
|
||||
// });
|
||||
//}
|
||||
if (expBalanceModifier) {
|
||||
let totalLevel = 0;
|
||||
let totalExp = 0;
|
||||
expPartyMembers.forEach((expPartyMember, epm) => {
|
||||
totalExp += partyMemberExp[epm];
|
||||
totalLevel += expPartyMember.level;
|
||||
});
|
||||
|
||||
const medianLevel = Math.floor(totalLevel / expPartyMembers.length);
|
||||
|
||||
const recipientExpPartyMemberIndexes = [];
|
||||
expPartyMembers.forEach((expPartyMember, epm) => {
|
||||
if (expPartyMember.level <= medianLevel) {
|
||||
recipientExpPartyMemberIndexes.push(epm);
|
||||
}
|
||||
});
|
||||
|
||||
const splitExp = Math.floor(totalExp / recipientExpPartyMemberIndexes.length);
|
||||
|
||||
expPartyMembers.forEach((_partyMember, pm) => {
|
||||
partyMemberExp[pm] = Phaser.Math.Linear(partyMemberExp[pm], recipientExpPartyMemberIndexes.indexOf(pm) > -1 ? splitExp : 0, 0.2 * expBalanceModifier.getStackCount());
|
||||
});
|
||||
}
|
||||
|
||||
for (let pm = 0; pm < expPartyMembers.length; pm++) {
|
||||
const exp = partyMemberExp[pm];
|
||||
|
||||
if (exp) {
|
||||
const partyMemberIndex = party.indexOf(expPartyMembers[pm]);
|
||||
scene.unshiftPhase(expPartyMembers[pm].isOnField() ? new ExpPhase(scene, partyMemberIndex, exp) : new ShowPartyExpBarPhase(scene, partyMemberIndex, exp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
leaveEncounterWithoutBattle,
|
||||
queueEncounterMessage,
|
||||
selectPokemonForOption,
|
||||
setCustomEncounterRewards,
|
||||
setEncounterRewards,
|
||||
updatePlayerMoney,
|
||||
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||
import MysteryEncounter, {MysteryEncounterBuilder, MysteryEncounterTier} from "../mystery-encounter";
|
||||
|
@ -134,7 +134,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = new MysteryEncounte
|
|||
i++;
|
||||
}
|
||||
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false});
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: modifiers, fillRemaining: false});
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
})
|
||||
.build())
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
EnemyPokemonConfig, generateModifierType,
|
||||
initBattleWithEnemyConfig,
|
||||
leaveEncounterWithoutBattle, queueEncounterMessage,
|
||||
setCustomEncounterRewards
|
||||
setEncounterRewards
|
||||
} from "./mystery-encounter-utils";
|
||||
import MysteryEncounter, {MysteryEncounterBuilder, MysteryEncounterTier} from "../mystery-encounter";
|
||||
import * as Utils from "../../utils";
|
||||
|
@ -23,7 +23,7 @@ import { BerryType } from "#enums/berry-type";
|
|||
|
||||
export const SleepingSnorlaxEncounter: MysteryEncounter = new MysteryEncounterBuilder()
|
||||
.withEncounterType(MysteryEncounterType.SLEEPING_SNORLAX)
|
||||
.withEncounterTier(MysteryEncounterTier.RARE)
|
||||
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||
.withIntroSpriteConfigs([
|
||||
{
|
||||
spriteKey: Species.SNORLAX.toString(),
|
||||
|
@ -78,7 +78,7 @@ export const SleepingSnorlaxEncounter: MysteryEncounter = new MysteryEncounterBu
|
|||
// const sitrus = (modifierTypes.BERRY?.() as ModifierTypeGenerator).generateType(scene.getParty(), [BerryType.SITRUS]);
|
||||
const sitrus = generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]);
|
||||
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTypeOptions: [new ModifierTypeOption(sitrus, 0)], fillRemaining: false});
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [new ModifierTypeOption(sitrus, 0)], fillRemaining: false});
|
||||
queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_2_bad_result");
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
} else {
|
||||
|
@ -101,7 +101,7 @@ export const SleepingSnorlaxEncounter: MysteryEncounter = new MysteryEncounterBu
|
|||
.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
|
||||
setCustomEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: false});
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.LEFTOVERS], fillRemaining: false});
|
||||
queueEncounterMessage(scene, "mysteryEncounter:sleeping_snorlax_option_3_good_result");
|
||||
leaveEncounterWithoutBattle(scene);
|
||||
})
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import BattleScene from "../../battle-scene";
|
||||
import {
|
||||
EnemyPartyConfig,
|
||||
getTextWithEncounterDialogueTokensAndColor,
|
||||
getEncounterText,
|
||||
initBattleWithEnemyConfig,
|
||||
selectPokemonForOption,
|
||||
setCustomEncounterRewards
|
||||
setEncounterRewards
|
||||
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||
import {MysteryEncounterType} from "#enums/mystery-encounter-type";
|
||||
import MysteryEncounter, {MysteryEncounterBuilder, MysteryEncounterTier} from "../mystery-encounter";
|
||||
|
@ -26,7 +26,7 @@ import {pokemonInfo} from "#app/locales/en/pokemon-info";
|
|||
|
||||
export const TrainingSessionEncounter: MysteryEncounter = new MysteryEncounterBuilder()
|
||||
.withEncounterType(MysteryEncounterType.TRAINING_SESSION)
|
||||
.withEncounterTier(MysteryEncounterTier.RARE)
|
||||
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||
.withIntroSpriteConfigs([
|
||||
{
|
||||
spriteKey: "training_gear",
|
||||
|
@ -128,10 +128,10 @@ export const TrainingSessionEncounter: MysteryEncounter = new MysteryEncounterBu
|
|||
scene.addModifier(mod, true, false, false, true);
|
||||
}
|
||||
scene.updateModifiers(true);
|
||||
scene.queueMessage(getTextWithEncounterDialogueTokensAndColor(scene, "mysteryEncounter:training_session_battle_finished_1"), null, true);
|
||||
scene.queueMessage(getEncounterText(scene, "mysteryEncounter:training_session_battle_finished_1"), null, true);
|
||||
};
|
||||
|
||||
setCustomEncounterRewards(scene, { fillRemaining: true }, null, onBeforeRewardsPhase);
|
||||
setEncounterRewards(scene, { fillRemaining: true }, null, onBeforeRewardsPhase);
|
||||
|
||||
return initBattleWithEnemyConfig(scene, config);
|
||||
})
|
||||
|
@ -174,7 +174,7 @@ export const TrainingSessionEncounter: MysteryEncounter = new MysteryEncounterBu
|
|||
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||
|
||||
const onBeforeRewardsPhase = () => {
|
||||
scene.queueMessage(getTextWithEncounterDialogueTokensAndColor(scene, "mysteryEncounter:training_session_battle_finished_2"), null, true);
|
||||
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);
|
||||
|
@ -187,7 +187,7 @@ export const TrainingSessionEncounter: MysteryEncounter = new MysteryEncounterBu
|
|||
scene.updateModifiers(true);
|
||||
};
|
||||
|
||||
setCustomEncounterRewards(scene, { fillRemaining: true }, null, onBeforeRewardsPhase);
|
||||
setEncounterRewards(scene, { fillRemaining: true }, null, onBeforeRewardsPhase);
|
||||
|
||||
return initBattleWithEnemyConfig(scene, config);
|
||||
})
|
||||
|
@ -237,7 +237,7 @@ export const TrainingSessionEncounter: MysteryEncounter = new MysteryEncounterBu
|
|||
scene.removePokemonFromPlayerParty(playerPokemon, false);
|
||||
|
||||
const onBeforeRewardsPhase = () => {
|
||||
scene.queueMessage(getTextWithEncounterDialogueTokensAndColor(scene, "mysteryEncounter:training_session_battle_finished_3"), null, true);
|
||||
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()) {
|
||||
|
@ -268,7 +268,7 @@ export const TrainingSessionEncounter: MysteryEncounter = new MysteryEncounterBu
|
|||
scene.updateModifiers(true);
|
||||
};
|
||||
|
||||
setCustomEncounterRewards(scene, { fillRemaining: true }, null, onBeforeRewardsPhase);
|
||||
setEncounterRewards(scene, { fillRemaining: true }, null, onBeforeRewardsPhase);
|
||||
|
||||
return initBattleWithEnemyConfig(scene, config);
|
||||
})
|
||||
|
|
|
@ -7,10 +7,10 @@ import {SimpleTranslationEntries} from "#app/interfaces/locales";
|
|||
*
|
||||
* '@ec{<token>}' will auto-inject the matching token value for the specified Encounter
|
||||
*
|
||||
* '@ecCol[<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)
|
||||
*
|
||||
* Any '(+)' or '(-)' type of tooltip will auto-color to green/blue respectively. THIS ONLY OCCURS FOR OPTION TOOLTIPS, NOWHERE ELSE
|
||||
* Other types of '(...)' tooltips will have to specify the text color manually by using '@ecCol[SUMMARY_GREEN]{<text>}' pattern
|
||||
* Other types of '(...)' tooltips will have to specify the text color manually by using '@[SUMMARY_GREEN]{<text>}' pattern
|
||||
*/
|
||||
export const mysteryEncounter: SimpleTranslationEntries = {
|
||||
// DO NOT REMOVE
|
||||
|
@ -22,7 +22,7 @@ export const mysteryEncounter: SimpleTranslationEntries = {
|
|||
"mysterious_chest_description": "A beautifully ornamented chest stands on the ground. There must be something good inside... right?",
|
||||
"mysterious_chest_query": "Will you open it?",
|
||||
"mysterious_chest_option_1_label": "Open it",
|
||||
"mysterious_chest_option_1_tooltip": "@ecCol[SUMMARY_BLUE]{(35%) Something terrible}\n@ecCol[SUMMARY_GREEN]{(40%) Okay Rewards}\n@ecCol[SUMMARY_GREEN]{(20%) Good Rewards}\n@ecCol[SUMMARY_GREEN]{(4%) Great Rewards}\n@ecCol[SUMMARY_GREEN]{(1%) Amazing Rewards}",
|
||||
"mysterious_chest_option_1_tooltip": "@[SUMMARY_BLUE]{(35%) Something terrible}\n@[SUMMARY_GREEN]{(40%) Okay Rewards}\n@[SUMMARY_GREEN]{(20%) Good Rewards}\n@[SUMMARY_GREEN]{(4%) Great Rewards}\n@[SUMMARY_GREEN]{(1%) Amazing Rewards}",
|
||||
"mysterious_chest_option_2_label": "It's too risky, leave",
|
||||
"mysterious_chest_option_2_tooltip": "(-) No Rewards",
|
||||
"mysterious_chest_option_1_selected_message": "You open the chest to find...",
|
||||
|
@ -41,8 +41,8 @@ export const mysteryEncounter: SimpleTranslationEntries = {
|
|||
"fight_or_flight_option_1_label": "Battle the Pokémon",
|
||||
"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_tooltip": "@ecCol[SUMMARY_GREEN]{(35%) Steal Item}\n@ecCol[SUMMARY_BLUE]{(65%) Harder Battle}",
|
||||
"fight_or_flight_option_2_steal_tooltip": "@ecCol[SUMMARY_GREEN]{(?) Use a Pokémon Move}",
|
||||
"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_3_label": "Leave",
|
||||
"fight_or_flight_option_3_tooltip": "(-) No Rewards",
|
||||
"fight_or_flight_option_1_selected_message": "You approach the\nPokémon without fear.",
|
||||
|
@ -167,7 +167,7 @@ export const mysteryEncounter: SimpleTranslationEntries = {
|
|||
"sleeping_snorlax_option_1_label": "Fight it",
|
||||
"sleeping_snorlax_option_1_tooltip": "(-) Fight Sleeping Snorlax",
|
||||
"sleeping_snorlax_option_2_label": "Wait for it to move",
|
||||
"sleeping_snorlax_option_2_tooltip": "@ecCol[SUMMARY_BLUE]{(75%) Wait a short time}\n@ecCol[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_tooltip": "(+) Leftovers",
|
||||
"sleeping_snorlax_option_3_disabled_tooltip": "Your Pokémon need to know certain moves to choose this",
|
||||
|
|
|
@ -67,7 +67,7 @@ import { TrainerType } from "#enums/trainer-type";
|
|||
import { BattlePhase } from "#app/phases/battle-phase";
|
||||
import { MysteryEncounterVariant } from "#app/data/mystery-encounter";
|
||||
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phase";
|
||||
import { getTextWithEncounterDialogueTokensAndColor, handleMysteryEncounterVictory } from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||
import { getEncounterText, handleMysteryEncounterVictory } from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||
|
||||
const { t } = i18next;
|
||||
|
@ -1081,8 +1081,8 @@ export class EncounterPhase extends BattlePhase {
|
|||
const showNextDialogue = () => {
|
||||
const nextAction = i === introDialogue.length - 1 ? doShowEncounterOptions : showNextDialogue;
|
||||
const dialogue = introDialogue[i];
|
||||
const title = getTextWithEncounterDialogueTokensAndColor(this.scene, dialogue.speaker);
|
||||
const text = getTextWithEncounterDialogueTokensAndColor(this.scene, dialogue.text);
|
||||
const title = getEncounterText(this.scene, dialogue.speaker);
|
||||
const text = getEncounterText(this.scene, dialogue.text);
|
||||
if (title) {
|
||||
this.scene.ui.showDialogue(text, title, null, nextAction, 0, i === 0 ? 750 : 0);
|
||||
} else {
|
||||
|
|
|
@ -3,7 +3,7 @@ import BattleScene from "../battle-scene";
|
|||
import { Phase } from "../phase";
|
||||
import { Mode } from "../ui/ui";
|
||||
import {
|
||||
getTextWithEncounterDialogueTokensAndColor
|
||||
getEncounterText
|
||||
} from "../data/mystery-encounters/mystery-encounter-utils";
|
||||
import { CheckSwitchPhase, NewBattlePhase, PostSummonPhase, ReturnPhase, ScanIvsPhase, SummonPhase, ToggleDoublePositionPhase } from "../phases";
|
||||
import MysteryEncounterOption from "../data/mystery-encounter-option";
|
||||
|
@ -89,9 +89,9 @@ export class MysteryEncounterPhase extends Phase {
|
|||
const nextAction = i === selectedDialogue.length - 1 ? endDialogueAndContinueEncounter : showNextDialogue;
|
||||
const dialogue = selectedDialogue[i];
|
||||
let title: string = null;
|
||||
const text: string = getTextWithEncounterDialogueTokensAndColor(this.scene, dialogue.text);
|
||||
const text: string = getEncounterText(this.scene, dialogue.text);
|
||||
if (dialogue.speaker) {
|
||||
title = getTextWithEncounterDialogueTokensAndColor(this.scene, dialogue.speaker);
|
||||
title = getEncounterText(this.scene, dialogue.speaker);
|
||||
}
|
||||
|
||||
if (title) {
|
||||
|
@ -377,6 +377,7 @@ export class MysteryEncounterBattlePhase extends Phase {
|
|||
|
||||
/**
|
||||
* Will handle (in order):
|
||||
* - Any encounter reward logic that is set within MysteryEncounter doEncounterExp
|
||||
* - Any encounter reward logic that is set within MysteryEncounter doEncounterRewards
|
||||
* - Otherwise, can add a no-reward-item shop with only Potions, etc. if addHealPhase is true
|
||||
* - Queuing of the PostMysteryEncounterPhase
|
||||
|
@ -393,6 +394,10 @@ export class MysteryEncounterRewardsPhase extends Phase {
|
|||
super.start();
|
||||
|
||||
this.scene.executeWithSeedOffset(() => {
|
||||
if (this.scene.currentBattle.mysteryEncounter.doEncounterExp) {
|
||||
this.scene.currentBattle.mysteryEncounter.doEncounterExp(this.scene);
|
||||
}
|
||||
|
||||
if (this.scene.currentBattle.mysteryEncounter.doEncounterRewards) {
|
||||
this.scene.currentBattle.mysteryEncounter.doEncounterRewards(this.scene);
|
||||
} else if (this.addHealPhase) {
|
||||
|
@ -451,9 +456,9 @@ export class PostMysteryEncounterPhase extends Phase {
|
|||
const nextAction = i === outroDialogue.length - 1 ? endPhase : showNextDialogue;
|
||||
const dialogue = outroDialogue[i];
|
||||
let title: string = null;
|
||||
const text: string = getTextWithEncounterDialogueTokensAndColor(this.scene, dialogue.text);
|
||||
const text: string = getEncounterText(this.scene, dialogue.text);
|
||||
if (dialogue.speaker) {
|
||||
title = getTextWithEncounterDialogueTokensAndColor(this.scene, dialogue.speaker);
|
||||
title = getEncounterText(this.scene, dialogue.speaker);
|
||||
}
|
||||
|
||||
this.scene.ui.setMode(Mode.MESSAGE);
|
||||
|
|
|
@ -3,7 +3,7 @@ import GameManager from "#app/test/utils/gameManager";
|
|||
import Phaser from "phaser";
|
||||
import {
|
||||
getHighestLevelPlayerPokemon, getLowestLevelPlayerPokemon,
|
||||
getRandomPlayerPokemon, getRandomSpeciesByStarterTier, getTextWithEncounterDialogueTokensAndColor,
|
||||
getRandomPlayerPokemon, getRandomSpeciesByStarterTier, getEncounterText,
|
||||
koPlayerPokemon, queueEncounterMessage, showEncounterDialogue, showEncounterText,
|
||||
} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||
import {initSceneWithoutEncounterPhase} from "#test/utils/gameManagerUtils";
|
||||
|
@ -276,7 +276,7 @@ describe("Mystery Encounter Utils", () => {
|
|||
scene.currentBattle.mysteryEncounter = new MysteryEncounter(null);
|
||||
scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value");
|
||||
|
||||
const result = getTextWithEncounterDialogueTokensAndColor(scene, "mysteryEncounter:unit_test_dialogue");
|
||||
const result = getEncounterText(scene, "mysteryEncounter:unit_test_dialogue");
|
||||
expect(result).toEqual("[color=#f8f8f8][shadow=#6b5a73]valuevalue @ec{testvalue} @ec{test1} value @ec{test\\} @ec{test\\} {test}[/color][/shadow]");
|
||||
});
|
||||
|
||||
|
@ -285,7 +285,7 @@ describe("Mystery Encounter Utils", () => {
|
|||
scene.currentBattle.mysteryEncounter.setDialogueToken("test", "value");
|
||||
scene.currentBattle.mysteryEncounter.setDialogueToken("testvalue", "new");
|
||||
|
||||
const result = getTextWithEncounterDialogueTokensAndColor(scene, "mysteryEncounter:unit_test_dialogue");
|
||||
const result = getEncounterText(scene, "mysteryEncounter:unit_test_dialogue");
|
||||
expect(result).toEqual("[color=#f8f8f8][shadow=#6b5a73]valuevalue new @ec{test1} value @ec{test\\} @ec{test\\} {test}[/color][/shadow]");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -63,7 +63,7 @@ describe("Mystery Encounter Phases", () => {
|
|||
|
||||
expect(game.scene.mysteryEncounterData.encounteredEvents.length).toBeGreaterThan(0);
|
||||
expect(game.scene.mysteryEncounterData.encounteredEvents[0][0]).toEqual(MysteryEncounterType.MYSTERIOUS_CHALLENGERS);
|
||||
expect(game.scene.mysteryEncounterData.encounteredEvents[0][1]).toEqual(MysteryEncounterTier.UNCOMMON);
|
||||
expect(game.scene.mysteryEncounterData.encounteredEvents[0][1]).toEqual(MysteryEncounterTier.GREAT);
|
||||
expect(game.scene.ui.getMode()).toBe(Mode.MYSTERY_ENCOUNTER);
|
||||
});
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import MysteryEncounterOption from "../data/mystery-encounter-option";
|
|||
import * as Utils from "../utils";
|
||||
import {isNullOrUndefined} from "../utils";
|
||||
import {getPokeballAtlasKey} from "../data/pokeball";
|
||||
import {getTextWithEncounterDialogueTokensAndColor} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||
import {getEncounterText} from "#app/data/mystery-encounters/mystery-encounter-utils";
|
||||
|
||||
export default class MysteryEncounterUiHandler extends UiHandler {
|
||||
private cursorContainer: Phaser.GameObjects.Container;
|
||||
|
@ -298,9 +298,9 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
|||
this.filteredEncounterOptions = mysteryEncounter.options;
|
||||
this.optionsMeetsReqs = [];
|
||||
|
||||
const titleText: string = getTextWithEncounterDialogueTokensAndColor(this.scene, mysteryEncounter.dialogue.encounterOptionsDialogue.title, TextStyle.TOOLTIP_TITLE);
|
||||
const descriptionText: string = getTextWithEncounterDialogueTokensAndColor(this.scene, mysteryEncounter.dialogue.encounterOptionsDialogue.description, TextStyle.TOOLTIP_CONTENT);
|
||||
const queryText: string = getTextWithEncounterDialogueTokensAndColor(this.scene, mysteryEncounter.dialogue.encounterOptionsDialogue.query, TextStyle.TOOLTIP_CONTENT);
|
||||
const titleText: string = getEncounterText(this.scene, mysteryEncounter.dialogue.encounterOptionsDialogue.title, TextStyle.TOOLTIP_TITLE);
|
||||
const descriptionText: string = getEncounterText(this.scene, mysteryEncounter.dialogue.encounterOptionsDialogue.description, TextStyle.TOOLTIP_CONTENT);
|
||||
const queryText: string = getEncounterText(this.scene, mysteryEncounter.dialogue.encounterOptionsDialogue.query, TextStyle.TOOLTIP_CONTENT);
|
||||
|
||||
// Clear options container (except cursor)
|
||||
this.optionsContainer.removeAll();
|
||||
|
@ -320,7 +320,7 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
|||
break;
|
||||
}
|
||||
const option = mysteryEncounter.dialogue.encounterOptionsDialogue.options[i];
|
||||
const text = getTextWithEncounterDialogueTokensAndColor(this.scene, option.buttonLabel, option.style ? option.style : TextStyle.WINDOW);
|
||||
const text = getEncounterText(this.scene, option.buttonLabel, option.style ? option.style : TextStyle.WINDOW);
|
||||
if (text) {
|
||||
optionText.setText(text);
|
||||
}
|
||||
|
@ -416,15 +416,15 @@ export default class MysteryEncounterUiHandler extends UiHandler {
|
|||
let text;
|
||||
const option = mysteryEncounter.dialogue.encounterOptionsDialogue.options[cursor];
|
||||
if (!this.optionsMeetsReqs[cursor] && option.disabledTooltip) {
|
||||
text = getTextWithEncounterDialogueTokensAndColor(this.scene, option.disabledTooltip, TextStyle.TOOLTIP_CONTENT);
|
||||
text = getEncounterText(this.scene, option.disabledTooltip, TextStyle.TOOLTIP_CONTENT);
|
||||
} else {
|
||||
text = getTextWithEncounterDialogueTokensAndColor(this.scene, option.buttonTooltip, TextStyle.TOOLTIP_CONTENT);
|
||||
text = getEncounterText(this.scene, option.buttonTooltip, TextStyle.TOOLTIP_CONTENT);
|
||||
}
|
||||
|
||||
// Auto-color options green/blue for good/bad by looking for (+)/(-)
|
||||
const primaryStyleString = [...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))][0];
|
||||
text = text.replace(/(\([^\(]*\+\)[^\(\[]*)/gi, substring => "[/color][/shadow]" + getBBCodeFrag(substring, TextStyle.SUMMARY_GREEN) + "[/color][/shadow]" + primaryStyleString);
|
||||
text = text.replace(/(\([^\(]*\-\)[^\(\[]*)/gi, substring => "[/color][/shadow]" + getBBCodeFrag(substring, TextStyle.SUMMARY_BLUE) + "[/color][/shadow]" + primaryStyleString);
|
||||
text = text.replace(/(\(\+\)[^\(\[]*)/gi, substring => "[/color][/shadow]" + getBBCodeFrag(substring, TextStyle.SUMMARY_GREEN) + "[/color][/shadow]" + primaryStyleString);
|
||||
text = text.replace(/(\(\-\)[^\(\[]*)/gi, substring => "[/color][/shadow]" + getBBCodeFrag(substring, TextStyle.SUMMARY_BLUE) + "[/color][/shadow]" + primaryStyleString);
|
||||
|
||||
if (text) {
|
||||
const tooltipTextObject = addBBCodeTextObject(this.scene, 6, 7, text, TextStyle.TOOLTIP_CONTENT, { wordWrap: { width: 600 }, fontSize: "72px" });
|
||||
|
|
|
@ -171,6 +171,34 @@ export function getBBCodeFrag(content: string, textStyle: TextStyle, uiTheme: Ui
|
|||
return `[color=${getTextColor(textStyle, false, uiTheme)}][shadow=${getTextColor(textStyle, true, uiTheme)}]${content}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should only be used with BBCodeText (see addBBCodeTextObject())
|
||||
* This does NOT work with UI showText() or showDialogue() methods.
|
||||
* Method will do pattern match/replace and apply BBCode color/shadow styling to substrings within the content:
|
||||
* @[<TextStyle>]{<text to color>}
|
||||
*
|
||||
* Example: passing a content string of "@[SUMMARY_BLUE]{blue text} primaryStyle text @[SUMMARY_RED]{red text}" will result in:
|
||||
* - "blue text" with TextStyle.SUMMARY_BLUE applied
|
||||
* - " primaryStyle text " with primaryStyle TextStyle applied
|
||||
* - "red text" with TextStyle.SUMMARY_RED applied
|
||||
* @param content - string with styling that need to be applied for BBCodeTextObject
|
||||
* @param primaryStyle - primary style is required in order to escape BBCode styling properly.
|
||||
* @param uiTheme
|
||||
*/
|
||||
export function getTextWithColors(content: string, primaryStyle: TextStyle, uiTheme: UiTheme = UiTheme.DEFAULT): string {
|
||||
// Apply primary styling before anything else
|
||||
let text = getBBCodeFrag(content, primaryStyle, uiTheme) + "[/color][/shadow]";
|
||||
const primaryStyleString = [...text.match(new RegExp(/\[color=[^\[]*\]\[shadow=[^\[]*\]/i))][0];
|
||||
|
||||
// Set custom colors
|
||||
text = text.replace(/@\[([^{]*)\]{([^}]*)}/gi, (substring, textStyle: string, textToColor: string) => {
|
||||
return "[/color][/shadow]" + getBBCodeFrag(textToColor, TextStyle[textStyle], uiTheme) + "[/color][/shadow]" + primaryStyleString;
|
||||
});
|
||||
|
||||
// Remove extra style block at the end
|
||||
return text.replace(/\[color=[^\[]*\]\[shadow=[^\[]*\]\[\/color\]\[\/shadow\]/gi, "");
|
||||
}
|
||||
|
||||
export function getTextColor(textStyle: TextStyle, shadow?: boolean, uiTheme: UiTheme = UiTheme.DEFAULT): string {
|
||||
switch (textStyle) {
|
||||
case TextStyle.MESSAGE:
|
||||
|
|
Loading…
Reference in New Issue