mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2024-12-03 12:16:22 +00:00
first implementation pass at clowning around
This commit is contained in:
parent
6d2129a3f0
commit
afe1015094
1694
public/battle-anims/encounter-smokescreen.json
Normal file
1694
public/battle-anims/encounter-smokescreen.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -110,7 +110,8 @@ export enum CommonAnim {
|
||||
*/
|
||||
export enum EncounterAnim {
|
||||
MAGMA_BG,
|
||||
MAGMA_SPOUT
|
||||
MAGMA_SPOUT,
|
||||
SMOKESCREEN
|
||||
}
|
||||
|
||||
export class AnimConfig {
|
||||
@ -533,16 +534,16 @@ export function initMoveAnim(scene: BattleScene, move: Moves): Promise<void> {
|
||||
export async function initEncounterAnims(scene: BattleScene, encounterAnim: EncounterAnim | EncounterAnim[]): Promise<void> {
|
||||
const anims = Array.isArray(encounterAnim) ? encounterAnim : [encounterAnim];
|
||||
const encounterAnimNames = Utils.getEnumKeys(EncounterAnim);
|
||||
const encounterAnimIds = Utils.getEnumValues(EncounterAnim);
|
||||
// const encounterAnimIds = Utils.getEnumValues(EncounterAnim);
|
||||
const encounterAnimFetches = [];
|
||||
for (const anim of anims) {
|
||||
if (encounterAnims.has(anim) && !isNullOrUndefined(encounterAnims.get(anim))) {
|
||||
continue;
|
||||
}
|
||||
const encounterAnimId = encounterAnimIds[anim];
|
||||
// const encounterAnimId = encounterAnimIds[anim];
|
||||
encounterAnimFetches.push(scene.cachedFetch(`./battle-anims/encounter-${encounterAnimNames[anim].toLowerCase().replace(/\_/g, "-")}.json`)
|
||||
.then(response => response.json())
|
||||
.then(cas => encounterAnims.set(encounterAnimId, new AnimConfig(cas))));
|
||||
.then(cas => encounterAnims.set(anim, new AnimConfig(cas))));
|
||||
}
|
||||
await Promise.allSettled(encounterAnimFetches);
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ export const AnOfferYouCantRefuseEncounter: IMysteryEncounter =
|
||||
const pokemon = getHighestStatTotalPlayerPokemon(scene, false);
|
||||
const price = scene.getWaveMoneyAmount(10);
|
||||
|
||||
encounter.setDialogueToken("strongestPokemon", pokemon.name);
|
||||
encounter.setDialogueToken("strongestPokemon", pokemon.getNameToRender());
|
||||
encounter.setDialogueToken("price", price.toString());
|
||||
|
||||
// Store pokemon and price
|
||||
|
@ -1,181 +0,0 @@
|
||||
import {
|
||||
EnemyPartyConfig,
|
||||
initBattleWithEnemyConfig,
|
||||
setEncounterRewards,
|
||||
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import {
|
||||
trainerConfigs,
|
||||
TrainerPartyCompoundTemplate,
|
||||
TrainerPartyTemplate,
|
||||
} from "#app/data/trainer-config";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import IMysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { Species } from "#enums/species";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
const namespace = "mysteryEncounter:clowningAround";
|
||||
|
||||
/**
|
||||
* Clowning Around encounter.
|
||||
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/69 | GitHub Issue #69}
|
||||
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||
*/
|
||||
export const ClowningAroundEncounter: IMysteryEncounter =
|
||||
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.CLOWNING_AROUND)
|
||||
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
|
||||
.withIntroSpriteConfigs([
|
||||
{
|
||||
spriteKey: Species.MR_MIME.toString(),
|
||||
fileRoot: "pokemon",
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
x: -25,
|
||||
tint: 0.3,
|
||||
y: -3,
|
||||
yShadow: -3
|
||||
},
|
||||
{
|
||||
spriteKey: Species.BLACEPHALON.toString(),
|
||||
fileRoot: "pokemon/exp",
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
x: 25,
|
||||
tint: 0.3,
|
||||
y: -3,
|
||||
yShadow: -3
|
||||
},
|
||||
{
|
||||
spriteKey: "harlequin",
|
||||
fileRoot: "trainer",
|
||||
hasShadow: true,
|
||||
x: 0
|
||||
},
|
||||
])
|
||||
.withIntroDialogue([
|
||||
{
|
||||
text: `${namespace}.intro`,
|
||||
},
|
||||
{
|
||||
text: `${namespace}.intro_dialogue`,
|
||||
speaker: `${namespace}.speaker`
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter;
|
||||
|
||||
// Clown trainer is pulled from pool of boss trainers (gym leaders) for the biome
|
||||
// They are given an E4 template team, so will be stronger than usual boss encounter and always have 6 mons
|
||||
const clownTrainerType = TrainerType.HARLEQUIN;
|
||||
const clownPartyTemplate = new TrainerPartyCompoundTemplate(
|
||||
new TrainerPartyTemplate(1, PartyMemberStrength.STRONG),
|
||||
new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER),
|
||||
new TrainerPartyTemplate(1, PartyMemberStrength.STRONG));
|
||||
const clownConfig = trainerConfigs[clownTrainerType].copy();
|
||||
clownConfig.setPartyTemplates(clownPartyTemplate);
|
||||
clownConfig.partyTemplateFunc = null; // Overrides party template func
|
||||
|
||||
encounter.enemyPartyConfigs.push({
|
||||
trainerConfig: clownConfig,
|
||||
pokemonConfigs: [ // Overrides first 2 pokemon to be Mr. Mime and Blacephalon
|
||||
{
|
||||
species: getPokemonSpecies(Species.MR_MIME),
|
||||
isBoss: false
|
||||
},
|
||||
{
|
||||
species: getPokemonSpecies(Species.BLACEPHALON),
|
||||
isBoss: true
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
return true;
|
||||
})
|
||||
.withTitle(`${namespace}.title`)
|
||||
.withDescription(`${namespace}.description`)
|
||||
.withQuery(`${namespace}.query`)
|
||||
.withSimpleOption(
|
||||
{
|
||||
buttonLabel: `${namespace}.option.1.label`,
|
||||
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter;
|
||||
// Spawn battle
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM], fillRemaining: true });
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
}
|
||||
)
|
||||
.withSimpleOption(
|
||||
{
|
||||
buttonLabel: `${namespace}.option.2.label`,
|
||||
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter;
|
||||
// Spawn hard fight with ULTRA/GREAT reward (can improve with luck)
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[1];
|
||||
|
||||
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ULTRA, ModifierTier.ULTRA, ModifierTier.GREAT, ModifierTier.GREAT], fillRemaining: true });
|
||||
|
||||
// Seed offsets to remove possibility of different trainers having exact same teams
|
||||
let ret;
|
||||
scene.executeWithSeedOffset(() => {
|
||||
ret = initBattleWithEnemyConfig(scene, config);
|
||||
}, scene.currentBattle.waveIndex * 100);
|
||||
return ret;
|
||||
}
|
||||
)
|
||||
.withSimpleOption(
|
||||
{
|
||||
buttonLabel: `${namespace}.option.3.label`,
|
||||
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}.option.selected`,
|
||||
},
|
||||
],
|
||||
},
|
||||
async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter;
|
||||
// Spawn brutal fight with ROGUE/ULTRA/GREAT reward (can improve with luck)
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[2];
|
||||
|
||||
// To avoid player level snowballing from picking this option
|
||||
encounter.expMultiplier = 0.9;
|
||||
|
||||
setEncounterRewards(scene, { guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ROGUE, ModifierTier.ULTRA, ModifierTier.GREAT], fillRemaining: true });
|
||||
|
||||
// Seed offsets to remove possibility of different trainers having exact same teams
|
||||
let ret;
|
||||
scene.executeWithSeedOffset(() => {
|
||||
ret = initBattleWithEnemyConfig(scene, config);
|
||||
}, scene.currentBattle.waveIndex * 1000);
|
||||
return ret;
|
||||
}
|
||||
)
|
||||
.withOutroDialogue([
|
||||
{
|
||||
text: `${namespace}.outro`,
|
||||
},
|
||||
])
|
||||
.build();
|
@ -0,0 +1,469 @@
|
||||
import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { trainerConfigs, TrainerPartyCompoundTemplate, TrainerPartyTemplate, } from "#app/data/trainer-config";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { BerryModifierType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import IMysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { Species } from "#enums/species";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { Type } from "#app/data/type";
|
||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||
import { randSeedInt, randSeedShuffle } from "#app/utils";
|
||||
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import i18next from "i18next";
|
||||
import { OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
|
||||
import { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||
import { Ability } from "#app/data/ability";
|
||||
import { BerryModifier } from "#app/modifier/modifier";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { EncounterAnim, EncounterBattleAnim } from "#app/data/battle-anims";
|
||||
import { MoveCategory } from "#app/data/move";
|
||||
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
const namespace = "mysteryEncounter:clowningAround";
|
||||
|
||||
const RANDOM_ABILITY_POOL = [
|
||||
Abilities.STURDY,
|
||||
Abilities.PICKUP,
|
||||
Abilities.INTIMIDATE,
|
||||
Abilities.GUTS,
|
||||
Abilities.DROUGHT,
|
||||
Abilities.DRIZZLE,
|
||||
Abilities.SNOW_WARNING,
|
||||
Abilities.SAND_STREAM,
|
||||
Abilities.ELECTRIC_SURGE,
|
||||
Abilities.PSYCHIC_SURGE,
|
||||
Abilities.GRASSY_SURGE,
|
||||
Abilities.MISTY_SURGE,
|
||||
Abilities.MAGICIAN,
|
||||
Abilities.SHEER_FORCE,
|
||||
Abilities.PRANKSTER
|
||||
];
|
||||
|
||||
/**
|
||||
* Clowning Around encounter.
|
||||
* @see {@link https://github.com/AsdarDevelops/PokeRogue-Events/issues/69 | GitHub Issue #69}
|
||||
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
||||
*/
|
||||
export const ClowningAroundEncounter: IMysteryEncounter =
|
||||
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.CLOWNING_AROUND)
|
||||
.withEncounterTier(MysteryEncounterTier.ULTRA)
|
||||
.withSceneWaveRangeRequirement(80, 180)
|
||||
.withAnimations(EncounterAnim.SMOKESCREEN)
|
||||
.withAutoHideIntroVisuals(false)
|
||||
.withIntroSpriteConfigs([
|
||||
{
|
||||
spriteKey: Species.MR_MIME.toString(),
|
||||
fileRoot: "pokemon",
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
x: -25,
|
||||
tint: 0.3,
|
||||
y: -3,
|
||||
yShadow: -3
|
||||
},
|
||||
{
|
||||
spriteKey: Species.BLACEPHALON.toString(),
|
||||
fileRoot: "pokemon/exp",
|
||||
hasShadow: true,
|
||||
repeat: true,
|
||||
x: 25,
|
||||
tint: 0.3,
|
||||
y: -3,
|
||||
yShadow: -3
|
||||
},
|
||||
{
|
||||
spriteKey: "harlequin",
|
||||
fileRoot: "trainer",
|
||||
hasShadow: true,
|
||||
x: 0,
|
||||
y: 2,
|
||||
yShadow: 2
|
||||
},
|
||||
])
|
||||
.withIntroDialogue([
|
||||
{
|
||||
text: `${namespace}.intro`,
|
||||
},
|
||||
{
|
||||
text: `${namespace}.intro_dialogue`,
|
||||
speaker: `${namespace}.speaker`
|
||||
},
|
||||
])
|
||||
.withOnInit((scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter;
|
||||
|
||||
// Clown trainer is pulled from pool of boss trainers (gym leaders) for the biome
|
||||
// They are given an E4 template team, so will be stronger than usual boss encounter and always have 6 mons
|
||||
const clownTrainerType = TrainerType.HARLEQUIN;
|
||||
const clownPartyTemplate = new TrainerPartyCompoundTemplate(
|
||||
new TrainerPartyTemplate(1, PartyMemberStrength.STRONG),
|
||||
new TrainerPartyTemplate(1, PartyMemberStrength.STRONGER));
|
||||
const clownConfig = trainerConfigs[clownTrainerType].copy();
|
||||
clownConfig.setPartyTemplates(clownPartyTemplate);
|
||||
clownConfig.setDoubleOnly();
|
||||
clownConfig.partyTemplateFunc = null; // Overrides party template func
|
||||
|
||||
// Generate random ability for Blacephalon from pool
|
||||
const ability = RANDOM_ABILITY_POOL[randSeedInt(RANDOM_ABILITY_POOL.length)];
|
||||
encounter.setDialogueToken("ability", new Ability(ability, 3).name);
|
||||
encounter.misc = { ability };
|
||||
|
||||
encounter.enemyPartyConfigs.push({
|
||||
trainerConfig: clownConfig,
|
||||
pokemonConfigs: [ // Overrides first 2 pokemon to be Mr. Mime and Blacephalon
|
||||
{
|
||||
species: getPokemonSpecies(Species.MR_MIME),
|
||||
isBoss: true,
|
||||
moveSet: [Moves.TEETER_DANCE, Moves.ALLY_SWITCH, Moves.DAZZLING_GLEAM, Moves.PSYCHIC]
|
||||
},
|
||||
{ // Blacephalon has the random ability from pool, and 2 entirely random types to fit with the theme of the encounter
|
||||
species: getPokemonSpecies(Species.BLACEPHALON),
|
||||
ability: ability,
|
||||
mysteryEncounterData: new MysteryEncounterPokemonData(null, null, null, [randSeedInt(18), randSeedInt(18)]),
|
||||
isBoss: true,
|
||||
moveSet: [Moves.TRICK, Moves.HYPNOSIS, Moves.SHADOW_BALL, Moves.MIND_BLOWN]
|
||||
},
|
||||
],
|
||||
doubleBattle: true
|
||||
});
|
||||
|
||||
// Load animations/sfx for start of fight moves
|
||||
loadCustomMovesForEncounter(scene, [Moves.ROLE_PLAY, Moves.TAUNT]);
|
||||
|
||||
// These have to be defined at runtime so that modifierTypes exist
|
||||
encounter.misc.RANDOM_ULTRA_POOL = [
|
||||
modifierTypes.REVIVER_SEED,
|
||||
modifierTypes.GOLDEN_PUNCH,
|
||||
modifierTypes.ATTACK_TYPE_BOOSTER,
|
||||
modifierTypes.QUICK_CLAW,
|
||||
modifierTypes.WIDE_LENS,
|
||||
modifierTypes.WHITE_HERB
|
||||
];
|
||||
|
||||
encounter.misc.RANDOM_ROGUE_POOL = [
|
||||
modifierTypes.LEFTOVERS,
|
||||
modifierTypes.SHELL_BELL,
|
||||
modifierTypes.SOUL_DEW,
|
||||
modifierTypes.SOOTHE_BELL,
|
||||
modifierTypes.SCOPE_LENS,
|
||||
modifierTypes.BATON,
|
||||
modifierTypes.FOCUS_BAND,
|
||||
modifierTypes.KINGS_ROCK,
|
||||
modifierTypes.GRIP_CLAW
|
||||
];
|
||||
|
||||
return true;
|
||||
})
|
||||
.withTitle(`${namespace}.title`)
|
||||
.withDescription(`${namespace}.description`)
|
||||
.withQuery(`${namespace}.query`)
|
||||
.withOption(
|
||||
new MysteryEncounterOptionBuilder()
|
||||
.withOptionMode(MysteryEncounterOptionMode.DEFAULT)
|
||||
.withDialogue({
|
||||
buttonLabel: `${namespace}.option.1.label`,
|
||||
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}.option.1.selected`,
|
||||
speaker: `${namespace}.speaker`
|
||||
},
|
||||
],
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
const encounter = scene.currentBattle.mysteryEncounter;
|
||||
// Spawn battle
|
||||
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
|
||||
|
||||
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.TM_COMMON, modifierTypes.TM_GREAT, modifierTypes.MEMORY_MUSHROOM], fillRemaining: true });
|
||||
|
||||
// TODO: when Magic Room and Wonder Room are implemented, add those to start of battle
|
||||
encounter.startOfBattleEffects.push(
|
||||
{ // Mr. Mime copies the Blacephalon's random ability
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||
targets: [BattlerIndex.ENEMY_2],
|
||||
move: new PokemonMove(Moves.ROLE_PLAY),
|
||||
ignorePp: true
|
||||
},
|
||||
{
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
||||
targets: [BattlerIndex.PLAYER],
|
||||
move: new PokemonMove(Moves.TAUNT),
|
||||
ignorePp: true
|
||||
},
|
||||
{
|
||||
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
||||
targets: [BattlerIndex.PLAYER_2],
|
||||
move: new PokemonMove(Moves.TAUNT),
|
||||
ignorePp: true
|
||||
});
|
||||
|
||||
await transitionMysteryEncounterIntroVisuals(scene);
|
||||
await initBattleWithEnemyConfig(scene, config);
|
||||
})
|
||||
.withPostOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||
// After the battle, offer the player the opportunity to permanently swap ability
|
||||
const abilityWasSwapped = await handleSwapAbility(scene);
|
||||
if (abilityWasSwapped) {
|
||||
await scene.ui.setMode(Mode.MESSAGE);
|
||||
await showEncounterText(scene, `${namespace}.option.1.ability_gained`);
|
||||
}
|
||||
|
||||
// Play animations once ability swap is complete
|
||||
// Trainer sprite that is shown at end of battle is not the same as mystery encounter intro visuals
|
||||
scene.tweens.add({
|
||||
targets: scene.currentBattle.trainer,
|
||||
x: "+=16",
|
||||
y: "-=16",
|
||||
alpha: 0,
|
||||
ease: "Sine.easeInOut",
|
||||
duration: 250
|
||||
});
|
||||
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon(), scene.getPlayerPokemon());
|
||||
background.playWithoutTargets(scene, 230, 40, 2);
|
||||
return true;
|
||||
})
|
||||
.build()
|
||||
)
|
||||
.withOption(
|
||||
new MysteryEncounterOptionBuilder()
|
||||
.withOptionMode(MysteryEncounterOptionMode.DEFAULT)
|
||||
.withDialogue({
|
||||
buttonLabel: `${namespace}.option.2.label`,
|
||||
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}.option.2.selected`,
|
||||
speaker: `${namespace}.speaker`
|
||||
},
|
||||
{
|
||||
text: `${namespace}.option.2.selected_2`,
|
||||
},
|
||||
{
|
||||
text: `${namespace}.option.2.selected_3`,
|
||||
speaker: `${namespace}.speaker`
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
// Swap player's items on pokemon with the most items
|
||||
// Item comparisons look at whichever Pokemon has the greatest number of TRANSFERABLE, non-berry items
|
||||
// So Vitamins, form change items, etc. are not included
|
||||
const encounter = scene.currentBattle.mysteryEncounter;
|
||||
|
||||
const party = scene.getParty();
|
||||
let mostHeldItemsPokemon = party[0];
|
||||
let count = mostHeldItemsPokemon.getHeldItems()
|
||||
.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
|
||||
.reduce((v, m) => v + m.stackCount, 0);
|
||||
|
||||
party.forEach(pokemon => {
|
||||
const nextCount = pokemon.getHeldItems()
|
||||
.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
|
||||
.reduce((v, m) => v + m.stackCount, 0);
|
||||
if (nextCount > count) {
|
||||
mostHeldItemsPokemon = pokemon;
|
||||
count = nextCount;
|
||||
}
|
||||
});
|
||||
|
||||
encounter.setDialogueToken("switchPokemon", mostHeldItemsPokemon.getNameToRender());
|
||||
|
||||
const items = mostHeldItemsPokemon.getHeldItems();
|
||||
|
||||
// Shuffles Berries (if they have any)
|
||||
const berries = items.filter(m => m instanceof BerryModifier);
|
||||
|
||||
berries.forEach(berry => {
|
||||
const stackCount = berry.stackCount;
|
||||
scene.removeModifier(berry);
|
||||
const newBerry = generateModifierTypeOption(scene, modifierTypes.BERRY, [randSeedInt(Object.keys(BerryType).filter(s => !isNaN(Number(s))).length) as BerryType]).type as BerryModifierType;
|
||||
for (let i = 0; i < stackCount; i++) {
|
||||
applyModifierTypeToPlayerPokemon(scene, mostHeldItemsPokemon, newBerry);
|
||||
}
|
||||
});
|
||||
|
||||
// Shuffle Transferable held items in the same tier (only shuffles Ultra and Rogue atm)
|
||||
const transferableItems = items.filter(m => m.isTransferrable && !(m instanceof BerryModifier));
|
||||
|
||||
transferableItems.forEach(transferableItem => {
|
||||
const stackCount = transferableItem.stackCount;
|
||||
transferableItem.type.withTierFromPool();
|
||||
|
||||
// Lucky Eggs and other items that do not appear in item pools are treated as Ultra rarity
|
||||
const tier = transferableItem.type.tier ?? ModifierTier.ULTRA;
|
||||
|
||||
if (tier === ModifierTier.ULTRA) {
|
||||
scene.removeModifier(transferableItem);
|
||||
for (let i = 0; i < stackCount; i++) {
|
||||
const newItemType = encounter.misc.RANDOM_ULTRA_POOL[randSeedInt(encounter.misc.RANDOM_ULTRA_POOL.length)];
|
||||
const newMod = generateModifierTypeOption(scene, newItemType).type as PokemonHeldItemModifierType;
|
||||
applyModifierTypeToPlayerPokemon(scene, mostHeldItemsPokemon, newMod);
|
||||
}
|
||||
} else if (tier === ModifierTier.ROGUE) {
|
||||
scene.removeModifier(transferableItem);
|
||||
for (let i = 0; i < stackCount; i++) {
|
||||
const newItemType = encounter.misc.RANDOM_ROGUE_POOL[randSeedInt(encounter.misc.RANDOM_ROGUE_POOL.length)];
|
||||
const newMod = generateModifierTypeOption(scene, newItemType).type as PokemonHeldItemModifierType;
|
||||
applyModifierTypeToPlayerPokemon(scene, mostHeldItemsPokemon, newMod);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
})
|
||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||
// Play animations
|
||||
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon(), scene.getPlayerPokemon());
|
||||
background.playWithoutTargets(scene, 230, 40, 2);
|
||||
await transitionMysteryEncounterIntroVisuals(scene);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
.withOption(
|
||||
new MysteryEncounterOptionBuilder()
|
||||
.withOptionMode(MysteryEncounterOptionMode.DEFAULT)
|
||||
.withDialogue({
|
||||
buttonLabel: `${namespace}.option.3.label`,
|
||||
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||
selected: [
|
||||
{
|
||||
text: `${namespace}.option.3.selected`,
|
||||
speaker: `${namespace}.speaker`
|
||||
},
|
||||
{
|
||||
text: `${namespace}.option.3.selected_2`,
|
||||
},
|
||||
{
|
||||
text: `${namespace}.option.3.selected_3`,
|
||||
speaker: `${namespace}.speaker`
|
||||
},
|
||||
],
|
||||
})
|
||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||
// Swap player's types on all party pokemon
|
||||
// If a Pokemon had a single type prior, they will still have a single type after
|
||||
for (const pokemon of scene.getParty()) {
|
||||
const originalTypes = pokemon.getTypes(false, false, true);
|
||||
|
||||
// If the Pokemon has non-status moves that don't match the Pokemon's type, prioritizes those as the new type
|
||||
// Makes the "randomness" of the shuffle slightly less punishing
|
||||
let priorityTypes = pokemon.moveset
|
||||
.filter(move => !originalTypes.includes(move.getMove().type) && move.getMove().category !== MoveCategory.STATUS)
|
||||
.map(move => move.getMove().type);
|
||||
if (priorityTypes?.length > 0) {
|
||||
priorityTypes = [...new Set(priorityTypes)];
|
||||
randSeedShuffle(priorityTypes);
|
||||
}
|
||||
|
||||
let newTypes;
|
||||
if (!originalTypes || originalTypes.length < 1) {
|
||||
newTypes = priorityTypes?.length > 0 ? [priorityTypes.pop()] : [(randSeedInt(18) as Type)];
|
||||
} else {
|
||||
newTypes = originalTypes.map(m => {
|
||||
if (priorityTypes?.length > 0) {
|
||||
const ret = priorityTypes.pop();
|
||||
randSeedShuffle(priorityTypes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return randSeedInt(18) as Type;
|
||||
});
|
||||
}
|
||||
|
||||
if (!pokemon.mysteryEncounterData) {
|
||||
pokemon.mysteryEncounterData = new MysteryEncounterPokemonData(null, null, null, newTypes);
|
||||
} else {
|
||||
pokemon.mysteryEncounterData.types = newTypes;
|
||||
}
|
||||
}
|
||||
})
|
||||
.withOptionPhase(async (scene: BattleScene) => {
|
||||
leaveEncounterWithoutBattle(scene, true);
|
||||
})
|
||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||
// Play animations
|
||||
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon(), scene.getPlayerPokemon());
|
||||
background.playWithoutTargets(scene, 230, 40, 2);
|
||||
await transitionMysteryEncounterIntroVisuals(scene);
|
||||
})
|
||||
.build()
|
||||
)
|
||||
.withOutroDialogue([
|
||||
{
|
||||
text: `${namespace}.outro`,
|
||||
},
|
||||
])
|
||||
.build();
|
||||
|
||||
async function handleSwapAbility(scene: BattleScene) {
|
||||
return new Promise<boolean>(async resolve => {
|
||||
await showEncounterDialogue(scene, `${namespace}.option.1.apply_ability_dialogue`, `${namespace}.speaker`);
|
||||
await showEncounterText(scene, `${namespace}.option.1.apply_ability_message`);
|
||||
|
||||
scene.ui.setMode(Mode.MESSAGE).then(() => {
|
||||
displayYesNoOptions(scene, resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function displayYesNoOptions(scene: BattleScene, resolve) {
|
||||
showEncounterText(scene, `${namespace}.option.1.ability_prompt`, 500, false);
|
||||
const fullOptions = [
|
||||
{
|
||||
label: i18next.t("menu:yes"),
|
||||
handler: () => {
|
||||
onYesAbilitySwap(scene, resolve);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: i18next.t("menu:no"),
|
||||
handler: () => {
|
||||
resolve(false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const config: OptionSelectConfig = {
|
||||
options: fullOptions,
|
||||
maxOptions: 7,
|
||||
yOffset: 0
|
||||
};
|
||||
scene.ui.setModeWithoutClear(Mode.OPTION_SELECT, config, null, true);
|
||||
}
|
||||
|
||||
function onYesAbilitySwap(scene: BattleScene, resolve) {
|
||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||
// Do ability swap
|
||||
if (!pokemon.mysteryEncounterData) {
|
||||
pokemon.mysteryEncounterData = new MysteryEncounterPokemonData(null, Abilities.AERILATE);
|
||||
}
|
||||
pokemon.mysteryEncounterData.ability = scene.currentBattle.mysteryEncounter.misc.ability;
|
||||
scene.currentBattle.mysteryEncounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender());
|
||||
resolve(true);
|
||||
};
|
||||
|
||||
const onPokemonNotSelected = () => {
|
||||
scene.ui.setMode(Mode.MESSAGE).then(() => {
|
||||
displayYesNoOptions(scene, resolve);
|
||||
});
|
||||
};
|
||||
|
||||
selectPokemonForOption(scene, onPokemonSelected, onPokemonNotSelected);
|
||||
}
|
@ -127,7 +127,7 @@ export const DarkDealEncounter: IMysteryEncounter =
|
||||
const removedPokemon = getRandomPlayerPokemon(scene, false, true);
|
||||
scene.removePokemonFromPlayerParty(removedPokemon);
|
||||
|
||||
scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", removedPokemon.name);
|
||||
scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", removedPokemon.getNameToRender());
|
||||
|
||||
// Store removed pokemon types
|
||||
scene.currentBattle.mysteryEncounter.misc = [
|
||||
|
@ -95,7 +95,7 @@ export const FieldTripEncounter: IMysteryEncounter =
|
||||
];
|
||||
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||
} else {
|
||||
encounter.setDialogueToken("pokeName", pokemon.name);
|
||||
encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
|
||||
encounter.setDialogueToken("move", move.getName());
|
||||
encounter.options[0].dialogue.selected = [
|
||||
{
|
||||
@ -187,7 +187,7 @@ export const FieldTripEncounter: IMysteryEncounter =
|
||||
];
|
||||
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||
} else {
|
||||
encounter.setDialogueToken("pokeName", pokemon.name);
|
||||
encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
|
||||
encounter.setDialogueToken("move", move.getName());
|
||||
encounter.options[1].dialogue.selected = [
|
||||
{
|
||||
@ -273,7 +273,7 @@ export const FieldTripEncounter: IMysteryEncounter =
|
||||
];
|
||||
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
||||
} else {
|
||||
encounter.setDialogueToken("pokeName", pokemon.name);
|
||||
encounter.setDialogueToken("pokeName", pokemon.getNameToRender());
|
||||
encounter.setDialogueToken("move", move.getName());
|
||||
encounter.options[2].dialogue.selected = [
|
||||
{
|
||||
|
@ -191,7 +191,7 @@ export const FieryFalloutEncounter: IMysteryEncounter =
|
||||
const chosenPokemon = burnable[roll];
|
||||
if (chosenPokemon.trySetStatus(StatusEffect.BURN)) {
|
||||
// Burn applied
|
||||
encounter.setDialogueToken("burnedPokemon", chosenPokemon.name);
|
||||
encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender());
|
||||
queueEncounterMessage(scene, `${namespace}.option.2.target_burned`);
|
||||
}
|
||||
}
|
||||
@ -245,7 +245,7 @@ function giveLeadPokemonCharcoal(scene: BattleScene) {
|
||||
if (leadPokemon) {
|
||||
const charcoal = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [Type.FIRE]).type as AttackTypeBoosterModifierType;
|
||||
applyModifierTypeToPlayerPokemon(scene, leadPokemon, charcoal);
|
||||
scene.currentBattle.mysteryEncounter.setDialogueToken("leadPokemon", leadPokemon.name);
|
||||
scene.currentBattle.mysteryEncounter.setDialogueToken("leadPokemon", leadPokemon.getNameToRender());
|
||||
queueEncounterMessage(scene, `${namespace}.found_charcoal`);
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ export const MysteriousChestEncounter: IMysteryEncounter =
|
||||
);
|
||||
koPlayerPokemon(scene, highestLevelPokemon);
|
||||
|
||||
scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", highestLevelPokemon.name);
|
||||
scene.currentBattle.mysteryEncounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender());
|
||||
// Show which Pokemon was KOed, then leave encounter with no rewards
|
||||
// Does this synchronously so that game over doesn't happen over result message
|
||||
await showEncounterText(scene, `${namespace}.option.1.bad`).then(() => {
|
||||
|
@ -92,7 +92,7 @@ export const ThePokemonSalesmanEncounter: IMysteryEncounter =
|
||||
encounter.options[0].dialogue.buttonTooltip = `${namespace}.option.1.tooltip_shiny`;
|
||||
}
|
||||
const price = scene.getWaveMoneyAmount(priceMultiplier);
|
||||
encounter.setDialogueToken("purchasePokemon", pokemon.name);
|
||||
encounter.setDialogueToken("purchasePokemon", pokemon.getNameToRender());
|
||||
encounter.setDialogueToken("price", price.toString());
|
||||
encounter.misc = {
|
||||
price: price,
|
||||
|
@ -16,6 +16,7 @@ import { BattleStat } from "#app/data/battle-stat";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { BerryType } from "#enums/berry-type";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
||||
|
||||
/** the i18n namespace for the encounter */
|
||||
const namespace = "mysteryEncounter:theStrongStuff";
|
||||
@ -70,7 +71,7 @@ export const TheStrongStuffEncounter: IMysteryEncounter =
|
||||
species: getPokemonSpecies(Species.SHUCKLE),
|
||||
isBoss: true,
|
||||
bossSegments: 5,
|
||||
spriteScale: 1.5,
|
||||
mysteryEncounterData: new MysteryEncounterPokemonData(1.5),
|
||||
nature: Nature.BOLD,
|
||||
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
|
||||
modifierTypes: [
|
||||
@ -147,7 +148,7 @@ export const TheStrongStuffEncounter: IMysteryEncounter =
|
||||
modifyPlayerPokemonBST(pokemon, 10);
|
||||
}
|
||||
|
||||
encounter.setDialogueToken("highBstPokemon", highestBst.name);
|
||||
encounter.setDialogueToken("highBstPokemon", highestBst.getNameToRender());
|
||||
await showEncounterText(scene, `${namespace}.option.1.selected_2`, null, true);
|
||||
|
||||
setEncounterRewards(scene, { fillRemaining: true });
|
||||
|
@ -0,0 +1,16 @@
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Type } from "#app/data/type";
|
||||
|
||||
export class MysteryEncounterPokemonData {
|
||||
public spriteScale: number;
|
||||
public ability: Abilities;
|
||||
public passive: Abilities;
|
||||
public types: Type[] = [];
|
||||
|
||||
constructor(spriteScale?: number, ability?: Abilities, passive?: Abilities, types?: Type[]) {
|
||||
this.spriteScale = spriteScale;
|
||||
this.ability = ability;
|
||||
this.passive = passive;
|
||||
this.types = types;
|
||||
}
|
||||
}
|
@ -305,7 +305,7 @@ export default class IMysteryEncounter implements IMysteryEncounter {
|
||||
}
|
||||
}
|
||||
if (this.primaryPokemon?.length > 0) {
|
||||
this.setDialogueToken("primaryName", this.primaryPokemon.name);
|
||||
this.setDialogueToken("primaryName", this.primaryPokemon.getNameToRender());
|
||||
for (const req of this.primaryPokemonRequirements) {
|
||||
if (!req.invertQuery) {
|
||||
const value = req.getDialogueToken(scene, this.primaryPokemon);
|
||||
@ -316,7 +316,7 @@ export default class IMysteryEncounter implements IMysteryEncounter {
|
||||
}
|
||||
}
|
||||
if (this.secondaryPokemonRequirements?.length > 0 && this.secondaryPokemon?.length > 0) {
|
||||
this.setDialogueToken("secondaryName", this.secondaryPokemon[0].name);
|
||||
this.setDialogueToken("secondaryName", this.secondaryPokemon[0].getNameToRender());
|
||||
for (const req of this.secondaryPokemonRequirements) {
|
||||
if (!req.invertQuery) {
|
||||
const value = req.getDialogueToken(scene, this.secondaryPokemon[0]);
|
||||
@ -342,7 +342,7 @@ export default class IMysteryEncounter implements IMysteryEncounter {
|
||||
}
|
||||
}
|
||||
if (opt.primaryPokemonRequirements?.length > 0 && opt.primaryPokemon?.length > 0) {
|
||||
this.setDialogueToken("option" + j + "PrimaryName", opt.primaryPokemon.name);
|
||||
this.setDialogueToken("option" + j + "PrimaryName", opt.primaryPokemon.getNameToRender());
|
||||
for (const req of opt.primaryPokemonRequirements) {
|
||||
if (!req.invertQuery) {
|
||||
const value = req.getDialogueToken(scene, opt.primaryPokemon);
|
||||
@ -353,7 +353,7 @@ export default class IMysteryEncounter implements IMysteryEncounter {
|
||||
}
|
||||
}
|
||||
if (opt.secondaryPokemonRequirements?.length > 0 && opt.secondaryPokemon?.length > 0) {
|
||||
this.setDialogueToken("option" + j + "SecondaryName", opt.secondaryPokemon[0].name);
|
||||
this.setDialogueToken("option" + j + "SecondaryName", opt.secondaryPokemon[0].getNameToRender());
|
||||
for (const req of opt.secondaryPokemonRequirements) {
|
||||
if (!req.invertQuery) {
|
||||
const value = req.getDialogueToken(scene, opt.secondaryPokemon[0]);
|
||||
|
@ -21,7 +21,7 @@ import { AbsoluteAvariceEncounter } from "#app/data/mystery-encounters/encounter
|
||||
import { ATrainersTestEncounter } from "#app/data/mystery-encounters/encounters/a-trainers-test-encounter";
|
||||
import { TrashToTreasureEncounter } from "#app/data/mystery-encounters/encounters/trash-to-treasure-encounter";
|
||||
import { BerriesAboundEncounter } from "#app/data/mystery-encounters/encounters/berries-abound-encounter";
|
||||
import { ClowningAroundEncounter } from "#app/data/mystery-encounters/encounters/clowing-around-encounter";
|
||||
import { ClowningAroundEncounter } from "#app/data/mystery-encounters/encounters/clowning-around-encounter";
|
||||
|
||||
// Spawn chance: (BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT + WIGHT_INCREMENT_ON_SPAWN_MISS * <number of missed spawns>) / 256
|
||||
export const BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT = 1;
|
||||
|
@ -30,6 +30,8 @@ import { TrainerConfig, trainerConfigs, TrainerSlot } from "#app/data/trainer-co
|
||||
import PokemonSpecies from "#app/data/pokemon-species";
|
||||
import Overrides from "#app/overrides";
|
||||
import { Egg, IEggOptions } from "#app/data/egg";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
||||
|
||||
/**
|
||||
* Animates exclamation sprite over trainer's head at start of encounter
|
||||
@ -63,7 +65,7 @@ export interface EnemyPokemonConfig {
|
||||
isBoss: boolean;
|
||||
bossSegments?: number;
|
||||
bossSegmentModifier?: number; // Additive to the determined segment number
|
||||
spriteScale?: number;
|
||||
mysteryEncounterData?: MysteryEncounterPokemonData;
|
||||
formIndex?: number;
|
||||
level?: number;
|
||||
gender?: Gender;
|
||||
@ -71,6 +73,8 @@ export interface EnemyPokemonConfig {
|
||||
moveSet?: Moves[];
|
||||
nature?: Nature;
|
||||
ivs?: [integer, integer, integer, integer, integer, integer];
|
||||
ability?: Abilities;
|
||||
shiny?: boolean;
|
||||
/** Can set just the status, or pass a timer on the status turns */
|
||||
status?: StatusEffect | [StatusEffect, number];
|
||||
mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void;
|
||||
@ -210,11 +214,14 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
||||
enemyPokemon.formIndex = config.formIndex;
|
||||
}
|
||||
|
||||
// Set scale
|
||||
if (!isNullOrUndefined(config.spriteScale)) {
|
||||
enemyPokemon.mysteryEncounterData = {
|
||||
spriteScale: config.spriteScale
|
||||
};
|
||||
// Set shiny
|
||||
if (!isNullOrUndefined(config.shiny)) {
|
||||
enemyPokemon.shiny = config.shiny;
|
||||
}
|
||||
|
||||
// Set custom mystery encounter data fields (such as sprite scale, custom abilities, types, etc.)
|
||||
if (!isNullOrUndefined(config.mysteryEncounterData)) {
|
||||
enemyPokemon.mysteryEncounterData = config.mysteryEncounterData;
|
||||
}
|
||||
|
||||
// Set Boss
|
||||
@ -252,6 +259,11 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
|
||||
|
||||
// Set summon data fields
|
||||
|
||||
// Set ability
|
||||
if (!isNullOrUndefined(config.ability)) {
|
||||
enemyPokemon.summonData.ability = config.ability;
|
||||
}
|
||||
|
||||
// Set gender
|
||||
if (!isNullOrUndefined(config.gender)) {
|
||||
enemyPokemon.gender = config.gender;
|
||||
@ -381,7 +393,7 @@ export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (p
|
||||
const pokemon = scene.getParty()[slotIndex];
|
||||
const secondaryOptions = onPokemonSelected(pokemon);
|
||||
if (!secondaryOptions) {
|
||||
scene.currentBattle.mysteryEncounter.setDialogueToken("selectedPokemon", pokemon.name);
|
||||
scene.currentBattle.mysteryEncounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
@ -395,7 +407,7 @@ export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (p
|
||||
const onSelect = option.handler;
|
||||
option.handler = () => {
|
||||
onSelect();
|
||||
scene.currentBattle.mysteryEncounter.setDialogueToken("selectedPokemon", pokemon.name);
|
||||
scene.currentBattle.mysteryEncounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
||||
resolve(true);
|
||||
return true;
|
||||
};
|
||||
|
@ -20,10 +20,6 @@ import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import { Gender } from "#app/data/gender";
|
||||
|
||||
export interface MysteryEncounterPokemonData {
|
||||
spriteScale?: number
|
||||
}
|
||||
|
||||
export function getSpriteKeysFromSpecies(species: Species, female?: boolean, formIndex?: integer, shiny?: boolean, variant?: integer): { spriteKey: string, fileRoot: string } {
|
||||
const spriteKey = getPokemonSpecies(species).getSpriteKey(female ?? false, formIndex ?? 0, shiny ?? false, variant ?? 0);
|
||||
const fileRoot = getPokemonSpecies(species).getSpriteAtlasPath(female ?? false, formIndex ?? 0, shiny ?? false, variant ?? 0);
|
||||
@ -447,7 +443,7 @@ function failCatch(scene: BattleScene, pokemon: EnemyPokemon, originalY: number,
|
||||
scene.currentBattle.lastUsedPokeball = pokeballType;
|
||||
removePb(scene, pokeball);
|
||||
|
||||
scene.ui.showText(i18next.t("battle:pokemonBrokeFree", { pokemonName: pokemon.name }), null, () => resolve(), null, true);
|
||||
scene.ui.showText(i18next.t("battle:pokemonBrokeFree", { pokemonName: pokemon.getNameToRender() }), null, () => resolve(), null, true);
|
||||
});
|
||||
}
|
||||
|
||||
@ -516,7 +512,7 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po
|
||||
Promise.all([pokemon.hideInfo(), scene.gameData.setPokemonCaught(pokemon)]).then(() => {
|
||||
if (scene.getParty().length === 6) {
|
||||
const promptRelease = () => {
|
||||
scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.name }), null, () => {
|
||||
scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => {
|
||||
scene.pokemonInfoContainer.makeRoomForConfirmUi();
|
||||
scene.ui.setMode(Mode.CONFIRM, () => {
|
||||
scene.ui.setMode(Mode.PARTY, PartyUiMode.RELEASE, 0, (slotIndex: integer, _option: PartyOption) => {
|
||||
@ -544,7 +540,7 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po
|
||||
};
|
||||
|
||||
if (showCatchObtainMessage) {
|
||||
scene.ui.showText(i18next.t(isObtain ? "battle:pokemonObtained" : "battle:pokemonCaught", { pokemonName: pokemon.name }), null, doPokemonCatchMenu, 0, true);
|
||||
scene.ui.showText(i18next.t(isObtain ? "battle:pokemonObtained" : "battle:pokemonCaught", { pokemonName: pokemon.getNameToRender() }), null, doPokemonCatchMenu, 0, true);
|
||||
} else {
|
||||
doPokemonCatchMenu();
|
||||
}
|
||||
@ -581,7 +577,7 @@ export async function doPokemonFlee(scene: BattleScene, pokemon: EnemyPokemon):
|
||||
onComplete: () => {
|
||||
pokemon.setVisible(false);
|
||||
scene.field.remove(pokemon, true);
|
||||
showEncounterText(scene, i18next.t("battle:pokemonFled", { pokemonName: pokemon.name }), 600, false)
|
||||
showEncounterText(scene, i18next.t("battle:pokemonFled", { pokemonName: pokemon.getNameToRender() }), 600, false)
|
||||
.then(() => {
|
||||
resolve();
|
||||
});
|
||||
@ -604,7 +600,7 @@ export function doPlayerFlee(scene: BattleScene, pokemon: EnemyPokemon): Promise
|
||||
onComplete: () => {
|
||||
pokemon.setVisible(false);
|
||||
scene.field.remove(pokemon, true);
|
||||
showEncounterText(scene, i18next.t("battle:playerFled", { pokemonName: pokemon.name }), 600, false)
|
||||
showEncounterText(scene, i18next.t("battle:playerFled", { pokemonName: pokemon.getNameToRender() }), 600, false)
|
||||
.then(() => {
|
||||
resolve();
|
||||
});
|
||||
|
@ -64,5 +64,5 @@ export enum BattlerTagType {
|
||||
STOCKPILING = "STOCKPILING",
|
||||
RECEIVE_DOUBLE_DAMAGE = "RECEIVE_DOUBLE_DAMAGE",
|
||||
ALWAYS_GET_HIT = "ALWAYS_GET_HIT",
|
||||
MYSTERY_ENCOUNTER_POST_SUMMON = "MYSTERY_ENCOUNTER_POST_SUMMON" // Provides effects on post-summon for MEs
|
||||
MYSTERY_ENCOUNTER_POST_SUMMON = "MYSTERY_ENCOUNTER_POST_SUMMON", // Provides effects on post-summon for MEs
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ import { Biome } from "#enums/biome";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { getPokemonNameWithAffix } from "#app/messages.js";
|
||||
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
||||
|
||||
export enum FieldPosition {
|
||||
CENTER,
|
||||
@ -187,6 +187,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
this.fusionVariant = dataSource.fusionVariant || 0;
|
||||
this.fusionGender = dataSource.fusionGender;
|
||||
this.fusionLuck = dataSource.fusionLuck;
|
||||
this.mysteryEncounterData = dataSource.mysteryEncounterData;
|
||||
} else {
|
||||
this.id = Utils.randSeedInt(4294967296);
|
||||
this.ivs = ivs || Utils.getIvsFromId(this.id);
|
||||
@ -233,6 +234,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
this.luck = (this.shiny ? this.variant + 1 : 0) + (this.fusionShiny ? this.fusionVariant + 1 : 0);
|
||||
this.fusionLuck = this.luck;
|
||||
this.mysteryEncounterData = new MysteryEncounterPokemonData();
|
||||
}
|
||||
|
||||
this.generateName();
|
||||
@ -927,7 +929,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
if (!types.length || !includeTeraType) {
|
||||
if (!ignoreOverride && this.summonData?.types) {
|
||||
if (this.mysteryEncounterData?.types?.length > 0) {
|
||||
// "Permanent" override for a Pokemon's normal types, currently only used by Mystery Encounters
|
||||
this.mysteryEncounterData.types.forEach(t => types.push(t));
|
||||
} else if (!ignoreOverride && this.summonData?.types) {
|
||||
this.summonData.types.forEach(t => types.push(t));
|
||||
} else {
|
||||
const speciesForm = this.getSpeciesForm(ignoreOverride);
|
||||
@ -994,6 +999,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (Overrides.OPP_ABILITY_OVERRIDE && !this.isPlayer()) {
|
||||
return allAbilities[Overrides.OPP_ABILITY_OVERRIDE];
|
||||
}
|
||||
if (this.mysteryEncounterData?.ability) {
|
||||
return allAbilities[this.mysteryEncounterData.ability];
|
||||
}
|
||||
if (this.isFusion()) {
|
||||
return allAbilities[this.getFusionSpeciesForm(ignoreOverride).getAbility(this.fusionAbilityIndex)];
|
||||
}
|
||||
@ -1018,6 +1026,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE && !this.isPlayer()) {
|
||||
return allAbilities[Overrides.OPP_PASSIVE_ABILITY_OVERRIDE];
|
||||
}
|
||||
if (this.mysteryEncounterData?.passive) {
|
||||
return allAbilities[this.mysteryEncounterData.passive];
|
||||
}
|
||||
|
||||
let starterSpeciesId = this.species.speciesId;
|
||||
while (pokemonPrevolutions.hasOwnProperty(starterSpeciesId)) {
|
||||
@ -4059,6 +4070,7 @@ export class PokemonSummonData {
|
||||
public speciesForm: PokemonSpeciesForm;
|
||||
public fusionSpeciesForm: PokemonSpeciesForm;
|
||||
public ability: Abilities = Abilities.NONE;
|
||||
public passiveAbility: Abilities = Abilities.NONE;
|
||||
public gender: Gender;
|
||||
public fusionGender: Gender;
|
||||
public stats: integer[];
|
||||
|
@ -1,31 +1,32 @@
|
||||
export const clowningAroundDialogue = {
|
||||
intro: "It's...@d{64} a clown?",
|
||||
speaker: "Clown",
|
||||
intro_dialogue: `Bumbling buffoon,\nbrace for a brilliant battle!
|
||||
$You’ll be beaten by this brawling busker!\nBring it!`,
|
||||
intro_dialogue: "Bumbling buffoon, brace for a brilliant battle!\nYou’ll be beaten by this brawling busker!",
|
||||
title: "Clowning Around",
|
||||
description: "The clown seems eager to goad you into a battle, but to what end?\n\nSomething is off about this encounter.",
|
||||
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.}",
|
||||
query: "What will you do?",
|
||||
option: {
|
||||
1: {
|
||||
label: "Battle the Clown",
|
||||
tooltip: "(-) Strange Battle\n(?) Affects Pokémon Abilities",
|
||||
selected: "Your pitiful Pokémon are poised for a pathetic performance!"
|
||||
selected: "Your pitiful Pokémon are poised for a pathetic performance!",
|
||||
apply_ability_dialogue: "A sensational showcase!\nYour savvy suits a sensational skill as spoils!",
|
||||
apply_ability_message: "The clown is offering to permanently Skill Swap one of your Pokémon's ability to {{ability}}!",
|
||||
ability_prompt: "Would you like to permanently teach a Pokémon the {{ability}} ability?",
|
||||
ability_gained: "@s{level_up_fanfare}{{chosenPokemon}} gained the {{ability}} ability!"
|
||||
},
|
||||
2: {
|
||||
label: "Remain Unprovoked",
|
||||
tooltip: "(-) Upsets the Clown\n(?) Affects Pokémon Items",
|
||||
selected: "Dismal dodger, you deny a delightful duel?\nFeel my fury!",
|
||||
selected_2: `The clown's Blacephalon uses Trick!
|
||||
All of your {{switchPokemon}}'s items were randomly swapped!`,
|
||||
selected_2: "The clown's Blacephalon uses Trick!\nAll of your {{switchPokemon}}'s items were randomly swapped!",
|
||||
selected_3: "Flustered fool, fall for my flawless deception!",
|
||||
},
|
||||
3: {
|
||||
label: "Return the Insults",
|
||||
tooltip: "(-) Upsets the Clown\n(?) Affects Pokémon Types",
|
||||
selected: "I'm appalled at your absurd antics!\nTaste my temper!",
|
||||
selected_2: `The clown's Blacephalon uses\na move you've never seen before!
|
||||
All of your team's types were randomly swapped!`,
|
||||
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_3: "Flustered fool, fall for my flawless deception!",
|
||||
},
|
||||
},
|
||||
|
@ -127,9 +127,9 @@ class DefaultOverrides {
|
||||
// -------------------------
|
||||
|
||||
// 1 to 256, set to null to ignore
|
||||
readonly MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = 256;
|
||||
readonly MYSTERY_ENCOUNTER_RATE_OVERRIDE: number = null;
|
||||
readonly MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier = null;
|
||||
readonly MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = MysteryEncounterType.CLOWNING_AROUND;
|
||||
readonly MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType = null;
|
||||
|
||||
// -------------------------
|
||||
// MODIFIER / ITEM OVERRIDES
|
||||
|
@ -1369,7 +1369,7 @@ export class PostSummonPhase extends PokemonPhase {
|
||||
}
|
||||
this.scene.arena.applyTags(ArenaTrapTag, pokemon);
|
||||
|
||||
// If this is fight or flight mystery encounter and is enemy pokemon summon phase, add enraged tag
|
||||
// If this is mystery encounter and has post summon phase tag, apply post summon effects
|
||||
if (pokemon.findTags(t => t instanceof MysteryEncounterPostSummonTag)) {
|
||||
pokemon.lapseTag(BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON);
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import { loadBattlerTag } from "../data/battler-tags";
|
||||
import { Biome } from "#enums/biome";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
||||
|
||||
export default class PokemonData {
|
||||
public id: integer;
|
||||
@ -54,6 +55,7 @@ export default class PokemonData {
|
||||
public bossSegments?: integer;
|
||||
|
||||
public summonData: PokemonSummonData;
|
||||
public mysteryEncounterData: MysteryEncounterPokemonData;
|
||||
|
||||
constructor(source: Pokemon | any, forHistory: boolean = false) {
|
||||
const sourcePokemon = source instanceof Pokemon ? source : null;
|
||||
@ -108,6 +110,7 @@ export default class PokemonData {
|
||||
this.status = sourcePokemon.status;
|
||||
if (this.player) {
|
||||
this.summonData = sourcePokemon.summonData;
|
||||
this.mysteryEncounterData = sourcePokemon.mysteryEncounterData;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -137,6 +140,14 @@ export default class PokemonData {
|
||||
this.summonData.tags = [];
|
||||
}
|
||||
}
|
||||
|
||||
this.mysteryEncounterData = new MysteryEncounterPokemonData();
|
||||
if (!forHistory && source.mysteryEncounterData) {
|
||||
this.mysteryEncounterData.spriteScale = source.mysteryEncounterData.spriteScale;
|
||||
this.mysteryEncounterData.ability = source.mysteryEncounterData.ability;
|
||||
this.mysteryEncounterData.passive = source.mysteryEncounterData.passive;
|
||||
this.mysteryEncounterData.types = source.mysteryEncounterData.types;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user