mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-21 00:52:47 +02:00
* Refactor evo conditions and descriptions * Fix test * Fix Shedinja * Simplify Gimmighoul evolution * Primeape and Stantler evolve by using their move 10 times * Basculin white stripe evolves by taking 294 recoil damage * Primeape and Stantler use modifiers for tracking * Basculin uses modifier too * Remove evo count from pokemon data * No more evo counter data, Gallade/Froslass * Fix allmoves import * Clamperl * Struggle shouldn't count for Basc recoil * Change to nicer type * Apply Benjie's suggestions Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> * Address formatting * Undo new evolution changes * Remove unused imports * Fix speciesid * Fixed up descriptions a little * Change a key name * Fix Gimmighoul * Apply Biome * Apply Biome unsafe fixes * Review suggestions - Convert `EvoCondKey` enum to `const` object - Use early returns in `SpeciesEvolutionCondition#description` and `SpeciesFormEvolution#description` - Replace `!!x.find` with `x.some` and `y.indexOf() > -1` with `y.includes()` - Implement `coerceArray` - Fix Shelmet evolution condition checking for Shelmet and not Karrablast - Remove unnecessary type casting in `battle-scene.ts` * Remove leftover enforce func loop * Fix circular imports issue - `getPokemonSpecies` moved to `src/utils/pokemon-utils.ts` - `allSpecies` moved to `src/data/data-lists.ts` --------- Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com> Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
319 lines
12 KiB
TypeScript
319 lines
12 KiB
TypeScript
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
|
import type { EnemyPartyConfig } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
|
import {
|
|
initBattleWithEnemyConfig,
|
|
loadCustomMovesForEncounter,
|
|
leaveEncounterWithoutBattle,
|
|
setEncounterExp,
|
|
setEncounterRewards,
|
|
transitionMysteryEncounterIntroVisuals,
|
|
generateModifierType,
|
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
|
import type { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
import { globalScene } from "#app/global-scene";
|
|
import type MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter";
|
|
import { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
|
import {
|
|
AbilityRequirement,
|
|
CombinationPokemonRequirement,
|
|
TypeRequirement,
|
|
} from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
|
import { SpeciesId } from "#enums/species-id";
|
|
import { getPokemonSpecies } from "#app/utils/pokemon-utils";
|
|
import { Gender } from "#app/data/gender";
|
|
import { PokemonType } from "#enums/pokemon-type";
|
|
import { BattlerIndex } from "#enums/battler-index";
|
|
import type Pokemon from "#app/field/pokemon";
|
|
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
|
import { MoveId } from "#enums/move-id";
|
|
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
|
import { WeatherType } from "#enums/weather-type";
|
|
import { isNullOrUndefined, randSeedInt } from "#app/utils/common";
|
|
import { StatusEffect } from "#enums/status-effect";
|
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
|
import {
|
|
applyAbilityOverrideToPokemon,
|
|
applyDamageToPokemon,
|
|
applyModifierTypeToPlayerPokemon,
|
|
} from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
|
import { EncounterAnim } from "#enums/encounter-anims";
|
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
|
import { AbilityId } from "#enums/ability-id";
|
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
|
import { Stat } from "#enums/stat";
|
|
import { FIRE_RESISTANT_ABILITIES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
|
import { MoveUseMode } from "#enums/move-use-mode";
|
|
import { allAbilities, modifierTypes } from "#app/data/data-lists";
|
|
|
|
/** the i18n namespace for the encounter */
|
|
const namespace = "mysteryEncounters/fieryFallout";
|
|
|
|
/**
|
|
* Damage percentage taken when suffering the heat.
|
|
* Can be a number between `0` - `100`.
|
|
* The higher the more damage taken (100% = instant KO).
|
|
*/
|
|
const DAMAGE_PERCENTAGE: number = 20;
|
|
|
|
/**
|
|
* Fiery Fallout encounter.
|
|
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3814 | GitHub Issue #3814}
|
|
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
|
|
*/
|
|
export const FieryFalloutEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(
|
|
MysteryEncounterType.FIERY_FALLOUT,
|
|
)
|
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
|
.withSceneWaveRangeRequirement(40, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
|
|
.withCatchAllowed(true)
|
|
.withIntroSpriteConfigs([]) // Set in onInit()
|
|
.withAnimations(EncounterAnim.MAGMA_BG, EncounterAnim.MAGMA_SPOUT)
|
|
.withAutoHideIntroVisuals(false)
|
|
.withFleeAllowed(false)
|
|
.withIntroDialogue([
|
|
{
|
|
text: `${namespace}:intro`,
|
|
},
|
|
])
|
|
.withOnInit(() => {
|
|
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
|
|
|
// Calculate boss mons
|
|
const volcaronaSpecies = getPokemonSpecies(SpeciesId.VOLCARONA);
|
|
const config: EnemyPartyConfig = {
|
|
pokemonConfigs: [
|
|
{
|
|
species: volcaronaSpecies,
|
|
isBoss: false,
|
|
gender: Gender.MALE,
|
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
|
globalScene.phaseManager.unshiftNew(
|
|
"StatStageChangePhase",
|
|
pokemon.getBattlerIndex(),
|
|
true,
|
|
[Stat.SPDEF, Stat.SPD],
|
|
1,
|
|
);
|
|
},
|
|
},
|
|
{
|
|
species: volcaronaSpecies,
|
|
isBoss: false,
|
|
gender: Gender.FEMALE,
|
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
|
globalScene.phaseManager.unshiftNew(
|
|
"StatStageChangePhase",
|
|
pokemon.getBattlerIndex(),
|
|
true,
|
|
[Stat.SPDEF, Stat.SPD],
|
|
1,
|
|
);
|
|
},
|
|
},
|
|
],
|
|
doubleBattle: true,
|
|
disableSwitch: true,
|
|
};
|
|
encounter.enemyPartyConfigs = [config];
|
|
|
|
// Load hidden Volcarona sprites
|
|
encounter.spriteConfigs = [
|
|
{
|
|
spriteKey: "",
|
|
fileRoot: "",
|
|
species: SpeciesId.VOLCARONA,
|
|
repeat: true,
|
|
hidden: true,
|
|
hasShadow: true,
|
|
x: -20,
|
|
startFrame: 20,
|
|
},
|
|
{
|
|
spriteKey: "",
|
|
fileRoot: "",
|
|
species: SpeciesId.VOLCARONA,
|
|
repeat: true,
|
|
hidden: true,
|
|
hasShadow: true,
|
|
x: 20,
|
|
},
|
|
];
|
|
|
|
// Load animations/sfx for Volcarona moves
|
|
loadCustomMovesForEncounter([MoveId.FIRE_SPIN, MoveId.QUIVER_DANCE]);
|
|
|
|
const pokemon = globalScene.getEnemyPokemon();
|
|
globalScene.arena.trySetWeather(WeatherType.SUNNY, pokemon);
|
|
|
|
encounter.setDialogueToken("volcaronaName", getPokemonSpecies(SpeciesId.VOLCARONA).getName());
|
|
|
|
return true;
|
|
})
|
|
.withOnVisualsStart(() => {
|
|
// Play animations
|
|
const background = new EncounterBattleAnim(
|
|
EncounterAnim.MAGMA_BG,
|
|
globalScene.getPlayerPokemon()!,
|
|
globalScene.getPlayerPokemon(),
|
|
);
|
|
background.playWithoutTargets(200, 70, 2, 3);
|
|
const animation = new EncounterBattleAnim(
|
|
EncounterAnim.MAGMA_SPOUT,
|
|
globalScene.getPlayerPokemon()!,
|
|
globalScene.getPlayerPokemon(),
|
|
);
|
|
animation.playWithoutTargets(80, 100, 2);
|
|
globalScene.time.delayedCall(600, () => {
|
|
animation.playWithoutTargets(-20, 100, 2);
|
|
});
|
|
globalScene.time.delayedCall(1200, () => {
|
|
animation.playWithoutTargets(140, 150, 2);
|
|
});
|
|
|
|
return true;
|
|
})
|
|
.setLocalizationKey(`${namespace}`)
|
|
.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 () => {
|
|
// Pick battle
|
|
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
|
setEncounterRewards({ fillRemaining: true }, undefined, () => giveLeadPokemonAttackTypeBoostItem());
|
|
|
|
encounter.startOfBattleEffects.push(
|
|
{
|
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
|
targets: [BattlerIndex.PLAYER],
|
|
move: new PokemonMove(MoveId.FIRE_SPIN),
|
|
useMode: MoveUseMode.IGNORE_PP,
|
|
},
|
|
{
|
|
sourceBattlerIndex: BattlerIndex.ENEMY_2,
|
|
targets: [BattlerIndex.PLAYER_2],
|
|
move: new PokemonMove(MoveId.FIRE_SPIN),
|
|
useMode: MoveUseMode.IGNORE_PP,
|
|
},
|
|
);
|
|
await initBattleWithEnemyConfig(globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]);
|
|
},
|
|
)
|
|
.withSimpleOption(
|
|
{
|
|
buttonLabel: `${namespace}:option.2.label`,
|
|
buttonTooltip: `${namespace}:option.2.tooltip`,
|
|
selected: [
|
|
{
|
|
text: `${namespace}:option.2.selected`,
|
|
},
|
|
],
|
|
},
|
|
async () => {
|
|
// Damage non-fire types and burn 1 random non-fire type member + give it Heatproof
|
|
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
|
const nonFireTypes = globalScene
|
|
.getPlayerParty()
|
|
.filter(p => p.isAllowedInBattle() && !p.getTypes().includes(PokemonType.FIRE));
|
|
|
|
for (const pkm of nonFireTypes) {
|
|
const percentage = DAMAGE_PERCENTAGE / 100;
|
|
const damage = Math.floor(pkm.getMaxHp() * percentage);
|
|
applyDamageToPokemon(pkm, damage);
|
|
}
|
|
|
|
// Burn random member
|
|
const burnable = nonFireTypes.filter(
|
|
p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status.effect) || p.status.effect === StatusEffect.NONE,
|
|
);
|
|
if (burnable?.length > 0) {
|
|
const roll = randSeedInt(burnable.length);
|
|
const chosenPokemon = burnable[roll];
|
|
if (chosenPokemon.trySetStatus(StatusEffect.BURN)) {
|
|
// Burn applied
|
|
encounter.setDialogueToken("burnedPokemon", chosenPokemon.getNameToRender());
|
|
encounter.setDialogueToken("abilityName", allAbilities[AbilityId.HEATPROOF].name);
|
|
queueEncounterMessage(`${namespace}:option.2.target_burned`);
|
|
|
|
// Also permanently change the burned Pokemon's ability to Heatproof
|
|
applyAbilityOverrideToPokemon(chosenPokemon, AbilityId.HEATPROOF);
|
|
}
|
|
}
|
|
|
|
// No rewards
|
|
leaveEncounterWithoutBattle(true);
|
|
},
|
|
)
|
|
.withOption(
|
|
MysteryEncounterOptionBuilder.newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_SPECIAL)
|
|
.withPrimaryPokemonRequirement(
|
|
CombinationPokemonRequirement.Some(
|
|
new TypeRequirement(PokemonType.FIRE, true, 1),
|
|
new AbilityRequirement(FIRE_RESISTANT_ABILITIES, true),
|
|
),
|
|
) // Will set option3PrimaryName dialogue token automatically
|
|
.withDialogue({
|
|
buttonLabel: `${namespace}:option.3.label`,
|
|
buttonTooltip: `${namespace}:option.3.tooltip`,
|
|
disabledButtonTooltip: `${namespace}:option.3.disabled_tooltip`,
|
|
selected: [
|
|
{
|
|
text: `${namespace}:option.3.selected`,
|
|
},
|
|
],
|
|
})
|
|
.withPreOptionPhase(async () => {
|
|
// Do NOT await this, to prevent player from repeatedly pressing options
|
|
transitionMysteryEncounterIntroVisuals(false, false, 2000);
|
|
})
|
|
.withOptionPhase(async () => {
|
|
// Fire types help calm the Volcarona
|
|
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
|
await transitionMysteryEncounterIntroVisuals();
|
|
setEncounterRewards({ fillRemaining: true }, undefined, () => {
|
|
giveLeadPokemonAttackTypeBoostItem();
|
|
});
|
|
|
|
const primary = encounter.options[2].primaryPokemon!;
|
|
|
|
setEncounterExp([primary.id], getPokemonSpecies(SpeciesId.VOLCARONA).baseExp * 2);
|
|
leaveEncounterWithoutBattle();
|
|
})
|
|
.build(),
|
|
)
|
|
.build();
|
|
|
|
function giveLeadPokemonAttackTypeBoostItem() {
|
|
// Give first party pokemon attack type boost item for free at end of battle
|
|
const leadPokemon = globalScene.getPlayerParty()?.[0];
|
|
if (leadPokemon) {
|
|
// Generate type booster held item, default to Charcoal if item fails to generate
|
|
let boosterModifierType = generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER) as AttackTypeBoosterModifierType;
|
|
if (!boosterModifierType) {
|
|
boosterModifierType = generateModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, [
|
|
PokemonType.FIRE,
|
|
]) as AttackTypeBoosterModifierType;
|
|
}
|
|
applyModifierTypeToPlayerPokemon(leadPokemon, boosterModifierType);
|
|
|
|
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
|
encounter.setDialogueToken("itemName", boosterModifierType.name);
|
|
encounter.setDialogueToken("leadPokemon", leadPokemon.getNameToRender());
|
|
queueEncounterMessage(`${namespace}:found_item`);
|
|
}
|
|
}
|