pokerogue/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts
flx-sta 95386861bb
[Qol][Refactor] i18n lazy-loading (#4327)
* move: locales files to `/public` (from `/src`)

* install: i18next-http-backend module

* implement: i18next language lazy-loading

* remove: all `config.ts` files (for locales)

* disable: enConfig import in i18next.d.ts

* remove: console.log from utils.camelCaseToKebabCase()

* remove localization tests

we don't need to test if i18next is working.
This is the job of i18next itself

* mock i18next for tests

* fix: tests that have to use the i18next key now

instead of the english translation

* fix: absolute-avarice-encounter test

* fix: loading mystery-encounter translations

with lazy-load

* fix: 2 mystery encounter translation loading

* replace: i18next mocks any vi.fn() calls

* fix: new namespace usage in ME tests

now using "mysteryEncounters/..."

* fix: delibirdy encounter not being language specific

the encounter was checking if the modifier name includes `Berry` which is only true for english. Instead it has to check if the modifier is an instance of BerryModifier

* fix: the-expert-pokemon-breeder

the new i18n pattern requires a different namespacing which has been adopted

* fix: GTS encounter tests

* add: `MockText.on()`

* fix: berries abound test

* chore: apply review suggestion

from @DayKev

* update i18next.d.ts

* chore: fix i18next.d.ts

* fix: `dialogue-misc` switchup between `en` and `ja`

* move: `SpeciesFormKey` into enum

there was an issue with circular dependencies

* replace: `#app/enums/` with `#enums/` for `SpeciesFormKey` imports

* re-sync locales from `beta`

* rename: `ca_ES` -> `ca-ES`

* rename: `pt_BR` -> `pt-BR`

* rename: `zh_CN` -> `zh-CN`

* rename: `zh_TW` -> `zh-TW`

* fix loading Species-Form-Key in poemon-evo.

* update: i18next `supporterLngs` ...

and remove `nonExplicitSupportedLngs`

* fix: `${namespace}.` -> `${namespace}:`

thanks @MokaStitcher
2024-10-01 21:55:16 +01:00

208 lines
8.2 KiB
TypeScript

import { EnemyPartyConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals, generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { modifierTypes, PokemonHeldItemModifierType, } from "#app/modifier/modifier-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 { getPokemonSpecies } from "#app/data/pokemon-species";
import { Species } from "#enums/species";
import { Nature } from "#app/data/nature";
import Pokemon, { PokemonMove } from "#app/field/pokemon";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { Moves } from "#enums/moves";
import { BattlerIndex } from "#app/battle";
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";
import { 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 = "mysteryEncounters/theStrongStuff";
// Halved for HP stat
const HIGH_BST_REDUCTION_VALUE = 15;
const BST_INCREASE_VALUE = 10;
/**
* The Strong Stuff encounter.
* @see {@link https://github.com/pagefaultgames/pokerogue/issues/3803 | GitHub Issue #3803}
* @see For biome requirements check {@linkcode mysteryEncountersByBiome}
*/
export const TheStrongStuffEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_STRONG_STUFF)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
.withScenePartySizeRequirement(3, 6) // Must have at least 3 pokemon in party
.withMaxAllowedEncounters(1)
.withHideWildIntroMessage(true)
.withAutoHideIntroVisuals(false)
.withFleeAllowed(false)
.withIntroSpriteConfigs([
{
spriteKey: "berry_juice",
fileRoot: "items",
hasShadow: true,
isItem: true,
scale: 1.25,
x: -15,
y: 3,
disableAnimation: true
},
{
spriteKey: Species.SHUCKLE.toString(),
fileRoot: "pokemon",
hasShadow: true,
repeat: true,
scale: 1.25,
x: 20,
y: 10,
yShadow: 7
},
]) // Set in onInit()
.withIntroDialogue([
{
text: `${namespace}:intro`,
},
])
.withOnInit((scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
// Calculate boss mon
const config: EnemyPartyConfig = {
levelAdditiveModifier: 1,
disableSwitch: true,
pokemonConfigs: [
{
species: getPokemonSpecies(Species.SHUCKLE),
isBoss: true,
bossSegments: 5,
mysteryEncounterPokemonData: new MysteryEncounterPokemonData({ spriteScale: 1.25 }),
nature: Nature.BOLD,
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
modifierConfigs: [
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.SITRUS]) as PokemonHeldItemModifierType
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.ENIGMA]) as PokemonHeldItemModifierType
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.APICOT]) as PokemonHeldItemModifierType
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.GANLON]) as PokemonHeldItemModifierType
},
{
modifier: generateModifierType(scene, modifierTypes.BERRY, [BerryType.LUM]) as PokemonHeldItemModifierType,
stackCount: 2
}
],
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}:option.2.stat_boost`);
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [Stat.DEF, Stat.SPDEF], 2));
}
}
],
};
encounter.enemyPartyConfigs = [config];
loadCustomMovesForEncounter(scene, [Moves.GASTRO_ACID, Moves.STEALTH_ROCK]);
encounter.setDialogueToken("shuckleName", getPokemonSpecies(Species.SHUCKLE).getName());
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) => {
const encounter = scene.currentBattle.mysteryEncounter!;
// Do blackout and hide intro visuals during blackout
scene.time.delayedCall(750, () => {
transitionMysteryEncounterIntroVisuals(scene, true, true, 50);
});
// -15 to all base stats of highest BST (halved for HP), +10 to all base stats of rest of party (halved for HP)
// Sort party by bst
const sortedParty = scene.getParty().slice(0)
.sort((pokemon1, pokemon2) => {
const pokemon1Bst = pokemon1.calculateBaseStats().reduce((a, b) => a + b, 0);
const pokemon2Bst = pokemon2.calculateBaseStats().reduce((a, b) => a + b, 0);
return pokemon2Bst - pokemon1Bst;
});
sortedParty.forEach((pokemon, index) => {
if (index < 2) {
// -15 to the two highest BST mons
modifyPlayerPokemonBST(pokemon, -HIGH_BST_REDUCTION_VALUE);
encounter.setDialogueToken("highBstPokemon" + (index + 1), pokemon.getNameToRender());
} else {
// +10 for the rest
modifyPlayerPokemonBST(pokemon, BST_INCREASE_VALUE);
}
});
encounter.setDialogueToken("reductionValue", HIGH_BST_REDUCTION_VALUE.toString());
encounter.setDialogueToken("increaseValue", BST_INCREASE_VALUE.toString());
await showEncounterText(scene, `${namespace}:option.1.selected_2`, null, undefined, true);
encounter.dialogue.outro = [
{
text: `${namespace}:outro`,
}
];
setEncounterRewards(scene, { fillRemaining: true });
leaveEncounterWithoutBattle(scene, true);
return true;
}
)
.withSimpleOption(
{
buttonLabel: `${namespace}:option.2.label`,
buttonTooltip: `${namespace}:option.2.tooltip`,
selected: [
{
text: `${namespace}:option.2.selected`,
},
],
},
async (scene: BattleScene) => {
// Pick battle
const encounter = scene.currentBattle.mysteryEncounter!;
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SOUL_DEW], fillRemaining: true });
encounter.startOfBattleEffects.push(
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
move: new PokemonMove(Moves.GASTRO_ACID),
ignorePp: true
},
{
sourceBattlerIndex: BattlerIndex.ENEMY,
targets: [BattlerIndex.PLAYER],
move: new PokemonMove(Moves.STEALTH_ROCK),
ignorePp: true
});
encounter.dialogue.outro = [];
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
}
)
.build();