mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-12-14 22:05:34 +01:00
278 lines
12 KiB
TypeScript
278 lines
12 KiB
TypeScript
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
|
import {
|
|
EnemyPartyConfig, generateModifierType, generateModifierTypeOption,
|
|
initBattleWithEnemyConfig,
|
|
leaveEncounterWithoutBattle, setEncounterExp,
|
|
setEncounterRewards
|
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
|
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
|
import {
|
|
BerryModifierType,
|
|
getPartyLuckValue,
|
|
ModifierPoolType,
|
|
ModifierTypeOption, modifierTypes,
|
|
regenerateModifierPoolThresholds,
|
|
} from "#app/modifier/modifier-type";
|
|
import { randSeedInt } from "#app/utils";
|
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
import BattleScene from "#app/battle-scene";
|
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
|
import { getPokemonNameWithAffix } from "#app/messages";
|
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
|
import { TrainerSlot } from "#app/data/trainer-config";
|
|
import { applyModifierTypeToPlayerPokemon, getEncounterPokemonLevelForWave, getHighestStatPlayerPokemon, getSpriteKeysFromPokemon, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
|
import PokemonData from "#app/system/pokemon-data";
|
|
import { BerryModifier } from "#app/modifier/modifier";
|
|
import i18next from "#app/plugins/i18n";
|
|
import { BerryType } from "#enums/berry-type";
|
|
import { PERMANENT_STATS, Stat } from "#enums/stat";
|
|
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
|
|
|
/** the i18n namespace for the encounter */
|
|
const namespace = "mysteryEncounter:berriesAbound";
|
|
|
|
/**
|
|
* Berries Abound encounter.
|
|
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3810 | GitHub Issue #3810}
|
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
|
*/
|
|
export const BerriesAboundEncounter: MysteryEncounter =
|
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.BERRIES_ABOUND)
|
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
|
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
|
.withCatchAllowed(true)
|
|
.withHideWildIntroMessage(true)
|
|
.withIntroSpriteConfigs([]) // Set in onInit()
|
|
.withIntroDialogue([
|
|
{
|
|
text: `${namespace}.intro`,
|
|
},
|
|
])
|
|
.withOnInit((scene: BattleScene) => {
|
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
|
|
|
// Calculate boss mon
|
|
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
|
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
|
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
|
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
|
const config: EnemyPartyConfig = {
|
|
pokemonConfigs: [{
|
|
level: level,
|
|
species: bossSpecies,
|
|
dataSource: new PokemonData(bossPokemon),
|
|
isBoss: true
|
|
}],
|
|
};
|
|
encounter.enemyPartyConfigs = [config];
|
|
|
|
// Calculate the number of extra berries that player receives
|
|
// 10-40: 2, 40-120: 4, 120-160: 5, 160-180: 7
|
|
const numBerries =
|
|
scene.currentBattle.waveIndex > 160 ? 7
|
|
: scene.currentBattle.waveIndex > 120 ? 5
|
|
: scene.currentBattle.waveIndex > 40 ? 4 : 2;
|
|
regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0);
|
|
encounter.misc = { numBerries };
|
|
|
|
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon);
|
|
encounter.spriteConfigs = [
|
|
{
|
|
spriteKey: "berries_abound_bush",
|
|
fileRoot: "mystery-encounters",
|
|
x: 25,
|
|
y: -6,
|
|
yShadow: -7,
|
|
disableAnimation: true,
|
|
hasShadow: true
|
|
},
|
|
{
|
|
spriteKey: spriteKey,
|
|
fileRoot: fileRoot,
|
|
hasShadow: true,
|
|
tint: 0.25,
|
|
x: -5,
|
|
repeat: true,
|
|
isPokemon: true
|
|
}
|
|
];
|
|
|
|
// Get fastest party pokemon for option 2
|
|
const fastestPokemon = getHighestStatPlayerPokemon(scene, PERMANENT_STATS[Stat.SPD], true, false);
|
|
encounter.misc.fastestPokemon = fastestPokemon;
|
|
encounter.misc.enemySpeed = bossPokemon.getStat(Stat.SPD);
|
|
encounter.setDialogueToken("fastestPokemon", fastestPokemon.getNameToRender());
|
|
|
|
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.1.selected`,
|
|
},
|
|
],
|
|
},
|
|
async (scene: BattleScene) => {
|
|
// Pick battle
|
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
|
const numBerries = encounter.misc.numBerries;
|
|
|
|
const doBerryRewards = () => {
|
|
const berryText = numBerries + " " + i18next.t(`${namespace}.berries`);
|
|
|
|
scene.playSound("item_fanfare");
|
|
queueEncounterMessage(scene, i18next.t("battle:rewardGain", { modifierName: berryText }));
|
|
|
|
// Generate a random berry and give it to the first Pokemon with room for it
|
|
for (let i = 0; i < numBerries; i++) {
|
|
tryGiveBerry(scene);
|
|
}
|
|
};
|
|
|
|
const shopOptions: ModifierTypeOption[] = [];
|
|
for (let i = 0; i < 5; i++) {
|
|
// Generate shop berries
|
|
const mod = generateModifierTypeOption(scene, modifierTypes.BERRY);
|
|
if (mod) {
|
|
shopOptions.push(mod);
|
|
}
|
|
}
|
|
|
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
|
|
await initBattleWithEnemyConfig(scene, scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
|
|
}
|
|
)
|
|
.withOption(
|
|
MysteryEncounterOptionBuilder
|
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
|
.withDialogue({
|
|
buttonLabel: `${namespace}.option.2.label`,
|
|
buttonTooltip: `${namespace}.option.2.tooltip`
|
|
})
|
|
.withOptionPhase(async (scene: BattleScene) => {
|
|
// Pick race for berries
|
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
|
const fastestPokemon: PlayerPokemon = encounter.misc.fastestPokemon;
|
|
const enemySpeed: number = encounter.misc.enemySpeed;
|
|
const speedDiff = fastestPokemon.getStat(Stat.SPD) / (enemySpeed * 1.1);
|
|
const numBerries: number = encounter.misc.numBerries;
|
|
|
|
const shopOptions: ModifierTypeOption[] = [];
|
|
for (let i = 0; i < 5; i++) {
|
|
// Generate shop berries
|
|
const mod = generateModifierTypeOption(scene, modifierTypes.BERRY);
|
|
if (mod) {
|
|
shopOptions.push(mod);
|
|
}
|
|
}
|
|
|
|
if (speedDiff < 1) {
|
|
// Caught and attacked by boss, gets +1 to all stats at start of fight
|
|
const doBerryRewards = () => {
|
|
const berryText = numBerries + " " + i18next.t(`${namespace}.berries`);
|
|
|
|
scene.playSound("item_fanfare");
|
|
queueEncounterMessage(scene, i18next.t("battle:rewardGain", { modifierName: berryText }));
|
|
|
|
// Generate a random berry and give it to the first Pokemon with room for it
|
|
for (let i = 0; i < numBerries; i++) {
|
|
tryGiveBerry(scene);
|
|
}
|
|
};
|
|
|
|
// Defense/Spd buffs below wave 50, +1 to all stats otherwise
|
|
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ?
|
|
[Stat.DEF, Stat.SPDEF, Stat.SPD] :
|
|
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD];
|
|
|
|
const config = scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0];
|
|
config.pokemonConfigs![0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON];
|
|
config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => {
|
|
queueEncounterMessage(pokemon.scene, `${namespace}.option.2.boss_enraged`);
|
|
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
|
|
};
|
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doBerryRewards);
|
|
await showEncounterText(scene, `${namespace}.option.2.selected_bad`);
|
|
await initBattleWithEnemyConfig(scene, config);
|
|
return;
|
|
} else {
|
|
// Gains 1 berry for every 10% faster the player's pokemon is than the enemy, up to a max of numBerries, minimum of 2
|
|
const numBerriesGrabbed = Math.max(Math.min(Math.round((speedDiff - 1)/0.08), numBerries), 2);
|
|
encounter.setDialogueToken("numBerries", String(numBerriesGrabbed));
|
|
const doFasterBerryRewards = () => {
|
|
const berryText = numBerriesGrabbed + " " + i18next.t(`${namespace}.berries`);
|
|
|
|
scene.playSound("item_fanfare");
|
|
queueEncounterMessage(scene, i18next.t("battle:rewardGain", { modifierName: berryText }));
|
|
|
|
// Generate a random berry and give it to the first Pokemon with room for it (trying to give to fastest first)
|
|
for (let i = 0; i < numBerriesGrabbed; i++) {
|
|
tryGiveBerry(scene, fastestPokemon);
|
|
}
|
|
};
|
|
|
|
setEncounterExp(scene, fastestPokemon.id, encounter.enemyPartyConfigs[0].pokemonConfigs![0].species.baseExp);
|
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: shopOptions, fillRemaining: false }, undefined, doFasterBerryRewards);
|
|
await showEncounterText(scene, `${namespace}.option.2.selected`);
|
|
leaveEncounterWithoutBattle(scene);
|
|
}
|
|
})
|
|
.build()
|
|
)
|
|
.withSimpleOption(
|
|
{
|
|
buttonLabel: `${namespace}.option.3.label`,
|
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
|
selected: [
|
|
{
|
|
text: `${namespace}.option.3.selected`,
|
|
},
|
|
],
|
|
},
|
|
async (scene: BattleScene) => {
|
|
// Leave encounter with no rewards or exp
|
|
leaveEncounterWithoutBattle(scene, true);
|
|
return true;
|
|
}
|
|
)
|
|
.build();
|
|
|
|
function tryGiveBerry(scene: BattleScene, prioritizedPokemon?: PlayerPokemon) {
|
|
const berryType = randSeedInt(Object.keys(BerryType).filter(s => !isNaN(Number(s))).length) as BerryType;
|
|
const berry = generateModifierType(scene, modifierTypes.BERRY, [berryType]) as BerryModifierType;
|
|
|
|
const party = scene.getParty();
|
|
|
|
// Will try to apply to prioritized pokemon first, then do normal application method if it fails
|
|
if (prioritizedPokemon) {
|
|
const heldBerriesOfType = scene.findModifier(m => m instanceof BerryModifier
|
|
&& m.pokemonId === prioritizedPokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
|
|
|
|
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
|
|
applyModifierTypeToPlayerPokemon(scene, prioritizedPokemon, berry);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Iterate over the party until berry was successfully given
|
|
for (const pokemon of party) {
|
|
const heldBerriesOfType = scene.findModifier(m => m instanceof BerryModifier
|
|
&& m.pokemonId === pokemon.id && (m as BerryModifier).berryType === berryType, true) as BerryModifier;
|
|
|
|
if (!heldBerriesOfType || heldBerriesOfType.getStackCount() < heldBerriesOfType.getMaxStackCount(scene)) {
|
|
applyModifierTypeToPlayerPokemon(scene, pokemon, berry);
|
|
return;
|
|
}
|
|
}
|
|
}
|