pokerogue/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts
NightKev 00f7fd47df
[Refactor] Remove unnecessary re-exports (#4818)
* Remove unnecessary re-exports

* Move `Type` enum to `src/enums/type.ts`

* Remove import style change from `modifier-type.ts`
2024-11-08 17:44:34 -05:00

247 lines
11 KiB
TypeScript

import { EnemyPartyConfig, generateModifierTypeOption, initBattleWithEnemyConfig, setEncounterExp, setEncounterRewards, transitionMysteryEncounterIntroVisuals, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { randSeedInt } from "#app/utils";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import BattleScene from "#app/battle-scene";
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
import { MoneyRequirement, WaveModulusRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
import Pokemon, { EnemyPokemon } from "#app/field/pokemon";
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import PokemonData from "#app/system/pokemon-data";
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { Biome } from "#enums/biome";
import { getBiomeKey } from "#app/field/arena";
import { Type } from "#enums/type";
import { getPartyLuckValue, modifierTypes } from "#app/modifier/modifier-type";
import { TrainerSlot } from "#app/data/trainer-config";
import { BattlerTagType } from "#enums/battler-tag-type";
import { getPokemonNameWithAffix } from "#app/messages";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { Stat } from "#enums/stat";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import { getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
/** the i18n namespace for this encounter */
const namespace = "mysteryEncounters/teleportingHijinks";
const MONEY_COST_MULTIPLIER = 1.75;
const BIOME_CANDIDATES = [ Biome.SPACE, Biome.FAIRY_CAVE, Biome.LABORATORY, Biome.ISLAND, Biome.WASTELAND, Biome.DOJO ];
const MACHINE_INTERFACING_TYPES = [ Type.ELECTRIC, Type.STEEL ];
/**
* Teleporting Hijinks encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3817 | GitHub Issue #3817}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const TeleportingHijinksEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.TELEPORTING_HIJINKS)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withSceneRequirement(new WaveModulusRequirement([ 1, 2, 3 ], 10)) // Must be in first 3 waves after boss wave
.withSceneRequirement(new MoneyRequirement(0, MONEY_COST_MULTIPLIER)) // Must be able to pay teleport cost
.withAutoHideIntroVisuals(false)
.withCatchAllowed(true)
.withFleeAllowed(false)
.withIntroSpriteConfigs([
{
spriteKey: "teleporting_hijinks_teleporter",
fileRoot: "mystery-encounters",
hasShadow: true,
x: 4,
y: 4,
yShadow: 1
}
])
.withIntroDialogue([
{
text: `${namespace}:intro`,
}
])
.setLocalizationKey(`${namespace}`)
.withTitle(`${namespace}:title`)
.withDescription(`${namespace}:description`)
.withQuery(`${namespace}:query`)
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
const price = scene.getWaveMoneyAmount(MONEY_COST_MULTIPLIER);
encounter.setDialogueToken("price", price.toString());
encounter.misc = {
price
};
return true;
})
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT)
.withSceneMoneyRequirement(0, MONEY_COST_MULTIPLIER) // Must be able to pay teleport cost
.withDialogue({
buttonLabel: `${namespace}:option.1.label`,
buttonTooltip: `${namespace}:option.1.tooltip`,
selected: [
{
text: `${namespace}:option.1.selected`,
}
],
})
.withPreOptionPhase(async (scene: BattleScene) => {
// Update money
updatePlayerMoney(scene, -scene.currentBattle.mysteryEncounter!.misc.price, true, false);
})
.withOptionPhase(async (scene: BattleScene) => {
const config: EnemyPartyConfig = await doBiomeTransitionDialogueAndBattleInit(scene);
setEncounterRewards(scene, { fillRemaining: true });
await initBattleWithEnemyConfig(scene, config);
})
.build()
)
.withOption(
MysteryEncounterOptionBuilder
.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
.withPokemonTypeRequirement(MACHINE_INTERFACING_TYPES, true, 1) // Must have Steel or Electric type
.withDialogue({
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
disabledButtonTooltip: `${namespace}:option.2.disabled_tooltip`,
selected: [
{
text: `${namespace}:option.2.selected`,
}
],
})
.withOptionPhase(async (scene: BattleScene) => {
const config: EnemyPartyConfig = await doBiomeTransitionDialogueAndBattleInit(scene);
setEncounterRewards(scene, { fillRemaining: true });
setEncounterExp(scene, scene.currentBattle.mysteryEncounter!.selectedOption!.primaryPokemon!.id, 100);
await initBattleWithEnemyConfig(scene, config);
})
.build()
)
.withSimpleOption(
{
buttonLabel: `${namespace}:option.3.label`,
buttonTooltip: `${namespace}:option.3.tooltip`,
selected: [
{
text: `${namespace}:option.3.selected`,
},
],
},
async (scene: BattleScene) => {
// Inspect the Machine
const encounter = scene.currentBattle.mysteryEncounter!;
// Init enemy
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), 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,
}],
};
const magnet = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.STEEL ])!;
const metalCoat = generateModifierTypeOption(scene, modifierTypes.ATTACK_TYPE_BOOSTER, [ Type.ELECTRIC ])!;
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [ magnet, metalCoat ], fillRemaining: true });
await transitionMysteryEncounterIntroVisuals(scene, true, true);
await initBattleWithEnemyConfig(scene, config);
}
)
.build();
async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
const encounter = scene.currentBattle.mysteryEncounter!;
// Calculate new biome (cannot be current biome)
const filteredBiomes = BIOME_CANDIDATES.filter(b => scene.arena.biomeType !== b);
const newBiome = filteredBiomes[randSeedInt(filteredBiomes.length)];
// Show dialogue and transition biome
await showEncounterText(scene, `${namespace}:transport`);
await Promise.all([ animateBiomeChange(scene, newBiome), transitionMysteryEncounterIntroVisuals(scene) ]);
scene.playBgm();
await showEncounterText(scene, `${namespace}:attacked`);
// Init enemy
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true);
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
// 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: EnemyPartyConfig = {
pokemonConfigs: [{
level: level,
species: bossSpecies,
dataSource: new PokemonData(bossPokemon),
isBoss: true,
tags: [ BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON ],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}:boss_enraged`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
}
}],
};
return config;
}
async function animateBiomeChange(scene: BattleScene, nextBiome: Biome) {
return new Promise<void>(resolve => {
scene.tweens.add({
targets: [ scene.arenaEnemy, scene.lastEnemyTrainer ],
x: "+=300",
duration: 2000,
onComplete: () => {
scene.newArena(nextBiome);
const biomeKey = getBiomeKey(nextBiome);
const bgTexture = `${biomeKey}_bg`;
scene.arenaBgTransition.setTexture(bgTexture);
scene.arenaBgTransition.setAlpha(0);
scene.arenaBgTransition.setVisible(true);
scene.arenaPlayerTransition.setBiome(nextBiome);
scene.arenaPlayerTransition.setAlpha(0);
scene.arenaPlayerTransition.setVisible(true);
scene.tweens.add({
targets: [ scene.arenaPlayer, scene.arenaBgTransition, scene.arenaPlayerTransition ],
duration: 1000,
ease: "Sine.easeInOut",
alpha: (target: any) => target === scene.arenaPlayer ? 0 : 1,
onComplete: () => {
scene.arenaBg.setTexture(bgTexture);
scene.arenaPlayer.setBiome(nextBiome);
scene.arenaPlayer.setAlpha(1);
scene.arenaEnemy.setBiome(nextBiome);
scene.arenaEnemy.setAlpha(1);
scene.arenaNextEnemy.setBiome(nextBiome);
scene.arenaBgTransition.setVisible(false);
scene.arenaPlayerTransition.setVisible(false);
if (scene.lastEnemyTrainer) {
scene.lastEnemyTrainer.destroy();
}
resolve();
scene.tweens.add({
targets: scene.arenaEnemy,
x: "-=300",
});
}
});
}
});
});
}