Merge pull request #4141 from ben-lear/mystery-encounters-feedback

dialogue updates and test fixes for MEs
This commit is contained in:
ImperialSympathizer 2024-09-09 17:06:55 -04:00 committed by GitHub
commit b25a0966ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 156 additions and 106 deletions

View File

@ -256,7 +256,7 @@ export default class BattleScene extends SceneBase {
public money: integer; public money: integer;
public pokemonInfoContainer: PokemonInfoContainer; public pokemonInfoContainer: PokemonInfoContainer;
private party: PlayerPokemon[]; private party: PlayerPokemon[];
public mysteryEncounterSaveData: MysteryEncounterSaveData = new MysteryEncounterSaveData(null); public mysteryEncounterSaveData: MysteryEncounterSaveData = new MysteryEncounterSaveData();
public lastMysteryEncounter?: MysteryEncounter; public lastMysteryEncounter?: MysteryEncounter;
/** Combined Biome and Wave count text */ /** Combined Biome and Wave count text */
private biomeWaveText: Phaser.GameObjects.Text; private biomeWaveText: Phaser.GameObjects.Text;

View File

@ -225,6 +225,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
}; };
encounter.enemyPartyConfigs = [config]; encounter.enemyPartyConfigs = [config];
encounter.setDialogueToken("greedentName", getPokemonSpecies(Species.GREEDENT).getName());
return true; return true;
}) })
@ -251,6 +252,9 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
party.forEach(p => { party.forEach(p => {
if (revSeed) { if (revSeed) {
const seedModifier = revSeed.newModifier(p); const seedModifier = revSeed.newModifier(p);
if (seedModifier) {
encounter.setDialogueToken("foodReward", seedModifier.type.name);
}
scene.addModifier(seedModifier, false, false, false, true); scene.addModifier(seedModifier, false, false, false, true);
} }
}); });

View File

@ -83,6 +83,8 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter =
} }
} }
encounter.setDialogueToken("liepardName", getPokemonSpecies(Species.LIEPARD).getName());
return true; return true;
}) })
.withOption( .withOption(

View File

@ -1,5 +1,5 @@
import { import {
EnemyPartyConfig, EnemyPartyConfig, generateModifierType,
generateModifierTypeOption, generateModifierTypeOption,
initBattleWithEnemyConfig, initBattleWithEnemyConfig,
leaveEncounterWithoutBattle, leaveEncounterWithoutBattle,
@ -223,6 +223,15 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
}, },
]; ];
const requiredItems = [
generateModifierType(scene, modifierTypes.QUICK_CLAW),
generateModifierType(scene, modifierTypes.GRIP_CLAW),
generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.BUG]),
];
const requiredItemString = requiredItems.map(m => m?.name ?? "unknown").join("/");
encounter.setDialogueToken("requiredBugItems", requiredItemString);
return true; return true;
}) })
.withTitle(`${namespace}.title`) .withTitle(`${namespace}.title`)
@ -361,6 +370,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
}) })
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => { .withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Get Pokemon held items and filter for valid ones // Get Pokemon held items and filter for valid ones
const validItems = pokemon.getHeldItems().filter(item => { const validItems = pokemon.getHeldItems().filter(item => {

View File

@ -129,7 +129,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
}, },
{ // Blacephalon has the random ability from pool, and 2 entirely random types to fit with the theme of the encounter { // Blacephalon has the random ability from pool, and 2 entirely random types to fit with the theme of the encounter
species: getPokemonSpecies(Species.BLACEPHALON), species: getPokemonSpecies(Species.BLACEPHALON),
mysteryEncounterPokemonData: new MysteryEncounterPokemonData(undefined, ability, undefined, [randSeedInt(18), randSeedInt(18)]), mysteryEncounterPokemonData: new MysteryEncounterPokemonData({ ability: ability, types: [randSeedInt(18), randSeedInt(18)] }),
isBoss: true, isBoss: true,
moveSet: [Moves.TRICK, Moves.HYPNOSIS, Moves.SHADOW_BALL, Moves.MIND_BLOWN] moveSet: [Moves.TRICK, Moves.HYPNOSIS, Moves.SHADOW_BALL, Moves.MIND_BLOWN]
}, },
@ -140,6 +140,8 @@ export const ClowningAroundEncounter: MysteryEncounter =
// Load animations/sfx for start of fight moves // Load animations/sfx for start of fight moves
loadCustomMovesForEncounter(scene, [Moves.ROLE_PLAY, Moves.TAUNT]); loadCustomMovesForEncounter(scene, [Moves.ROLE_PLAY, Moves.TAUNT]);
encounter.setDialogueToken("blacephalonName", getPokemonSpecies(Species.BLACEPHALON).getName());
return true; return true;
}) })
.withTitle(`${namespace}.title`) .withTitle(`${namespace}.title`)
@ -345,10 +347,9 @@ export const ClowningAroundEncounter: MysteryEncounter =
} }
newTypes.push(secondType); newTypes.push(secondType);
if (!pokemon.mysteryEncounterPokemonData) { if (!pokemon.mysteryEncounterPokemonData) {
pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData(undefined, undefined, undefined, newTypes); pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData();
} else {
pokemon.mysteryEncounterPokemonData.types = newTypes;
} }
pokemon.mysteryEncounterPokemonData.types = newTypes;
} }
}) })
.withOptionPhase(async (scene: BattleScene) => { .withOptionPhase(async (scene: BattleScene) => {
@ -410,11 +411,12 @@ function displayYesNoOptions(scene: BattleScene, resolve) {
function onYesAbilitySwap(scene: BattleScene, resolve) { function onYesAbilitySwap(scene: BattleScene, resolve) {
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Do ability swap // Do ability swap
const encounter = scene.currentBattle.mysteryEncounter!;
if (!pokemon.mysteryEncounterPokemonData) { if (!pokemon.mysteryEncounterPokemonData) {
pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData(undefined, Abilities.AERILATE); pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData();
} }
pokemon.mysteryEncounterPokemonData.ability = scene.currentBattle.mysteryEncounter!.misc.ability; pokemon.mysteryEncounterPokemonData.ability = encounter.misc.ability;
scene.currentBattle.mysteryEncounter!.setDialogueToken("chosenPokemon", pokemon.getNameToRender()); encounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender());
scene.ui.setMode(Mode.MESSAGE).then(() => resolve(true)); scene.ui.setMode(Mode.MESSAGE).then(() => resolve(true));
}; };

View File

@ -162,6 +162,8 @@ export const DancingLessonsEncounter: MysteryEncounter =
oricorioData oricorioData
}; };
encounter.setDialogueToken("oricorioName", getPokemonSpecies(Species.ORICORIO).getName());
return true; return true;
}) })
.withOption( .withOption(

View File

@ -15,6 +15,7 @@ import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import i18next from "#app/plugins/i18n"; import i18next from "#app/plugins/i18n";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { getPokemonSpecies } from "#app/data/pokemon-species";
/** the i18n namespace for this encounter */ /** the i18n namespace for this encounter */
const namespace = "mysteryEncounter:delibirdy"; const namespace = "mysteryEncounter:delibirdy";
@ -88,6 +89,11 @@ export const DelibirdyEncounter: MysteryEncounter =
text: `${namespace}.outro`, text: `${namespace}.outro`,
} }
]) ])
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("delibirdName", getPokemonSpecies(Species.DELIBIRD).getName());
return true;
})
.withOption( .withOption(
MysteryEncounterOptionBuilder MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)

View File

@ -100,6 +100,8 @@ export const FieryFalloutEncounter: MysteryEncounter =
scene.arena.trySetWeather(WeatherType.SUNNY, true); scene.arena.trySetWeather(WeatherType.SUNNY, true);
encounter.setDialogueToken("volcaronaName", getPokemonSpecies(Species.VOLCARONA).getName());
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart((scene: BattleScene) => {

View File

@ -77,7 +77,9 @@ export const FunAndGamesEncounter: MysteryEncounter =
.withDescription(`${namespace}.description`) .withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`) .withQuery(`${namespace}.query`)
.withOnInit((scene: BattleScene) => { .withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
scene.loadBgm("mystery_encounter_fun_and_games", "mystery_encounter_fun_and_games.mp3"); scene.loadBgm("mystery_encounter_fun_and_games", "mystery_encounter_fun_and_games.mp3");
encounter.setDialogueToken("wobbuffetName", getPokemonSpecies(Species.WOBBUFFET).getName());
return true; return true;
}) })
.withOnVisualsStart((scene: BattleScene) => { .withOnVisualsStart((scene: BattleScene) => {

View File

@ -215,7 +215,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;
const onPokemonSelected = (pokemon: PlayerPokemon) => { const onPokemonSelected = (pokemon: PlayerPokemon) => {
// Randomly generate a Wonder Trade pokemon // Randomly generate a Wonder Trade pokemon
const randomTradeOption = generateTradeOption(scene.getParty().map(p => p.species)); // const randomTradeOption = generateTradeOption(scene.getParty().map(p => p.species));
const randomTradeOption = getPokemonSpecies(Species.BURMY);
const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false); const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
// Extra shiny roll at 1/128 odds (boosted by events and charms) // Extra shiny roll at 1/128 odds (boosted by events and charms)
if (!tradePokemon.shiny) { if (!tradePokemon.shiny) {
@ -265,7 +266,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
receivedPokemonData.passive = tradedPokemon.passive; receivedPokemonData.passive = tradedPokemon.passive;
receivedPokemonData.pokeball = randSeedInt(5); receivedPokemonData.pokeball = randSeedInt(5);
const dataSource = new PokemonData(receivedPokemonData); const dataSource = new PokemonData(receivedPokemonData);
const newPlayerPokemon = scene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, undefined, undefined, undefined, undefined, undefined, undefined, undefined, dataSource); const newPlayerPokemon = scene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, dataSource.abilityIndex, dataSource.formIndex, dataSource.gender, dataSource.shiny, dataSource.variant, dataSource.ivs, dataSource.nature, dataSource);
scene.getParty().push(newPlayerPokemon); scene.getParty().push(newPlayerPokemon);
await newPlayerPokemon.loadAssets(); await newPlayerPokemon.loadAssets();

View File

@ -1,5 +1,5 @@
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { ModifierTier } from "#app/modifier/modifier-tier"; import { ModifierTier } from "#app/modifier/modifier-tier";
import { randSeedInt } from "#app/utils.js"; import { randSeedInt } from "#app/utils.js";
@ -60,7 +60,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
// Calculate boss mon // Calculate boss mon
const config: EnemyPartyConfig = { const config: EnemyPartyConfig = {
levelAdditiveMultiplier: 1, levelAdditiveMultiplier: 0.5,
disableSwitch: true, disableSwitch: true,
pokemonConfigs: [ pokemonConfigs: [
{ {
@ -74,7 +74,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
encounter.enemyPartyConfigs = [config]; encounter.enemyPartyConfigs = [config];
loadCustomMovesForEncounter(scene, [Moves.CONFUSE_RAY, Moves.ASTONISH]); encounter.setDialogueToken("gimmighoulName", getPokemonSpecies(Species.GIMMIGHOUL).getName());
return true; return true;
}) })

View File

@ -68,6 +68,8 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter =
// Load animations/sfx for Snorlax fight start moves // Load animations/sfx for Snorlax fight start moves
loadCustomMovesForEncounter(scene, [Moves.SNORE]); loadCustomMovesForEncounter(scene, [Moves.SNORE]);
encounter.setDialogueToken("snorlaxName", getPokemonSpecies(Species.SNORLAX).getName());
return true; return true;
}) })
.withTitle(`${namespace}.title`) .withTitle(`${namespace}.title`)

View File

@ -76,7 +76,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
species: getPokemonSpecies(Species.SHUCKLE), species: getPokemonSpecies(Species.SHUCKLE),
isBoss: true, isBoss: true,
bossSegments: 5, bossSegments: 5,
mysteryEncounterPokemonData: new MysteryEncounterPokemonData(1.25), mysteryEncounterPokemonData: new MysteryEncounterPokemonData({ spriteScale: 1.25 }),
nature: Nature.BOLD, nature: Nature.BOLD,
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER], moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
modifierConfigs: [ modifierConfigs: [
@ -110,6 +110,8 @@ export const TheStrongStuffEncounter: MysteryEncounter =
loadCustomMovesForEncounter(scene, [Moves.GASTRO_ACID, Moves.STEALTH_ROCK]); loadCustomMovesForEncounter(scene, [Moves.GASTRO_ACID, Moves.STEALTH_ROCK]);
encounter.setDialogueToken("shuckleName", getPokemonSpecies(Species.SHUCKLE).getName());
return true; return true;
}) })
.withTitle(`${namespace}.title`) .withTitle(`${namespace}.title`)

View File

@ -77,6 +77,7 @@ export const TrashToTreasureEncounter: MysteryEncounter =
scene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav"); scene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav");
scene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav"); scene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav");
return true; return true;
}) })
.withOption( .withOption(

View File

@ -174,16 +174,17 @@ export const UncommonBreedEncounter: MysteryEncounter =
// Remove 4 random berries from player's party // Remove 4 random berries from player's party
// Get all player berry items, remove from party, and store reference // Get all player berry items, remove from party, and store reference
let berryItems: BerryModifier[] = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const berryItems: BerryModifier[]= scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
berryItems = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const index = randSeedInt(berryItems.length);
const randBerry = berryItems[randSeedInt(berryItems.length)]; const randBerry = berryItems[index];
randBerry.stackCount--; randBerry.stackCount--;
if (randBerry.stackCount === 0) { if (randBerry.stackCount === 0) {
scene.removeModifier(randBerry); scene.removeModifier(randBerry);
berryItems.splice(index, 1);
}
} }
scene.updateModifiers(true, true); scene.updateModifiers(true, true);
}
// Pokemon joins the team, with 2 egg moves // Pokemon joins the team, with 2 egg moves
const encounter = scene.currentBattle.mysteryEncounter!; const encounter = scene.currentBattle.mysteryEncounter!;

View File

@ -370,10 +370,9 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
} }
newTypes.push(newType); newTypes.push(newType);
if (!newPokemon.mysteryEncounterPokemonData) { if (!newPokemon.mysteryEncounterPokemonData) {
newPokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData(undefined, undefined, undefined, newTypes); newPokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData();
} else {
newPokemon.mysteryEncounterPokemonData.types = newTypes;
} }
newPokemon.mysteryEncounterPokemonData.types = newTypes;
for (const item of transformation.heldItems) { for (const item of transformation.heldItems) {
item.pokemonId = newPokemon.id; item.pokemonId = newPokemon.id;

View File

@ -1,5 +1,6 @@
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { Type } from "#app/data/type"; import { Type } from "#app/data/type";
import { isNullOrUndefined } from "#app/utils";
export class MysteryEncounterPokemonData { export class MysteryEncounterPokemonData {
public spriteScale: number; public spriteScale: number;
@ -7,10 +8,14 @@ export class MysteryEncounterPokemonData {
public passive: Abilities | -1; public passive: Abilities | -1;
public types: Type[]; public types: Type[];
constructor(spriteScale?: number, ability?: Abilities, passive?: Abilities, types?: Type[]) { constructor(data?: MysteryEncounterPokemonData | Partial<MysteryEncounterPokemonData>) {
this.spriteScale = spriteScale ?? -1; if (!isNullOrUndefined(data)) {
this.ability = ability ?? -1; Object.assign(this, data);
this.passive = passive ?? -1; }
this.types = types ?? [];
this.spriteScale = this.spriteScale ?? -1;
this.ability = this.ability ?? -1;
this.passive = this.passive ?? -1;
this.types = this.types ?? [];
} }
} }

View File

@ -27,9 +27,12 @@ export class MysteryEncounterSaveData {
encounterSpawnChance: number = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT; encounterSpawnChance: number = BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT;
queuedEncounters: QueuedEncounter[] = []; queuedEncounters: QueuedEncounter[] = [];
constructor(data: MysteryEncounterSaveData | null) { constructor(data?: MysteryEncounterSaveData) {
if (!isNullOrUndefined(data)) { if (!isNullOrUndefined(data)) {
Object.assign(this, data); Object.assign(this, data);
} }
this.encounteredEvents = this.encounteredEvents ?? [];
this.queuedEncounters = this.queuedEncounters ?? [];
} }
} }

View File

@ -5,7 +5,7 @@ import { variantData } from "#app/data/variant";
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info"; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info";
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr } from "../data/move"; import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr } from "../data/move";
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species"; import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species";
import { Constructor, randSeedInt } from "#app/utils"; import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type"; import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type";
import { getLevelTotalExp } from "../data/exp"; import { getLevelTotalExp } from "../data/exp";
@ -202,7 +202,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.fusionGender = dataSource.fusionGender; this.fusionGender = dataSource.fusionGender;
this.fusionLuck = dataSource.fusionLuck; this.fusionLuck = dataSource.fusionLuck;
this.usedTMs = dataSource.usedTMs ?? []; this.usedTMs = dataSource.usedTMs ?? [];
this.mysteryEncounterPokemonData = dataSource.mysteryEncounterPokemonData ?? new MysteryEncounterPokemonData(); this.mysteryEncounterPokemonData = new MysteryEncounterPokemonData(dataSource.mysteryEncounterPokemonData);
} else { } else {
this.id = Utils.randSeedInt(4294967296); this.id = Utils.randSeedInt(4294967296);
this.ivs = ivs || Utils.getIvsFromId(this.id); this.ivs = ivs || Utils.getIvsFromId(this.id);
@ -548,12 +548,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (!ignoreOverride && this.summonData?.speciesForm) { if (!ignoreOverride && this.summonData?.speciesForm) {
return this.summonData.speciesForm; return this.summonData.speciesForm;
} }
if (!this.species.forms?.length) { if (this.species.forms && this.species.forms.length > 0) {
return this.species;
}
return this.species.forms[this.formIndex]; return this.species.forms[this.formIndex];
} }
return this.species;
}
getFusionSpeciesForm(ignoreOverride?: boolean): PokemonSpeciesForm { getFusionSpeciesForm(ignoreOverride?: boolean): PokemonSpeciesForm {
if (!ignoreOverride && this.summonData?.speciesForm) { if (!ignoreOverride && this.summonData?.speciesForm) {
return this.summonData.fusionSpeciesForm; return this.summonData.fusionSpeciesForm;
@ -1151,7 +1152,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (Overrides.OPP_ABILITY_OVERRIDE && !this.isPlayer()) { if (Overrides.OPP_ABILITY_OVERRIDE && !this.isPlayer()) {
return allAbilities[Overrides.OPP_ABILITY_OVERRIDE]; return allAbilities[Overrides.OPP_ABILITY_OVERRIDE];
} }
if (this.mysteryEncounterPokemonData.ability !== -1) { if (!isNullOrUndefined(this.mysteryEncounterPokemonData.ability) && this.mysteryEncounterPokemonData.ability !== -1) {
return allAbilities[this.mysteryEncounterPokemonData.ability]; return allAbilities[this.mysteryEncounterPokemonData.ability];
} }
if (this.isFusion()) { if (this.isFusion()) {
@ -1178,7 +1179,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE && !this.isPlayer()) { if (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE && !this.isPlayer()) {
return allAbilities[Overrides.OPP_PASSIVE_ABILITY_OVERRIDE]; return allAbilities[Overrides.OPP_PASSIVE_ABILITY_OVERRIDE];
} }
if (this.mysteryEncounterPokemonData.passive !== -1) { if (!isNullOrUndefined(this.mysteryEncounterPokemonData.passive) && this.mysteryEncounterPokemonData.passive !== -1) {
return allAbilities[this.mysteryEncounterPokemonData.passive]; return allAbilities[this.mysteryEncounterPokemonData.passive];
} }

View File

@ -1,25 +1,25 @@
{ {
"intro": "A Greedent ambushes you\nand steals your party's berries!", "intro": "A {{greedentName}} ambushes you\nand steals your party's berries!",
"title": "Absolute Avarice", "title": "Absolute Avarice",
"description": "The Greedent has caught you totally off guard now all your berries are gone!\n\nThe Greedent looks like it's about to eat them when it pauses to look at you, interested.", "description": "The {{greedentName}} has caught you totally off guard now all your berries are gone!\n\nThe {{greedentName}} looks like it's about to eat them when it pauses to look at you, interested.",
"query": "What will you do?", "query": "What will you do?",
"option": { "option": {
"1": { "1": {
"label": "Battle It", "label": "Battle It",
"tooltip": "(-) Tough Battle\n(+) Rewards from its Berry Hoard", "tooltip": "(-) Tough Battle\n(+) Rewards from its Berry Hoard",
"selected": "The Greedent stuffs its cheeks\nand prepares for battle!", "selected": "The {{greedentName}} stuffs its cheeks\nand prepares for battle!",
"boss_enraged": "Greedent's fierce love for food has it incensed!", "boss_enraged": "{{greedentName}}'s fierce love for food has it incensed!",
"food_stash": "It looks like the Greedent was guarding an enormous stash of food!$@s{item_fanfare}Each Pokémon in your party gains 1x Reviver Seed!" "food_stash": "It looks like the {{greedentName}} was guarding an enormous stash of food!$@s{item_fanfare}Each Pokémon in your party gains a {{foodReward}}!"
}, },
"2": { "2": {
"label": "Reason with It", "label": "Reason with It",
"tooltip": "(+) Regain Some Lost Berries", "tooltip": "(+) Regain Some Lost Berries",
"selected": "Your pleading strikes a chord with the Greedent.$It doesn't give all your berries back, but still tosses a few in your direction." "selected": "Your pleading strikes a chord with the {{greedentName}}.$It doesn't give all your berries back, but still tosses a few in your direction."
}, },
"3": { "3": {
"label": "Let It Have the Food", "label": "Let It Have the Food",
"tooltip": "(-) Lose All Berries\n(?) The Greedent Will Like You", "tooltip": "(-) Lose All Berries\n(?) The {{greedentName}} Will Like You",
"selected": "The Greedent devours the entire\nstash of berries in a flash!$Patting its stomach,\nit looks at you appreciatively.$Perhaps you could feed it\nmore berries on your adventure...$@s{level_up_fanfare}The Greedent wants to join your party!" "selected": "The {{greedentName}} devours the entire\nstash of berries in a flash!$Patting its stomach,\nit looks at you appreciatively.$Perhaps you could feed it\nmore berries on your adventure...$@s{level_up_fanfare}The {{greedentName}} wants to join your party!"
} }
} }
} }

View File

@ -15,12 +15,12 @@
"label": "Extort the Kid", "label": "Extort the Kid",
"tooltip": "(+) {{option2PrimaryName}} uses {{moveOrAbility}}\n(+) Gain {{price, money}}", "tooltip": "(+) {{option2PrimaryName}} uses {{moveOrAbility}}\n(+) Gain {{price, money}}",
"tooltip_disabled": "Your Pokémon need to have certain moves or abilities to choose this", "tooltip_disabled": "Your Pokémon need to have certain moves or abilities to choose this",
"selected": "My word, we're being robbed, Liepard!\n $You'll be hearing from my lawyers for this!" "selected": "My word, we're being robbed, {{liepardName}}!$You'll be hearing from my lawyers for this!"
}, },
"3": { "3": {
"label": "Leave", "label": "Leave",
"tooltip": "(-) No Rewards", "tooltip": "(-) No Rewards",
"selected": "What a rotten day...$Ah, well. Let's return to the yacht club then, Liepard." "selected": "What a rotten day...$Ah, well. Let's return to the yacht club then, {{liepardName}}."
} }
} }
} }

View File

@ -1,7 +1,7 @@
{ {
"intro": "There's a huge berry bush\nnear that Pokémon!", "intro": "There's a huge berry bush\nnear that Pokémon!",
"title": "Berries Abound", "title": "Berries Abound",
"description": "It looks like there's a strong Pokémon guarding a berry bush. Battling is the straightforward approach, but this Pokémon looks strong. Maybe a fast Pokémon would be able to grab some without getting caught?", "description": "It looks like there's a strong Pokémon guarding a berry bush. Battling is the straightforward approach, but it looks strong. Perhaps a fast Pokémon could grab some berries without getting caught?",
"query": "What will you do?", "query": "What will you do?",
"berries": "Berries!", "berries": "Berries!",
"option": { "option": {

View File

@ -23,8 +23,8 @@
}, },
"3": { "3": {
"label": "Gift a Bug Item", "label": "Gift a Bug Item",
"tooltip": "(-) Give the trainer a Quick Claw, Grip Claw, or Silver Powder\n(+) Receive a Gift Item", "tooltip": "(-) Give the trainer a {{requiredBugItems}}\n(+) Receive a Gift Item",
"disabled_tooltip": "You need to have a Quick Claw, Grip Claw, or Silver Powder to select this.", "disabled_tooltip": "You need to have a {{requiredBugItems}} to select this.",
"select_prompt": "Select an item to give.", "select_prompt": "Select an item to give.",
"invalid_selection": "Pokémon doesn't have that kind of item.", "invalid_selection": "Pokémon doesn't have that kind of item.",
"selected": "You hand the trainer a {{selectedItem}}.", "selected": "You hand the trainer a {{selectedItem}}.",

View File

@ -3,7 +3,7 @@
"speaker": "Clown", "speaker": "Clown",
"intro_dialogue": "Bumbling buffoon, brace for a brilliant battle!\nYou'll be beaten by this brawling busker!", "intro_dialogue": "Bumbling buffoon, brace for a brilliant battle!\nYou'll be beaten by this brawling busker!",
"title": "Clowning Around", "title": "Clowning Around",
"description": "Something is off about this encounter. The clown seems eager to goad you into a battle, but to what end?\n\nThe Blacephalon is especially strange, like it has @[TOOLTIP_TITLE]{weird types and ability.}", "description": "Something is off about this encounter. The clown seems eager to goad you into a battle, but to what end?\n\nThe {{blacephalonName}} is especially strange, like it has @[TOOLTIP_TITLE]{weird types and ability.}",
"query": "What will you do?", "query": "What will you do?",
"option": { "option": {
"1": { "1": {
@ -19,14 +19,14 @@
"label": "Remain Unprovoked", "label": "Remain Unprovoked",
"tooltip": "(-) Upsets the Clown\n(?) Affects Pokémon Items", "tooltip": "(-) Upsets the Clown\n(?) Affects Pokémon Items",
"selected": "Dismal dodger, you deny a delightful duel?\nFeel my fury!", "selected": "Dismal dodger, you deny a delightful duel?\nFeel my fury!",
"selected_2": "The clown's Blacephalon uses Trick!\nAll of your {{switchPokemon}}'s items were randomly swapped!", "selected_2": "The clown's {{blacephalonName}} uses Trick!\nAll of your {{switchPokemon}}'s items were randomly swapped!",
"selected_3": "Flustered fool, fall for my flawless deception!" "selected_3": "Flustered fool, fall for my flawless deception!"
}, },
"3": { "3": {
"label": "Return the Insults", "label": "Return the Insults",
"tooltip": "(-) Upsets the Clown\n(?) Affects Pokémon Types", "tooltip": "(-) Upsets the Clown\n(?) Affects Pokémon Types",
"selected": "Dismal dodger, you deny a delightful duel?\nFeel my fury!", "selected": "Dismal dodger, you deny a delightful duel?\nFeel my fury!",
"selected_2": "The clown's Blacephalon uses a strange move!\nAll of your team's types were randomly swapped!", "selected_2": "The clown's {{blacephalonName}} uses a strange move!\nAll of your team's types were randomly swapped!",
"selected_3": "Flustered fool, fall for my flawless deception!" "selected_3": "Flustered fool, fall for my flawless deception!"
} }
}, },

View File

@ -1,26 +1,26 @@
{ {
"intro": "An Oricorio dances sadly alone, without a partner.", "intro": "An {{oricorioName}} dances sadly alone, without a partner.",
"title": "Dancing Lessons", "title": "Dancing Lessons",
"description": "The Oricorio doesn't seem aggressive, if anything it seems sad.\n\nMaybe it just wants someone to dance with...", "description": "The {{oricorioName}} doesn't seem aggressive, if anything it seems sad.\n\nMaybe it just wants someone to dance with...",
"query": "What will you do?", "query": "What will you do?",
"option": { "option": {
"1": { "1": {
"label": "Battle It", "label": "Battle It",
"tooltip": "(-) Tough Battle\n(+) Gain a Baton", "tooltip": "(-) Tough Battle\n(+) Gain a Baton",
"selected": "The Oricorio is distraught and moves to defend itself!", "selected": "The {{oricorioName}} is distraught and moves to defend itself!",
"boss_enraged": "The Oricorio's fear boosted its stats!" "boss_enraged": "The {{oricorioName}}'s fear boosted its stats!"
}, },
"2": { "2": {
"label": "Learn Its Dance", "label": "Learn Its Dance",
"tooltip": "(+) Teach a Pokémon Revelation Dance", "tooltip": "(+) Teach a Pokémon Revelation Dance",
"selected": "You watch the Oricorio closely as it performs its dance...$@s{level_up_fanfare}Your {{selectedPokemon}} learned from the Oricorio!" "selected": "You watch the {{oricorioName}} closely as it performs its dance...$@s{level_up_fanfare}Your {{selectedPokemon}} learned from the {{oricorioName}}!"
}, },
"3": { "3": {
"label": "Show It a Dance", "label": "Show It a Dance",
"tooltip": "(-) Teach the Oricorio a Dance Move\n(+) The Oricorio Will Like You", "tooltip": "(-) Teach the {{oricorioName}} a Dance Move\n(+) The {{oricorioName}} Will Like You",
"disabled_tooltip": "Your Pokémon need to know a Dance move for this.", "disabled_tooltip": "Your Pokémon need to know a Dance move for this.",
"select_prompt": "Select a Dance type move to use.", "select_prompt": "Select a Dance type move to use.",
"selected": "The Oricorio watches in fascination as\n{{selectedPokemon}} shows off {{selectedMove}}!$It loves the display!$@s{level_up_fanfare}The Oricorio wants to join your party!" "selected": "The {{oricorioName}} watches in fascination as\n{{selectedPokemon}} shows off {{selectedMove}}!$It loves the display!$@s{level_up_fanfare}The {{oricorioName}} wants to join your party!"
} }
}, },
"invalid_selection": "This Pokémon doesn't know a Dance move" "invalid_selection": "This Pokémon doesn't know a Dance move"

View File

@ -1,29 +1,29 @@
{ {
"intro": "A pack of Delibird have appeared!", "intro": "A pack of {{delibirdName}} have appeared!",
"title": "Delibir-dy", "title": "Delibir-dy",
"description": "The Delibirds are looking at you expectantly, as if they want something. Perhaps giving them an item or some money would satisfy them?", "description": "The {{delibirdName}}s are looking at you expectantly, as if they want something. Perhaps giving them an item or some money would satisfy them?",
"query": "What will you give them?", "query": "What will you give them?",
"invalid_selection": "Pokémon doesn't have that kind of item.", "invalid_selection": "Pokémon doesn't have that kind of item.",
"option": { "option": {
"1": { "1": {
"label": "Give Money", "label": "Give Money",
"tooltip": "(-) Give the Delibirds {{money, money}}\n(+) Receive a Gift Item", "tooltip": "(-) Give the {{delibirdName}}s {{money, money}}\n(+) Receive a Gift Item",
"selected": "You toss the money to the Delibirds,\nwho chatter amongst themselves excitedly.$They turn back to you and happily give you a present!" "selected": "You toss the money to the {{delibirdName}}s,\nwho chatter amongst themselves excitedly.$They turn back to you and happily give you a present!"
}, },
"2": { "2": {
"label": "Give Food", "label": "Give Food",
"tooltip": "(-) Give the Delibirds a Berry or Reviver Seed\n(+) Receive a Gift Item", "tooltip": "(-) Give the {{delibirdName}}s a Berry or Reviver Seed\n(+) Receive a Gift Item",
"select_prompt": "Select an item to give.", "select_prompt": "Select an item to give.",
"selected": "You toss the {{chosenItem}} to the Delibirds,\nwho chatter amongst themselves excitedly.$They turn back to you and happily give you a present!" "selected": "You toss the {{chosenItem}} to the {{delibirdName}}s,\nwho chatter amongst themselves excitedly.$They turn back to you and happily give you a present!"
}, },
"3": { "3": {
"label": "Give an Item", "label": "Give an Item",
"tooltip": "(-) Give the Delibirds a Held Item\n(+) Receive a Gift Item", "tooltip": "(-) Give the {{delibirdName}}s a Held Item\n(+) Receive a Gift Item",
"select_prompt": "Select an item to give.", "select_prompt": "Select an item to give.",
"selected": "You toss the {{chosenItem}} to the Delibirds,\nwho chatter amongst themselves excitedly.$They turn back to you and happily give you a present!" "selected": "You toss the {{chosenItem}} to the {{delibirdName}}s,\nwho chatter amongst themselves excitedly.$They turn back to you and happily give you a present!"
} }
}, },
"outro": "The Delibird pack happily waddles off into the distance.$What a curious little exchange!" "outro": "The {{delibirdName}} pack happily waddles off into the distance.$What a curious little exchange!"
} }

View File

@ -7,7 +7,7 @@
"1": { "1": {
"label": "Find the Source", "label": "Find the Source",
"tooltip": "(?) Discover the source\n(-) Hard Battle", "tooltip": "(?) Discover the source\n(-) Hard Battle",
"selected": "You push through the storm, and find two Volcarona in the middle of a mating dance!$They don't take kindly to the interruption and attack!" "selected": "You push through the storm, and find two {{volcaronaName}}s in the middle of a mating dance!$They don't take kindly to the interruption and attack!"
}, },
"2": { "2": {
"label": "Hunker Down", "label": "Hunker Down",
@ -19,7 +19,7 @@
"label": "Your Fire Types Help", "label": "Your Fire Types Help",
"tooltip": "(+) End the conditions\n(+) Gain a Charcoal", "tooltip": "(+) End the conditions\n(+) Gain a Charcoal",
"disabled_tooltip": "You need at least 2 Fire Type Pokémon to choose this", "disabled_tooltip": "You need at least 2 Fire Type Pokémon to choose this",
"selected": "Your {{option3PrimaryName}} and {{option3SecondaryName}} guide you to where two Volcarona are in the middle of a mating dance!$Thankfully, your Pokémon are able to calm them,\nand they depart without issue." "selected": "Your {{option3PrimaryName}} and {{option3SecondaryName}} guide you to where two {{volcaronaName}}s are in the middle of a mating dance!$Thankfully, your Pokémon are able to calm them,\nand they depart without issue."
} }
}, },
"found_charcoal": "After the weather clears,\nyour {{leadPokemon}} spots something on the ground.$@s{item_fanfare}{{leadPokemon}} gained a Charcoal!" "found_charcoal": "After the weather clears,\nyour {{leadPokemon}} spots something on the ground.$@s{item_fanfare}{{leadPokemon}} gained a Charcoal!"

View File

@ -1,7 +1,7 @@
{ {
"intro": "Something shiny is sparkling\non the ground near that Pokémon!", "intro": "Something shiny is sparkling\non the ground near that Pokémon!",
"title": "Fight or Flight", "title": "Fight or Flight",
"description": "It looks like there's a strong Pokémon guarding an item. Battling is the straightforward approach, but this Pokémon looks strong. You could also try to sneak around, though the Pokémon might catch you.", "description": "It looks like there's a strong Pokémon guarding an item. Battling is the straightforward approach, but it looks strong. Perhaps you could steal the item, if you have the right Pokémon for the job.",
"query": "What will you do?", "query": "What will you do?",
"option": { "option": {
"1": { "1": {

View File

@ -1,13 +1,13 @@
{ {
"intro_dialogue": "Step right up, folks! Try your luck\non the brand new Wobbuffet Whack-o-matic!", "intro_dialogue": "Step right up, folks! Try your luck\non the brand new {{wobbuffetName}} Whack-o-matic!",
"speaker": "Showman", "speaker": "Showman",
"title": "Fun And Games!", "title": "Fun And Games!",
"description": "You've encountered a traveling show with a prize game! You will have @[TOOLTIP_TITLE]{3 turns} to bring the Wobbuffet as close to @[TOOLTIP_TITLE]{1 HP} as possible @[TOOLTIP_TITLE]{without KOing it} so it can wind up a huge Counter on the bell-ringing machine.\nBut be careful! If you KO the Wobbuffet, you'll have to pay for the cost of reviving it!", "description": "You've encountered a traveling show with a prize game! You will have @[TOOLTIP_TITLE]{3 turns} to bring the {{wobbuffetName}} as close to @[TOOLTIP_TITLE]{1 HP} as possible @[TOOLTIP_TITLE]{without KOing it} so it can wind up a huge Counter on the bell-ringing machine.\nBut be careful! If you KO the {{wobbuffetName}}, you'll have to pay for the cost of reviving it!",
"query": "Would you like to play?", "query": "Would you like to play?",
"option": { "option": {
"1": { "1": {
"label": "Play the Game", "label": "Play the Game",
"tooltip": "(-) Pay {{option1Money, money}}\n(+) Play Wobbuffet Whack-o-matic", "tooltip": "(-) Pay {{option1Money, money}}\n(+) Play {{wobbuffetName}} Whack-o-matic",
"selected": "Time to test your luck!" "selected": "Time to test your luck!"
}, },
"2": { "2": {
@ -16,15 +16,15 @@
"selected": "You hurry along your way,\nwith a slight feeling of regret." "selected": "You hurry along your way,\nwith a slight feeling of regret."
} }
}, },
"ko": "Oh no! The Wobbuffet fainted!$You lose the game and\nhave to pay for the revive cost...", "ko": "Oh no! The {{wobbuffetName}} fainted!$You lose the game and\nhave to pay for the revive cost...",
"charging_continue": "The Wubboffet keeps charging its counter-swing!", "charging_continue": "The Wubboffet keeps charging its counter-swing!",
"turn_remaining_3": "Three turns remaining!", "turn_remaining_3": "Three turns remaining!",
"turn_remaining_2": "Two turns remaining!", "turn_remaining_2": "Two turns remaining!",
"turn_remaining_1": "One turn remaining!", "turn_remaining_1": "One turn remaining!",
"end_game": "Time's up!$The Wobbuffet winds up to counter-swing and@d{16}.@d{16}.@d{16}.", "end_game": "Time's up!$The {{wobbuffetName}} winds up to counter-swing and@d{16}.@d{16}.@d{16}.",
"best_result": "The Wobbuffet smacks the button so hard\nthe bell breaks off the top!$You win the grand prize!", "best_result": "The {{wobbuffetName}} smacks the button so hard\nthe bell breaks off the top!$You win the grand prize!",
"great_result": "The Wobbuffet smacks the button, nearly hitting the bell!$So close!\nYou earn the second tier prize!", "great_result": "The {{wobbuffetName}} smacks the button, nearly hitting the bell!$So close!\nYou earn the second tier prize!",
"good_result": "The Wobbuffet hits the button hard enough to go midway up the scale!$You earn the third tier prize!", "good_result": "The {{wobbuffetName}} hits the button hard enough to go midway up the scale!$You earn the third tier prize!",
"bad_result": "The Wobbuffet barely taps the button and nothing happens...$Oh no!\nYou don't win anything!", "bad_result": "The {{wobbuffetName}} barely taps the button and nothing happens...$Oh no!\nYou don't win anything!",
"outro": "That was a fun little game!" "outro": "That was a fun little game!"
} }

View File

@ -12,7 +12,7 @@
"good": "Some pretty nice tools and items.", "good": "Some pretty nice tools and items.",
"great": "A couple great tools and items!", "great": "A couple great tools and items!",
"amazing": "Whoa! An amazing item!", "amazing": "Whoa! An amazing item!",
"bad": "Oh no!@d{32}\nThe chest was actually a Gimmighoul in disguise!$Your {{pokeName}} jumps in front of you\nbut is KOed in the process!" "bad": "Oh no!@d{32}\nThe chest was actually a {{gimmighoulName}} in disguise!$Your {{pokeName}} jumps in front of you\nbut is KOed in the process!"
}, },
"2": { "2": {
"label": "Too Risky, Leave", "label": "Too Risky, Leave",

View File

@ -1,25 +1,25 @@
{ {
"intro": "As you walk down a narrow pathway, you see a towering silhouette blocking your path.$You get closer to see a Snorlax sleeping peacefully.\nIt seems like there's no way around it.", "intro": "As you walk down a narrow pathway, you see a towering silhouette blocking your path.$You get closer to see a {{snorlaxName}} sleeping peacefully.\nIt seems like there's no way around it.",
"title": "Slumbering Snorlax", "title": "Slumbering {{snorlaxName}}",
"description": "You could attack it to try and get it to move, or simply wait for it to wake up. Who knows how long that could take, though...", "description": "You could attack it to try and get it to move, or simply wait for it to wake up. Who knows how long that could take, though...",
"query": "What will you do?", "query": "What will you do?",
"option": { "option": {
"1": { "1": {
"label": "Battle It", "label": "Battle It",
"tooltip": "(-) Fight Sleeping Snorlax\n(+) Special Reward", "tooltip": "(-) Fight Sleeping {{snorlaxName}}\n(+) Special Reward",
"selected": "You approach the\nPokémon without fear." "selected": "You approach the\nPokémon without fear."
}, },
"2": { "2": {
"label": "Wait for It to Move", "label": "Wait for It to Move",
"tooltip": "(-) Wait a Long Time\n(+) Recover Party", "tooltip": "(-) Wait a Long Time\n(+) Recover Party",
"selected": ".@d{32}.@d{32}.@d{32}$You wait for a time, but the Snorlax's yawns make your party sleepy...", "selected": ".@d{32}.@d{32}.@d{32}$You wait for a time, but the {{snorlaxName}}'s yawns make your party sleepy...",
"rest_result": "When you all awaken, the Snorlax is no where to be found -\nbut your Pokémon are all healed!" "rest_result": "When you all awaken, the {{snorlaxName}} is no where to be found -\nbut your Pokémon are all healed!"
}, },
"3": { "3": {
"label": "Steal Its Item", "label": "Steal Its Item",
"tooltip": "(+) {{option3PrimaryName}} uses {{option3PrimaryMove}}\n(+) Special Reward", "tooltip": "(+) {{option3PrimaryName}} uses {{option3PrimaryMove}}\n(+) Special Reward",
"disabled_tooltip": "Your Pokémon need to know certain moves to choose this", "disabled_tooltip": "Your Pokémon need to know certain moves to choose this",
"selected": "Your {{option3PrimaryName}} uses {{option3PrimaryMove}}!$@s{item_fanfare}It steals Leftovers off the sleeping\nSnorlax and you make out like bandits!" "selected": "Your {{option3PrimaryName}} uses {{option3PrimaryMove}}!$@s{item_fanfare}It steals Leftovers off the sleeping\n{{snorlaxName}} and you make out like bandits!"
} }
} }
} }

View File

@ -1,20 +1,20 @@
{ {
"intro": "It's a massive Shuckle and what appears\nto be a large stash of... juice?", "intro": "It's a massive {{shuckleName}} and what appears\nto be a large stash of... juice?",
"title": "The Strong Stuff", "title": "The Strong Stuff",
"description": "The Shuckle that blocks your path looks incredibly strong. Meanwhile, the juice next to it is emanating power of some kind.\n\nThe Shuckle extends its feelers in your direction. It seems like it wants to do something...", "description": "The {{shuckleName}} that blocks your path looks incredibly strong. Meanwhile, the juice next to it is emanating power of some kind.\n\nThe {{shuckleName}} extends its feelers in your direction. It seems like it wants to do something...",
"query": "What will you do?", "query": "What will you do?",
"option": { "option": {
"1": { "1": {
"label": "Approach the Shuckle", "label": "Approach the {{shuckleName}}",
"tooltip": "(?) Something awful or amazing might happen", "tooltip": "(?) Something awful or amazing might happen",
"selected": "You black out.", "selected": "You black out.",
"selected_2": "@f{150}When you awaken, the Shuckle is gone\nand juice stash completely drained.${{highBstPokemon1}} and {{highBstPokemon2}}\nfeel a terrible lethargy come over them!$Their base stats were reduced by {{reductionValue}}!$Your remaining Pokémon feel an incredible vigor, though!\nTheir base stats are increased by {{increaseValue}}!" "selected_2": "@f{150}When you awaken, the {{shuckleName}} is gone\nand juice stash completely drained.${{highBstPokemon1}} and {{highBstPokemon2}}\nfeel a terrible lethargy come over them!$Their base stats were reduced by {{reductionValue}}!$Your remaining Pokémon feel an incredible vigor, though!\nTheir base stats are increased by {{increaseValue}}!"
}, },
"2": { "2": {
"label": "Battle the Shuckle", "label": "Battle the {{shuckleName}}",
"tooltip": "(-) Hard Battle\n(+) Special Rewards", "tooltip": "(-) Hard Battle\n(+) Special Rewards",
"selected": "Enraged, the Shuckle drinks some of its juice and attacks!", "selected": "Enraged, the {{shuckleName}} drinks some of its juice and attacks!",
"stat_boost": "The Shuckle's juice boosts its stats!" "stat_boost": "The {{shuckleName}}'s juice boosts its stats!"
} }
}, },
"outro": "What a bizarre turn of events." "outro": "What a bizarre turn of events."

View File

@ -1,5 +1,5 @@
import BattleScene from "#app/battle-scene"; import BattleScene from "#app/battle-scene";
import { BattleType, BattlerIndex } from "#app/battle"; import { BattlerIndex, BattleType } from "#app/battle";
import { applyAbAttrs, SyncEncounterNatureAbAttr } from "#app/data/ability"; import { applyAbAttrs, SyncEncounterNatureAbAttr } from "#app/data/ability";
import { getCharVariantFromDialogue } from "#app/data/dialogue"; import { getCharVariantFromDialogue } from "#app/data/dialogue";
import { TrainerSlot } from "#app/data/trainer-config"; import { TrainerSlot } from "#app/data/trainer-config";
@ -10,7 +10,7 @@ import { Species } from "#app/enums/species";
import { EncounterPhaseEvent } from "#app/events/battle-scene"; import { EncounterPhaseEvent } from "#app/events/battle-scene";
import Pokemon, { FieldPosition } from "#app/field/pokemon"; import Pokemon, { FieldPosition } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages"; import { getPokemonNameWithAffix } from "#app/messages";
import { regenerateModifierPoolThresholds, ModifierPoolType } from "#app/modifier/modifier-type"; import { ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier"; import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier";
import { achvs } from "#app/system/achv"; import { achvs } from "#app/system/achv";
import { handleTutorial, Tutorial } from "#app/tutorial"; import { handleTutorial, Tutorial } from "#app/tutorial";
@ -18,6 +18,7 @@ import { Mode } from "#app/ui/ui";
import i18next from "i18next"; import i18next from "i18next";
import { BattlePhase } from "./battle-phase"; import { BattlePhase } from "./battle-phase";
import * as Utils from "#app/utils"; import * as Utils from "#app/utils";
import { randSeedInt } from "#app/utils";
import { CheckSwitchPhase } from "./check-switch-phase"; import { CheckSwitchPhase } from "./check-switch-phase";
import { GameOverPhase } from "./game-over-phase"; import { GameOverPhase } from "./game-over-phase";
import { PostSummonPhase } from "./post-summon-phase"; import { PostSummonPhase } from "./post-summon-phase";
@ -32,7 +33,6 @@ import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import { doTrainerExclamation } from "#app/data/mystery-encounters/utils/encounter-phase-utils"; import { doTrainerExclamation } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
import { randSeedInt } from "#app/utils";
import { getGoldenBugNetSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import { getGoldenBugNetSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
export class EncounterPhase extends BattlePhase { export class EncounterPhase extends BattlePhase {
@ -153,8 +153,7 @@ export class EncounterPhase extends BattlePhase {
loadEnemyAssets.push(battle.trainer?.loadAssets().then(() => battle.trainer?.initSprite())!); // TODO: is this bang correct? loadEnemyAssets.push(battle.trainer?.loadAssets().then(() => battle.trainer?.initSprite())!); // TODO: is this bang correct?
} else if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) { } else if (battle.battleType === BattleType.MYSTERY_ENCOUNTER) {
if (!battle.mysteryEncounter) { if (!battle.mysteryEncounter) {
const newEncounter = this.scene.getMysteryEncounter(mysteryEncounter); battle.mysteryEncounter = this.scene.getMysteryEncounter(mysteryEncounter?.encounterType);
battle.mysteryEncounter = newEncounter;
} }
if (battle.mysteryEncounter.introVisuals) { if (battle.mysteryEncounter.introVisuals) {
loadEnemyAssets.push(battle.mysteryEncounter.introVisuals.loadAssets().then(() => battle.mysteryEncounter!.introVisuals!.initSprite())); loadEnemyAssets.push(battle.mysteryEncounter.introVisuals.loadAssets().then(() => battle.mysteryEncounter!.introVisuals!.initSprite()));

View File

@ -952,7 +952,7 @@ export class GameData {
gameVersion: scene.game.config.gameVersion, gameVersion: scene.game.config.gameVersion,
timestamp: new Date().getTime(), timestamp: new Date().getTime(),
challenges: scene.gameMode.challenges.map(c => new ChallengeData(c)), challenges: scene.gameMode.challenges.map(c => new ChallengeData(c)),
mysteryEncounterType: scene.currentBattle.mysteryEncounter?.encounterType, mysteryEncounterType: scene.currentBattle.mysteryEncounter?.encounterType ?? -1,
mysteryEncounterSaveData: scene.mysteryEncounterSaveData mysteryEncounterSaveData: scene.mysteryEncounterSaveData
} as SessionSaveData; } as SessionSaveData;
} }
@ -1044,7 +1044,7 @@ export class GameData {
scene.score = sessionData.score; scene.score = sessionData.score;
scene.updateScoreText(); scene.updateScoreText();
scene.mysteryEncounterSaveData = sessionData?.mysteryEncounterSaveData ?? new MysteryEncounterSaveData(null); scene.mysteryEncounterSaveData = new MysteryEncounterSaveData(sessionData.mysteryEncounterSaveData);
scene.newArena(sessionData.arena.biome); scene.newArena(sessionData.arena.biome);

View File

@ -103,7 +103,7 @@ export default class PokemonData {
this.fusionLuck = source.fusionLuck !== undefined ? source.fusionLuck : (source.fusionShiny ? source.fusionVariant + 1 : 0); this.fusionLuck = source.fusionLuck !== undefined ? source.fusionLuck : (source.fusionShiny ? source.fusionVariant + 1 : 0);
this.usedTMs = source.usedTMs ?? []; this.usedTMs = source.usedTMs ?? [];
this.mysteryEncounterPokemonData = source.mysteryEncounterPokemonData ?? new MysteryEncounterPokemonData(); this.mysteryEncounterPokemonData = new MysteryEncounterPokemonData(source.mysteryEncounterPokemonData);
if (!forHistory) { if (!forHistory) {
this.boss = (source instanceof EnemyPokemon && !!source.bossSegments) || (!this.player && !!source.boss); this.boss = (source instanceof EnemyPokemon && !!source.bossSegments) || (!this.player && !!source.boss);

View File

@ -39,6 +39,8 @@ describe("Berries Abound - Mystery Encounter", () => {
game.override.startingWave(defaultWave); game.override.startingWave(defaultWave);
game.override.startingBiome(defaultBiome); game.override.startingBiome(defaultBiome);
game.override.disableTrainerWaves(); game.override.disableTrainerWaves();
game.override.startingModifier([]);
game.override.startingHeldItems([]);
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue( vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(
new Map<Biome, MysteryEncounterType[]>([ new Map<Biome, MysteryEncounterType[]>([
@ -130,11 +132,15 @@ describe("Berries Abound - Mystery Encounter", () => {
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
}); });
it("should reward the player with X berries based on wave", { retry: 5 }, async () => { it("should reward the player with X berries based on wave", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty);
const numBerries = game.scene.currentBattle.mysteryEncounter!.misc.numBerries; const numBerries = game.scene.currentBattle.mysteryEncounter!.misc.numBerries;
scene.modifiers = [];
// Clear out any pesky mods that slipped through test spin-up
scene.modifiers.forEach(mod => {
scene.removeModifier(mod);
});
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);

View File

@ -165,7 +165,7 @@ describe("Global Trade System - Mystery Encounter", () => {
}); });
}); });
it("Should trade a Pokemon from the player's party for the a random wonder trade Pokemon", async () => { it("Should trade a Pokemon from the player's party for a random wonder trade Pokemon", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty);
const speciesBefore = scene.getParty()[2].species.speciesId; const speciesBefore = scene.getParty()[2].species.speciesId;

View File

@ -121,7 +121,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
species: getPokemonSpecies(Species.SHUCKLE), species: getPokemonSpecies(Species.SHUCKLE),
isBoss: true, isBoss: true,
bossSegments: 5, bossSegments: 5,
mysteryEncounterPokemonData: new MysteryEncounterPokemonData(1.25), mysteryEncounterPokemonData: new MysteryEncounterPokemonData({ spriteScale: 1.25 }),
nature: Nature.BOLD, nature: Nature.BOLD,
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER], moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
modifierConfigs: expect.any(Array), modifierConfigs: expect.any(Array),

View File

@ -164,9 +164,9 @@ describe("Uncommon Breed - Mystery Encounter", () => {
}); });
it("should NOT be selectable if the player doesn't have enough berries", async () => { it("should NOT be selectable if the player doesn't have enough berries", async () => {
game.override.startingHeldItems([]);
await game.runToMysteryEncounter(MysteryEncounterType.UNCOMMON_BREED, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.UNCOMMON_BREED, defaultParty);
await game.phaseInterceptor.to(MysteryEncounterPhase, false); await game.phaseInterceptor.to(MysteryEncounterPhase, false);
game.scene.modifiers = [];
const encounterPhase = scene.getCurrentPhase(); const encounterPhase = scene.getCurrentPhase();
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);